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.

2476 lines
68 KiB

  1. /******************************************************************************
  2. Copyright (C) Microsoft Corporation 1985-1991. All rights reserved.
  3. Title: aviopen.c - open a AVI file
  4. *****************************************************************************/
  5. #include "graphic.h"
  6. #ifdef WIN32
  7. #include <wchar.h>
  8. #endif
  9. #ifdef USEAVIFILE
  10. #include <initguid.h>
  11. DEFINE_AVIGUID(IID_IAVIFile, 0x00020020, 0, 0);
  12. DEFINE_AVIGUID(IID_IAVIStream, 0x00020021, 0, 0);
  13. #endif
  14. #define comptypeNONE mmioFOURCC('N','O','N','E')
  15. //
  16. // special error to use AVIFile to open this file.
  17. //
  18. #define AVIERR_NOT_AVIFILE 4242
  19. //
  20. // if this is defined we will always use AVIFILE.DLL, except for
  21. // 1:1 interleaved files.
  22. //
  23. #define USE_AVIFILE_FOR_NON_INT
  24. /***************************************************************************
  25. *
  26. ***************************************************************************/
  27. BOOL FAR PASCAL mciaviCloseFile(NPMCIGRAPHIC npMCI);
  28. BOOL FAR PASCAL mciaviOpenFile(NPMCIGRAPHIC npMCI);
  29. BOOL NEAR PASCAL InitStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
  30. BOOL NEAR PASCAL InitVideoStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
  31. BOOL NEAR PASCAL InitAudioStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
  32. BOOL NEAR PASCAL InitOtherStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
  33. void NEAR PASCAL CloseStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi);
  34. BOOL NEAR PASCAL OpenFileInit(NPMCIGRAPHIC npMCI);
  35. BOOL NEAR PASCAL ParseNewHeader(NPMCIGRAPHIC npMCI);
  36. BOOL NEAR PASCAL OpenRiffAVIFile(NPMCIGRAPHIC npMCI);
  37. BOOL NEAR PASCAL OpenWithAVIFile(NPMCIGRAPHIC npMCI);
  38. BOOL NEAR PASCAL OpenInterface(NPMCIGRAPHIC npMCI);
  39. BOOL NEAR PASCAL OpenAVIFile(NPMCIGRAPHIC npMCI, IAVIFile FAR *pf);
  40. BOOL NEAR PASCAL OpenAVIStream(NPMCIGRAPHIC npMCI, int stream, IAVIStream FAR *pf);
  41. static BOOL NEAR PASCAL IsRectBogus(LPRECT prc);
  42. static LONG NEAR PASCAL atol(char *sz);
  43. #ifdef WIN32
  44. #define GetFileDriveType GetDriveType
  45. #else
  46. static UINT NEAR PASCAL GetFileDriveType(LPSTR szPath);
  47. #endif
  48. #ifndef WIN32
  49. SZCODE szOLENLSDLL[] = "OLE2NLS.DLL";
  50. SZCODE szOLENLSAPI[] = "GetUserDefaultLangID";
  51. #endif
  52. /***************************************************************************
  53. *
  54. * @doc INTERNAL MCIAVI
  55. *
  56. * @api BOOL | mciaviOpenFile | Open an AVI file.
  57. * the filename we are to open is passed to npMCI->szFileName.
  58. *
  59. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  60. *
  61. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  62. *
  63. ***************************************************************************/
  64. BOOL FAR PASCAL mciaviOpenFile(NPMCIGRAPHIC npMCI)
  65. {
  66. //
  67. // mciaviOpenFile should not be called with a file open!
  68. //
  69. Assert(npMCI->streams == 0);
  70. Assert(npMCI->hmmio == NULL);
  71. Assert(npMCI->hpIndex == NULL);
  72. Assert(!(npMCI->dwFlags & (
  73. MCIAVI_NOTINTERLEAVED |
  74. MCIAVI_ANIMATEPALETTE |
  75. MCIAVI_CANDRAW |
  76. MCIAVI_HASINDEX)));
  77. //
  78. // !!!support open new
  79. //
  80. if (npMCI->szFilename[0] == '\0') {
  81. }
  82. //
  83. // what media is this file coming from, will be important later
  84. // when we play.
  85. //
  86. if (npMCI->szFilename[0] == '@')
  87. npMCI->uDriveType = DRIVE_INTERFACE;
  88. else
  89. npMCI->uDriveType = GetFileDriveType(npMCI->szFilename);
  90. #ifdef DEBUG
  91. switch (npMCI->uDriveType) {
  92. case DRIVE_CDROM:
  93. DOUT2("Media is a CD-ROM\n");
  94. break;
  95. case DRIVE_REMOTE:
  96. DOUT2("Media is a Network\n");
  97. break;
  98. case DRIVE_FIXED:
  99. DOUT2("Media is a Hard disk\n");
  100. break;
  101. case DRIVE_REMOVABLE:
  102. DOUT2("Media is a floppy disk\n");
  103. break;
  104. case DRIVE_INTERFACE:
  105. DOUT2("Media is OLE COM Interface\n");
  106. break;
  107. default:
  108. DPF(("Unknown Media type %d\n", npMCI->uDriveType));
  109. break;
  110. }
  111. #endif
  112. #ifdef USEAVIFILE
  113. //
  114. // if the "filename" is of the form: '@########' then we assume we
  115. // have been pased a interface pointer of some sort.
  116. //
  117. if (npMCI->szFilename[0] == '@' &&
  118. OpenInterface(npMCI))
  119. goto DoneOpening;
  120. // !!! This will open even AVI files this way!
  121. if ((npMCI->dwOptionFlags & MCIAVIO_USEAVIFILE) &&
  122. OpenWithAVIFile(npMCI))
  123. goto DoneOpening;
  124. #endif
  125. if (!OpenRiffAVIFile(npMCI)) {
  126. //
  127. // unable to open RIFF file, if it was because it was
  128. // not a AVI file, then give AVIFile a try.
  129. //
  130. if (npMCI->dwTaskError != AVIERR_NOT_AVIFILE)
  131. goto error;
  132. #ifdef USEAVIFILE
  133. npMCI->dwTaskError = 0;
  134. if (!OpenWithAVIFile(npMCI))
  135. #endif
  136. goto error;
  137. }
  138. DoneOpening:
  139. if (OpenFileInit(npMCI)) {
  140. npMCI->dwTaskError = 0;
  141. return TRUE;
  142. }
  143. error:
  144. mciaviCloseFile(npMCI);
  145. if (npMCI->dwTaskError == 0)
  146. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  147. return FALSE;
  148. }
  149. /***************************************************************************
  150. *
  151. * @doc INTERNAL MCIAVI
  152. *
  153. * @api BOOL | OpenFileInit | called after a file is opened to init things
  154. *
  155. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  156. *
  157. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  158. *
  159. ***************************************************************************/
  160. BOOL NEAR PASCAL OpenFileInit(NPMCIGRAPHIC npMCI)
  161. {
  162. int i;
  163. RECT rc;
  164. //
  165. // lets make sure we opened something.
  166. //
  167. if (npMCI->streams == 0)
  168. return FALSE;
  169. if (npMCI->nVideoStreams + npMCI->nAudioStreams + npMCI->nOtherStreams == 0)
  170. return FALSE;
  171. if (npMCI->nVideoStreams == 0)
  172. npMCI->dwFlags &= ~MCIAVI_SHOWVIDEO;
  173. if (npMCI->nAudioStreams == 0)
  174. npMCI->dwFlags &= ~MCIAVI_PLAYAUDIO;
  175. if (npMCI->nAudioStreams > 1) {
  176. UINT wLang;
  177. int stream;
  178. #ifndef WIN32
  179. UINT (WINAPI * GetUserDefaultLangID)(void);
  180. UINT u;
  181. HANDLE hdll;
  182. u = SetErrorMode(SEM_NOOPENFILEERRORBOX);
  183. hdll = LoadLibrary(szOLENLSDLL);
  184. SetErrorMode(u);
  185. if ((UINT)hdll > (UINT)HINSTANCE_ERROR)
  186. {
  187. if ((FARPROC) GetUserDefaultLangID = GetProcAddress(hdll, szOLENLSAPI)) {
  188. #endif
  189. wLang = GetUserDefaultLangID();
  190. #ifndef WIN32
  191. }
  192. FreeLibrary(hdll);
  193. } else
  194. wLang = 0;
  195. #endif
  196. DPF(("Current language: %x\n", wLang));
  197. if (wLang > 0) {
  198. for (stream = 0; stream < npMCI->streams; stream++) {
  199. if (SH(stream).fccType == streamtypeAUDIO) {
  200. if (!(SH(stream).dwFlags & STREAM_ENABLED))
  201. continue;
  202. if (SH(stream).wLanguage == wLang) {
  203. npMCI->nAudioStream = stream;
  204. npMCI->psiAudio = SI(stream);
  205. break;
  206. }
  207. }
  208. }
  209. }
  210. }
  211. if (npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) {
  212. npMCI->wEarlyRecords = npMCI->wEarlyVideo;
  213. }
  214. else {
  215. npMCI->wEarlyRecords = max(npMCI->wEarlyVideo, npMCI->wEarlyAudio);
  216. }
  217. if (npMCI->wEarlyRecords == 0 &&
  218. !(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED)) {
  219. DPF(("Interleaved file with no audio skew?\n"));
  220. npMCI->dwFlags |= MCIAVI_NOTINTERLEAVED;
  221. }
  222. if (npMCI->dwFlags & MCIAVI_ANIMATEPALETTE) {
  223. DPF(("This AVI file has palette changes.\n"));
  224. if (npMCI->nVideoStreams > 1) {
  225. npMCI->dwFlags &= ~MCIAVI_ANIMATEPALETTE;
  226. DPF(("...But we are going to ignore them?\n"));
  227. }
  228. }
  229. //
  230. // this must be set
  231. //
  232. if (npMCI->dwSuggestedBufferSize == 0) {
  233. for (i=0; i<npMCI->streams; i++)
  234. npMCI->dwSuggestedBufferSize =
  235. max(SH(i).dwSuggestedBufferSize,npMCI->dwSuggestedBufferSize);
  236. }
  237. //
  238. // check all fields in the main header
  239. //
  240. if (npMCI->dwScale == 0 ||
  241. npMCI->dwRate == 0) {
  242. }
  243. ////will be set when header parsed
  244. ////npMCI->dwMicroSecPerFrame = muldiv32(npMCI->dwScale, 1000000, npMCI->dwRate);
  245. npMCI->dwPlayMicroSecPerFrame = npMCI->dwMicroSecPerFrame;
  246. #define COMMON_SCALE 10000
  247. //
  248. // convert the rate/scale into something that is normalized to 1000
  249. //
  250. npMCI->dwRate = muldiv32(npMCI->dwRate, COMMON_SCALE, npMCI->dwScale);
  251. npMCI->dwScale = COMMON_SCALE;
  252. //
  253. // walk all streams and fix them up.
  254. //
  255. for (i=0; i<npMCI->streams; i++) {
  256. STREAMINFO *psi = SI(i);
  257. LONG lStart;
  258. LONG lEnd;
  259. //
  260. // convert the rate/scale into something that is normalized to 1000
  261. //
  262. psi->sh.dwRate = muldiv32(psi->sh.dwRate, COMMON_SCALE, psi->sh.dwScale);
  263. psi->sh.dwScale = COMMON_SCALE;
  264. //
  265. // trim any streams that hang over the movie.
  266. //
  267. lStart = MovieToStream(psi, 0);
  268. lEnd = MovieToStream(psi, npMCI->lFrames);
  269. if ((LONG)(psi->sh.dwStart + psi->sh.dwLength) > lEnd) {
  270. DPF(("Stream #%d is too long, was %ld now %ld\n", i,
  271. psi->sh.dwLength, lEnd - psi->sh.dwStart));
  272. psi->sh.dwLength = lEnd - psi->sh.dwStart;
  273. }
  274. }
  275. //
  276. // fix up the movie rect
  277. //
  278. if (IsRectEmpty(&npMCI->rcMovie)) {
  279. DPF2(("Movie rect is empty\n"));
  280. SetRectEmpty(&rc);
  281. for (i=0; i<npMCI->streams; i++)
  282. UnionRect(&rc,&rc,&SH(i).rcFrame);
  283. npMCI->rcMovie = rc;
  284. }
  285. rc = npMCI->rcMovie;
  286. //
  287. // always read the index, so we can skip frames even on CD!
  288. //
  289. ReadIndex(npMCI);
  290. DPF(("Key frames are every (on average): %ld frames (%ld ms)\n",npMCI->dwKeyFrameInfo, MovieToTime(npMCI->dwKeyFrameInfo)));
  291. // force things to happen, in case we're re-loading
  292. SetRectEmpty(&npMCI->rcSource);
  293. SetRectEmpty(&npMCI->rcDest);
  294. /* this will call DrawDibBegin() ... */
  295. DevicePut(npMCI, &rc, MCI_DGV_PUT_SOURCE);
  296. /*
  297. * also set the dest rect. This should be done
  298. * by the WM_SIZE message sent during SetWindowToDefaultSize.
  299. * On NT, the WM_SIZE message is not sent synchronously since it
  300. * is an inter-thread sendmessage (the winproc is on the original thread
  301. * whereas we are currently running on the avi thread). The winproc
  302. * thread may well not get the WM_SIZE message until much too late, so
  303. * set the dest rect here. Note: don't use ResetDestRect since that
  304. * also relies on the window size, which is not set yet.
  305. */
  306. /* double frame size of destination if zoom by 2 */
  307. if (npMCI->dwOptionFlags & MCIAVIO_ZOOMBY2)
  308. SetRect(&rc, 0, 0, rc.right*2, rc.bottom*2);
  309. DevicePut(npMCI, &rc, MCI_DGV_PUT_DESTINATION);
  310. //
  311. // size the window and things.
  312. //
  313. SetWindowToDefaultSize(npMCI);
  314. DrawBegin(npMCI, NULL);
  315. return TRUE;
  316. }
  317. #ifdef USEAVIFILE
  318. /***************************************************************************
  319. *
  320. * @doc INTERNAL MCIAVI
  321. *
  322. * @api BOOL | OpenWithAVIFile | Open an file using AVIFile
  323. *
  324. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  325. *
  326. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  327. *
  328. ***************************************************************************/
  329. BOOL NEAR PASCAL OpenWithAVIFile(NPMCIGRAPHIC npMCI)
  330. {
  331. IAVIFile FAR *pf = NULL;
  332. if (!InitAVIFile(npMCI))
  333. return FALSE;
  334. AVIFileOpen(&pf, npMCI->szFilename, MMIO_READ, 0);
  335. if (pf == NULL) {
  336. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  337. return FALSE;
  338. }
  339. if (!OpenAVIFile(npMCI, pf)) {
  340. mciaviCloseFile(npMCI);
  341. pf->lpVtbl->Release(pf);
  342. return FALSE;
  343. }
  344. return TRUE;
  345. }
  346. /***************************************************************************
  347. *
  348. * @doc INTERNAL MCIAVI
  349. *
  350. * @api BOOL | OpenInterface | Open an interface pointer
  351. *
  352. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  353. *
  354. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  355. *
  356. ***************************************************************************/
  357. BOOL NEAR PASCAL OpenInterface(NPMCIGRAPHIC npMCI)
  358. {
  359. IUnknown FAR *p;
  360. IAVIFile FAR *pf=NULL;
  361. IAVIStream FAR *ps=NULL;
  362. if (!InitAVIFile(npMCI))
  363. return FALSE;
  364. if (npMCI->szFilename[0] != '@')
  365. return FALSE;
  366. #ifdef UNICODE
  367. p = (IUnknown FAR *)wcstol(npMCI->szFilename+1, NULL, 10);
  368. #else
  369. p = (IUnknown FAR *)atol(npMCI->szFilename+1);
  370. #endif
  371. if (!IsValidInterface(p))
  372. return FALSE;
  373. #ifndef WIN32
  374. //!!!we need to do the PSP stuff? or will the TASK stuff in
  375. //!!!COMPOBJ mess us up?
  376. {
  377. extern void FAR SetPSP(UINT psp);
  378. SetPSP(npMCI->pspParent);
  379. }
  380. #endif
  381. p->lpVtbl->QueryInterface(p, &IID_IAVIFile, (LPVOID FAR *)&pf);
  382. if (pf != NULL)
  383. {
  384. if (OpenAVIFile(npMCI, pf))
  385. return TRUE;
  386. pf->lpVtbl->Release(pf);
  387. }
  388. p->lpVtbl->QueryInterface(p, &IID_IAVIStream, (LPVOID FAR *)&ps);
  389. if (ps != NULL)
  390. {
  391. AVIMakeFileFromStreams(&pf, 1, &ps);
  392. ps->lpVtbl->Release(ps);
  393. if (pf == NULL)
  394. return FALSE;
  395. if (OpenAVIFile(npMCI, pf))
  396. return TRUE;
  397. pf->lpVtbl->Release(pf);
  398. return FALSE;
  399. }
  400. return FALSE;
  401. }
  402. /***************************************************************************
  403. *
  404. * @doc INTERNAL MCIAVI
  405. *
  406. * @api BOOL | OpenAVIFile | Open an a AVIFile object
  407. *
  408. * NOTE we do not do call AddRef() we assume we dont need to.
  409. *
  410. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  411. *
  412. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  413. *
  414. ***************************************************************************/
  415. BOOL NEAR PASCAL OpenAVIFile(NPMCIGRAPHIC npMCI, IAVIFile FAR *pf)
  416. {
  417. AVIFILEINFO info;
  418. HRESULT hr;
  419. IAVIStream FAR *ps;
  420. STREAMINFO *psi;
  421. int i;
  422. Assert(npMCI->pf == NULL);
  423. _fmemset(&info, 0, sizeof(info));
  424. hr = AVIFileInfo(pf, &info, sizeof(info));
  425. if (FAILED(GetScode(hr))) {
  426. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  427. return FALSE;
  428. }
  429. DPF(("OpenAVIFile: %s\n", (LPSTR)info.szFileType));
  430. //
  431. // get rid of bad files
  432. //
  433. if (info.dwStreams == 0 || info.dwStreams > 255 || info.dwLength == 0) {
  434. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  435. return FALSE;
  436. }
  437. //
  438. // make a copy of the VTable, for later use
  439. //
  440. npMCI->pf = pf;
  441. ////npMCI->vt = *pf->lpVtbl;
  442. npMCI->dwFlags |= MCIAVI_HASINDEX;
  443. npMCI->dwMicroSecPerFrame = muldiv32(info.dwScale, 1000000, info.dwRate);
  444. npMCI->lFrames = (LONG)info.dwLength;
  445. npMCI->dwRate = info.dwRate;
  446. npMCI->dwScale = info.dwScale;
  447. npMCI->streams = (int)info.dwStreams;
  448. npMCI->dwBytesPerSec = info.dwMaxBytesPerSec;
  449. npMCI->dwSuggestedBufferSize = info.dwSuggestedBufferSize + 2*sizeof(DWORD);
  450. SetRect(&npMCI->rcMovie,0,0,(int)info.dwWidth,(int)info.dwHeight);
  451. npMCI->paStreamInfo = (STREAMINFO*)
  452. LocalAlloc(LPTR,npMCI->streams * sizeof(STREAMINFO));
  453. if (npMCI->paStreamInfo == NULL) {
  454. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  455. npMCI->pf = NULL;
  456. return FALSE;
  457. }
  458. for (i = 0; i < npMCI->streams; i++) {
  459. ps = NULL;
  460. AVIFileGetStream(pf, &ps, 0, i);
  461. if (ps == NULL) {
  462. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  463. npMCI->pf = NULL;
  464. return FALSE;
  465. }
  466. if (!OpenAVIStream(npMCI, i, ps))
  467. DPF(("Error opening stream %d!\n", i));
  468. if (npMCI->dwTaskError) {
  469. npMCI->pf = NULL;
  470. return FALSE;
  471. }
  472. }
  473. //
  474. // compute the key frames every value
  475. //
  476. // do this by finding the key frame average over the first few frames.
  477. //
  478. #define NFRAMES 250
  479. if (psi = npMCI->psiVideo) {
  480. LONG l;
  481. int nKeyFrames=0;
  482. for (l=0; l<NFRAMES; l++) {
  483. if (AVIStreamFindSample(psi->ps, psi->sh.dwStart+l, FIND_PREV|FIND_KEY) == l)
  484. nKeyFrames++;
  485. }
  486. if (nKeyFrames > 1)
  487. npMCI->dwKeyFrameInfo = (DWORD)((NFRAMES + nKeyFrames/2)/nKeyFrames);
  488. else
  489. npMCI->dwKeyFrameInfo = 0;
  490. }
  491. return TRUE;
  492. }
  493. /***************************************************************************
  494. *
  495. * @doc INTERNAL MCIAVI
  496. *
  497. * @api BOOL | OpenAVIStream | Open an a AVIStream object
  498. *
  499. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  500. *
  501. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  502. *
  503. ***************************************************************************/
  504. BOOL NEAR PASCAL OpenAVIStream(NPMCIGRAPHIC npMCI, int stream, IAVIStream FAR *ps)
  505. {
  506. STREAMINFO* psi;
  507. AVISTREAMINFO info;
  508. HRESULT hr;
  509. _fmemset(&info, 0, sizeof(info));
  510. hr = AVIStreamInfo(ps, &info, sizeof(info));
  511. if (FAILED(GetScode(hr))) {
  512. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  513. return FALSE;
  514. }
  515. DPF(("OpenAVIStream(%d) %4.4s:%4.4s %s\n", stream, (LPSTR)&info.fccType, (LPSTR)&info.fccHandler, (LPSTR)info.szName));
  516. //
  517. // init the STREAMINFO from the IAVIStream
  518. //
  519. psi = SI(stream);
  520. psi->ps = ps; // save interface
  521. ////psi->vt = *ps->lpVtbl; // save VTable !!!needed?
  522. psi->sh.fccType = info.fccType;
  523. psi->sh.fccHandler = info.fccHandler;
  524. psi->sh.dwFlags = info.dwFlags;
  525. psi->sh.wPriority = info.wPriority;
  526. psi->sh.wLanguage = info.wLanguage;
  527. psi->sh.dwInitialFrames = 0; // info.dwInitialFrames;
  528. psi->sh.dwScale = info.dwScale;
  529. psi->sh.dwRate = info.dwRate;
  530. psi->sh.dwStart = info.dwStart;
  531. psi->sh.dwLength = info.dwLength;
  532. psi->sh.dwSuggestedBufferSize = info.dwSuggestedBufferSize;
  533. psi->sh.dwQuality = info.dwQuality;
  534. psi->sh.dwSampleSize = info.dwSampleSize;
  535. psi->sh.rcFrame = info.rcFrame;
  536. DPF0(("OpenAVIStream: #%d, rc [%d %d %d %d]\n", stream, info.rcFrame));
  537. //
  538. // get the format of the stream.
  539. //
  540. AVIStreamFormatSize(ps, 0, &psi->cbFormat);
  541. psi->lpFormat = GlobalAllocPtr(GMEM_MOVEABLE, psi->cbFormat);
  542. if (!psi->lpFormat) {
  543. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  544. return FALSE;
  545. }
  546. AVIStreamReadFormat(psi->ps, 0, psi->lpFormat, &psi->cbFormat);
  547. //
  548. // get the extra data for the stream.
  549. //
  550. AVIStreamReadData(psi->ps,ckidSTREAMHANDLERDATA, NULL, &psi->cbData);
  551. if (psi->cbData > 0) {
  552. psi->lpData = GlobalAllocPtr(GMEM_MOVEABLE, psi->cbData);
  553. if (!psi->lpData) {
  554. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  555. return FALSE;
  556. }
  557. AVIStreamReadData(psi->ps, ckidSTREAMHANDLERDATA, NULL, &psi->cbData);
  558. }
  559. return InitStream(npMCI, psi);
  560. }
  561. /***************************************************************************
  562. *
  563. * @doc INTERNAL MCIAVI
  564. *
  565. * @api BOOL | InitAVIFile | called to RTL to AVIFILE.DLL
  566. *
  567. * @rdesc TRUE means OK, otherwise error
  568. *
  569. ***************************************************************************/
  570. #ifdef WIN32
  571. // For the moment the 16 bit and 32 bit AVIFILE DLLs share the same
  572. // name. If the history of NT is to be repeated this will change.
  573. SZCODE szAVIFILE[] = TEXT("AVIFILE.DLL");
  574. SZCODE szCOMPOBJ[] = TEXT("COMPOB32");
  575. #else
  576. SZCODE szAVIFILE[] = "AVIFILE.DLL";
  577. SZCODE szCOMPOBJ[] = "COMPOBJ";
  578. #endif
  579. // On NT the entry points will NOT be unicode strings, as there is
  580. // no unicode version of GetProcAddress. BUT SZCODE generate UNICODE...
  581. SZCODEA szAVIFileInit[] = "AVIFileInit";
  582. SZCODEA szAVIFileExit[] = "AVIFileExit";
  583. SZCODEA szIsValidInterface[] = "IsValidInterface";
  584. SZCODEA szAVIMakeFileFromStreams[] = "AVIMakeFileFromStreams";
  585. SZCODEA szAVIStreamBeginStreaming[] = "AVIStreamBeginStreaming";
  586. SZCODEA szAVIStreamEndStreaming[] = "AVIStreamEndStreaming";
  587. #ifdef UNICODE
  588. // There has GOT to be a neat way of combining macros so that we can
  589. // assign AVIFileOpenA/W to the name string definining the entry point,
  590. // and still get avifilex.h to get AVIFileOpen function calls replaced by
  591. // using the function variable.
  592. SZCODEA szAVIFileOpen[] = "AVIFileOpenW";
  593. #else
  594. SZCODEA szAVIFileOpen[] = "AVIFileOpen";
  595. #endif
  596. BOOL FAR InitAVIFile(NPMCIGRAPHIC npMCI)
  597. {
  598. UINT u;
  599. if (hdllAVIFILE == (HMODULE)-1)
  600. return FALSE;
  601. if (hdllAVIFILE == NULL) {
  602. u = SetErrorMode(SEM_NOOPENFILEERRORBOX);
  603. hdllAVIFILE = LoadLibrary(szAVIFILE);
  604. SetErrorMode(u);
  605. #ifndef WIN32
  606. if ((UINT)hdllAVIFILE <= (UINT)HINSTANCE_ERROR)
  607. hdllAVIFILE = NULL;
  608. #endif
  609. if (hdllAVIFILE == NULL) {
  610. hdllAVIFILE = (HMODULE)-1;
  611. return FALSE;
  612. }
  613. hdllCOMPOBJ = GetModuleHandle(szCOMPOBJ);
  614. (FARPROC)XIsValidInterface = GetProcAddress(hdllCOMPOBJ, szIsValidInterface);
  615. Assert(hdllCOMPOBJ != NULL);
  616. Assert(XIsValidInterface != NULL);
  617. (FARPROC)XAVIFileInit = GetProcAddress(hdllAVIFILE, szAVIFileInit);
  618. (FARPROC)XAVIFileExit = GetProcAddress(hdllAVIFILE, szAVIFileExit);
  619. (FARPROC)XAVIFileOpen = GetProcAddress(hdllAVIFILE, szAVIFileOpen);
  620. (FARPROC)XAVIMakeFileFromStreams = GetProcAddress(hdllAVIFILE, szAVIMakeFileFromStreams);
  621. (FARPROC)XAVIStreamBeginStreaming = GetProcAddress(hdllAVIFILE, szAVIStreamBeginStreaming);
  622. (FARPROC)XAVIStreamEndStreaming = GetProcAddress(hdllAVIFILE, szAVIStreamEndStreaming);
  623. Assert(XAVIFileInit != NULL);
  624. Assert(XAVIFileExit != NULL);
  625. Assert(XAVIFileOpen != NULL);
  626. Assert(XAVIMakeFileFromStreams != NULL);
  627. }
  628. //
  629. // we need to call AVIFileInit() and AVIFileExit() for each task that
  630. // is using AVIFile or things will not work right.
  631. //
  632. if (!(npMCI->dwFlags & MCIAVI_USING_AVIFILE)) {
  633. npMCI->dwFlags |= MCIAVI_USING_AVIFILE;
  634. AVIFileInit();
  635. uAVIFILE++;
  636. }
  637. return TRUE;
  638. }
  639. /***************************************************************************
  640. *
  641. * @doc INTERNAL MCIAVI
  642. *
  643. * @api BOOL | FreeAVIFile | called to un-RTL to AVIFILE.DLL
  644. *
  645. * @rdesc TRUE means OK, otherwise error
  646. *
  647. ***************************************************************************/
  648. BOOL FAR FreeAVIFile(NPMCIGRAPHIC npMCI)
  649. {
  650. if (!(npMCI->dwFlags & MCIAVI_USING_AVIFILE))
  651. return FALSE;
  652. Assert(hdllAVIFILE != (HMODULE)-1 && hdllAVIFILE != NULL);
  653. // free this tasks use of AVIFile
  654. AVIFileExit();
  655. // if no more people using AVIFile lets the DLLs go.
  656. Assert(uAVIFILE > 0);
  657. uAVIFILE--;
  658. if (uAVIFILE == 0) {
  659. FreeLibrary(hdllAVIFILE);
  660. hdllAVIFILE = NULL;
  661. hdllCOMPOBJ = NULL;
  662. }
  663. }
  664. #endif /* USEAVIFILE */
  665. /***************************************************************************
  666. *
  667. * @doc INTERNAL MCIAVI
  668. *
  669. * @api BOOL | OpenRiffAVIFile | Open an RIFF AVI file
  670. *
  671. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  672. *
  673. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  674. *
  675. ***************************************************************************/
  676. BOOL NEAR PASCAL OpenRiffAVIFile(NPMCIGRAPHIC npMCI)
  677. {
  678. HMMIO hmmio;
  679. HANDLE h = NULL;
  680. BOOL fRet = TRUE;
  681. MMIOINFO mmioInfo;
  682. MMCKINFO ckRIFF;
  683. MMCKINFO ckLIST;
  684. MMCKINFO ckRECORD;
  685. _fmemset(&mmioInfo, 0, sizeof(MMIOINFO));
  686. mmioInfo.htask = (HANDLE)npMCI->hCallingTask;
  687. hmmio = mmioOpen(npMCI->szFilename, &mmioInfo, MMIO_READ | MMIO_DENYWRITE);
  688. if (hmmio == NULL)
  689. hmmio = mmioOpen(npMCI->szFilename, &mmioInfo, MMIO_READ);
  690. if (!hmmio) {
  691. switch (mmioInfo.wErrorRet) {
  692. case MMIOERR_OUTOFMEMORY:
  693. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  694. break;
  695. case MMIOERR_FILENOTFOUND:
  696. case MMIOERR_CANNOTOPEN:
  697. default:
  698. npMCI->dwTaskError = MCIERR_FILE_NOT_FOUND;
  699. break;
  700. }
  701. fRet = FALSE;
  702. goto exit;
  703. }
  704. npMCI->hmmio = hmmio;
  705. /*
  706. ** Descend into RIFF file
  707. */
  708. if (mmioDescend(hmmio, &ckRIFF, NULL, 0) != 0) {
  709. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  710. goto ERROR_BADFILE;
  711. }
  712. /*
  713. * check for a 'QuickTime AVI' file, a QuickTime AVI file is a
  714. * QuickTime public movie with a AVI file in the 'mdat' atom.
  715. */
  716. if (ckRIFF.cksize == mmioFOURCC('m','d','a','t'))
  717. {
  718. DPF(("File is a QuickTime public movie\n"));
  719. /*
  720. * now the 'mdat' atom better be a RIFF/AVI or we cant handle it.
  721. */
  722. if (mmioDescend(hmmio, &ckRIFF, NULL, 0) != 0) {
  723. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  724. goto ERROR_BADFILE;
  725. }
  726. }
  727. /* Make sure it's a RIFF file */
  728. if (ckRIFF.ckid != FOURCC_RIFF) {
  729. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  730. goto ERROR_NOTAVIFILE;
  731. }
  732. /* Otherwise, it should be an AVI file */
  733. if (ckRIFF.fccType != formtypeAVI) {
  734. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  735. goto ERROR_NOTAVIFILE;
  736. }
  737. /*
  738. ** Descend into header LIST
  739. */
  740. ckLIST.fccType = listtypeAVIHEADER;
  741. if (mmioDescend(hmmio, &ckLIST, &ckRIFF, MMIO_FINDLIST) != 0) {
  742. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  743. goto ERROR_BADFILE;
  744. }
  745. /* Leave space at end of buffer for pad word */
  746. h = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, ckLIST.cksize -
  747. sizeof(DWORD) +
  748. sizeof(DWORD));
  749. if (!h) {
  750. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  751. return FALSE;
  752. }
  753. npMCI->lp = npMCI->lpBuffer = (LPSTR) GlobalLock(h);
  754. DPF(("Reading header list: %lu bytes.\n", ckLIST.cksize - sizeof(DWORD)));
  755. if (mmioRead(hmmio, npMCI->lp, ckLIST.cksize - sizeof(DWORD))
  756. != (LONG) (ckLIST.cksize - sizeof(DWORD))) {
  757. npMCI->dwTaskError = MCIERR_FILE_READ;
  758. goto ERROR_BADFILE;
  759. }
  760. #ifdef USE_AVIFILE_FOR_NON_INT
  761. //
  762. // we check here for AVI RIFF files we dont want to handle with our
  763. // built in code, and want to pass on to AVIFILE.DLL
  764. //
  765. // we handle the following files:
  766. //
  767. // interleaved
  768. //
  769. // we pass on the following files to AVIFILE.DLL
  770. //
  771. // non-interleaved
  772. //
  773. // pretty simple right now, just interleaved non-interlaved
  774. // but could get as complex as you want.
  775. //
  776. {
  777. MainAVIHeader FAR * lpHdr;
  778. lpHdr = (MainAVIHeader FAR *)((BYTE FAR *)npMCI->lp + 8);
  779. if (!(lpHdr->dwFlags & AVIF_ISINTERLEAVED) ||
  780. lpHdr->dwInitialFrames == 0) {
  781. DOUT("File is not interleaved, giving it to AVIFILE.DLL\n");
  782. goto ERROR_NOTAVIFILE;
  783. }
  784. //
  785. // ok now we have a 1:1 interleved file.
  786. //
  787. // always use our code on a CD-ROM, but on other media...
  788. //
  789. switch (npMCI->uDriveType) {
  790. case DRIVE_CDROM:
  791. break;
  792. case DRIVE_REMOTE:
  793. case DRIVE_FIXED:
  794. case DRIVE_REMOVABLE:
  795. break;
  796. default:
  797. break;
  798. }
  799. }
  800. #endif
  801. if (PEEK_DWORD() == ckidAVIMAINHDR) {
  802. if (!ParseNewHeader(npMCI))
  803. goto ERROR_BADFILE;
  804. } else {
  805. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  806. goto ERROR_BADFILE;
  807. }
  808. /* Ascend out of header LIST */
  809. if (mmioAscend(hmmio, &ckLIST, 0) != 0) {
  810. npMCI->dwTaskError = MCIERR_FILE_READ;
  811. goto ERROR_BADFILE;
  812. }
  813. /* Initially, no frame has been drawn */
  814. npMCI->lFrameDrawn = (- (LONG) npMCI->wEarlyRecords) - 1;
  815. /*
  816. ** Descend into big 'Movie LIST'
  817. */
  818. ckLIST.fccType = listtypeAVIMOVIE;
  819. if (mmioDescend(hmmio, &ckLIST, &ckRIFF, MMIO_FINDLIST) != 0) {
  820. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  821. goto ERROR_BADFILE;
  822. }
  823. npMCI->dwMovieListOffset = ckLIST.dwDataOffset;
  824. /* Calculate end of 'movi' list, in case we need to read the index */
  825. npMCI->dwBigListEnd = ckLIST.dwDataOffset + ckLIST.cksize +
  826. (ckLIST.cksize & 1);
  827. /*
  828. ** Descend into header of first chunk
  829. */
  830. if (mmioDescend(hmmio, &ckRECORD, &ckLIST, 0) != 0) {
  831. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  832. goto ERROR_BADFILE;
  833. }
  834. npMCI->dwFirstRecordType = ckRECORD.ckid;
  835. npMCI->dwFirstRecordSize = ckRECORD.cksize + 2 * sizeof(DWORD);
  836. npMCI->dwFirstRecordPosition = mmioSeek(hmmio, 0, SEEK_CUR);
  837. if (mmioAscend(hmmio, &ckRECORD, 0) != 0) {
  838. npMCI->dwTaskError = MCIERR_FILE_READ;
  839. goto ERROR_BADFILE;
  840. }
  841. #ifdef DEBUG
  842. DPF2(("First record (%4.4s) 0x%lx bytes at position 0x%lx.\n",
  843. (LPSTR)&npMCI->dwFirstRecordType,
  844. npMCI->dwFirstRecordSize,
  845. npMCI->dwFirstRecordPosition));
  846. if (npMCI->dwFirstRecordPosition & 0x7ff) {
  847. DPF(("!!\n"));
  848. DPF(("!! This file is not properly aligned to a 2K boundary.\n"));
  849. DPF(("!! It may not play well from CD-ROM.\n"));
  850. DPF(("!!\n"));
  851. }
  852. #endif
  853. exit:
  854. if (!fRet)
  855. mciaviCloseFile(npMCI);
  856. if (h) {
  857. npMCI->lpBuffer = NULL;
  858. npMCI->dwBufferSize = 0L;
  859. GlobalUnlock(h);
  860. GlobalFree(h);
  861. }
  862. return fRet;
  863. ERROR_NOTAVIFILE:
  864. npMCI->dwTaskError = AVIERR_NOT_AVIFILE; // mark as not a AVI file
  865. ERROR_BADFILE:
  866. fRet = FALSE;
  867. goto exit;
  868. }
  869. /***************************************************************************
  870. * @doc INTERNAL MCIAVI
  871. *
  872. * @api BOOL | ParseNewHeader | 'nuf said
  873. *
  874. ***************************************************************************/
  875. BOOL NEAR PASCAL ParseNewHeader(NPMCIGRAPHIC npMCI)
  876. {
  877. DWORD dwHeaderSize;
  878. MainAVIHeader FAR * lpHdr;
  879. int stream;
  880. if (GET_DWORD() != ckidAVIMAINHDR) {
  881. goto FileError;
  882. }
  883. dwHeaderSize = GET_DWORD(); /* Skip size */
  884. /* Now, we're pointing at the actual header */
  885. lpHdr = (MainAVIHeader FAR *) npMCI->lp;
  886. npMCI->lFrames = (LONG)lpHdr->dwTotalFrames;
  887. npMCI->dwMicroSecPerFrame = lpHdr->dwMicroSecPerFrame;
  888. npMCI->dwRate = 1000000;
  889. npMCI->dwScale = npMCI->dwMicroSecPerFrame;
  890. /* Reject some bad values */
  891. if (!lpHdr->dwStreams || lpHdr->dwStreams > 255 || !npMCI->lFrames) {
  892. goto FileError;
  893. }
  894. npMCI->streams = (int) lpHdr->dwStreams;
  895. npMCI->dwBytesPerSec = lpHdr->dwMaxBytesPerSec;
  896. npMCI->wEarlyRecords = (UINT) lpHdr->dwInitialFrames;
  897. npMCI->dwSuggestedBufferSize = lpHdr->dwSuggestedBufferSize;
  898. SetRect(&npMCI->rcMovie,0,0,(int)lpHdr->dwWidth,(int)lpHdr->dwHeight);
  899. npMCI->dwFlags |= MCIAVI_HASINDEX;
  900. if (!(lpHdr->dwFlags & AVIF_ISINTERLEAVED)) {
  901. DPF(("File is not interleaved.\n"));
  902. npMCI->dwFlags |= MCIAVI_NOTINTERLEAVED;
  903. }
  904. SKIP_BYTES(dwHeaderSize); /* Skip rest of chunk */
  905. npMCI->paStreamInfo = (STREAMINFO NEAR *)
  906. LocalAlloc(LPTR, npMCI->streams * sizeof(STREAMINFO));
  907. // !!! error check
  908. for (stream = 0; stream < npMCI->streams; stream++) {
  909. AVIStreamHeader FAR * lpStream;
  910. HPSTR hpNextChunk;
  911. STREAMINFO * psi = &npMCI->paStreamInfo[stream];
  912. if (GET_DWORD() != FOURCC_LIST) {
  913. goto FileError;
  914. }
  915. dwHeaderSize = GET_DWORD(); /* Skip size */
  916. hpNextChunk = npMCI->lp + (dwHeaderSize + (dwHeaderSize & 1));
  917. if (GET_DWORD() != listtypeSTREAMHEADER) {
  918. goto FileError;
  919. }
  920. /* Now, we're at the begging of the stream's header chunks. */
  921. if (GET_DWORD() != ckidSTREAMHEADER) {
  922. goto FileError;
  923. }
  924. dwHeaderSize = GET_DWORD(); /* Skip size */
  925. /* Now, we're pointing at the stream header */
  926. lpStream = (AVIStreamHeader FAR *) npMCI->lp;
  927. hmemcpy(&psi->sh, lpStream, min(dwHeaderSize, sizeof(psi->sh)));
  928. //
  929. // reject files with more than one video stream.
  930. //
  931. if (psi->sh.fccType == streamtypeVIDEO &&
  932. npMCI->nVideoStreams >= 1) {
  933. DPF(("File has multiple video streams, giving it to AVIFILE.DLL\n"));
  934. goto DontHandleThisFile;
  935. }
  936. SKIP_BYTES(dwHeaderSize);
  937. /* Read the format */
  938. if (GET_DWORD() != ckidSTREAMFORMAT) {
  939. goto FileError;
  940. }
  941. dwHeaderSize = GET_DWORD(); /* Skip size */
  942. if (dwHeaderSize > 16384L) {
  943. goto FileError;
  944. }
  945. psi->cbFormat = dwHeaderSize;
  946. psi->lpFormat = GlobalAllocPtr(GHND,dwHeaderSize);
  947. if (!psi->lpFormat) {
  948. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  949. return FALSE;
  950. }
  951. hmemcpy(psi->lpFormat, npMCI->lp, dwHeaderSize);
  952. SKIP_BYTES(dwHeaderSize);
  953. if (PEEK_DWORD() == ckidSTREAMHANDLERDATA) {
  954. GET_DWORD();
  955. dwHeaderSize = GET_DWORD(); /* Skip size */
  956. psi->cbData = dwHeaderSize;
  957. psi->lpData = GlobalAllocPtr(GHND,dwHeaderSize);
  958. if (!psi->lpData) {
  959. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  960. return FALSE;
  961. }
  962. hmemcpy(psi->lpData, npMCI->lp, dwHeaderSize);
  963. /* Skip to end of Data chunk */
  964. SKIP_BYTES(dwHeaderSize);
  965. } else {
  966. psi->cbData = 0;
  967. psi->lpData = NULL;
  968. }
  969. InitStream(npMCI, psi);
  970. npMCI->lp = hpNextChunk;
  971. }
  972. return TRUE;
  973. FileError:
  974. npMCI->dwTaskError = MCIERR_INVALID_FILE;
  975. return FALSE;
  976. DontHandleThisFile:
  977. npMCI->dwTaskError = AVIERR_NOT_AVIFILE;
  978. return FALSE;
  979. }
  980. /***************************************************************************
  981. *
  982. * @doc INTERNAL MCIAVI
  983. *
  984. * @api BOOL | mciaviCloseFile | Close an AVI file.
  985. *
  986. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  987. *
  988. * @rdesc TRUE means OK, otherwise mci error in dwTaskError
  989. *
  990. ***************************************************************************/
  991. BOOL FAR PASCAL mciaviCloseFile (NPMCIGRAPHIC npMCI)
  992. {
  993. if (!npMCI)
  994. return FALSE;
  995. #ifdef DEBUG
  996. npMCI->mciid = MCIIDX;
  997. #endif
  998. if (npMCI->lpMMIOBuffer) {
  999. GlobalFreePtr(npMCI->lpMMIOBuffer);
  1000. npMCI->lpMMIOBuffer = NULL;
  1001. }
  1002. npMCI->hicDraw = NULL;
  1003. if (npMCI->hicDrawDefault) {
  1004. if (npMCI->hicDrawDefault != (HIC) -1)
  1005. ICClose(npMCI->hicDrawDefault);
  1006. npMCI->hicDrawDefault = NULL;
  1007. }
  1008. if (npMCI->hicDrawFull) {
  1009. if (npMCI->hicDrawFull != (HIC) -1)
  1010. ICClose(npMCI->hicDrawFull);
  1011. npMCI->hicDrawFull = NULL;
  1012. }
  1013. if (npMCI->hicDecompress) {
  1014. // !!! What if we never began it?
  1015. ICDecompressEnd(npMCI->hicDecompress);
  1016. ICClose(npMCI->hicDecompress);
  1017. npMCI->hicDecompress = NULL;
  1018. }
  1019. if (npMCI->hicInternal) {
  1020. ICClose(npMCI->hicInternal);
  1021. npMCI->hicInternal = NULL;
  1022. }
  1023. if (npMCI->hicInternalFull) {
  1024. ICClose(npMCI->hicInternalFull);
  1025. npMCI->hicInternalFull = NULL;
  1026. }
  1027. if (npMCI->hmmio) {
  1028. mmioClose(npMCI->hmmio, 0);
  1029. npMCI->hmmio = NULL;
  1030. }
  1031. if (npMCI->hmmioAudio) {
  1032. mmioClose(npMCI->hmmioAudio, 0);
  1033. npMCI->hmmioAudio = NULL;
  1034. }
  1035. if (npMCI->pWF) {
  1036. LocalFree((HANDLE) npMCI->pWF);
  1037. npMCI->pWF = NULL;
  1038. }
  1039. if (npMCI->pbiFormat) {
  1040. GlobalFreePtr(npMCI->pbiFormat);
  1041. npMCI->pbiFormat = NULL;
  1042. }
  1043. // if (npMCI->hpal) {
  1044. // DeleteObject(npMCI->hpal);
  1045. // npMCI->hpal = NULL;
  1046. // }
  1047. if (npMCI->hpDecompress) {
  1048. GlobalFreePtr(npMCI->hpDecompress);
  1049. npMCI->hpDecompress = NULL;
  1050. }
  1051. if (npMCI->hpIndex) {
  1052. GlobalFreePtr(npMCI->hpIndex);
  1053. npMCI->hpIndex = NULL;
  1054. }
  1055. if (npMCI->hpFrameIndex) {
  1056. GlobalFreePtr(npMCI->hpFrameIndex); //!!!NTBUG not same pointer!
  1057. npMCI->hpFrameIndex = NULL;
  1058. }
  1059. if (npMCI->pVolumeTable) {
  1060. LocalFree((HLOCAL)npMCI->pVolumeTable);
  1061. npMCI->pVolumeTable = NULL;
  1062. }
  1063. #ifdef USEAVIFILE
  1064. if (npMCI->pf) {
  1065. AVIFileClose(npMCI->pf);
  1066. npMCI->pf = NULL;
  1067. }
  1068. #endif
  1069. if (npMCI->paStreamInfo) {
  1070. int i;
  1071. for (i = 0; i < npMCI->streams; i++)
  1072. CloseStream(npMCI, &npMCI->paStreamInfo[i]);
  1073. LocalFree((HLOCAL)npMCI->paStreamInfo);
  1074. npMCI->paStreamInfo = NULL;
  1075. }
  1076. npMCI->streams = 0;
  1077. npMCI->nAudioStreams = 0;
  1078. npMCI->nVideoStreams = 0;
  1079. npMCI->nErrorStreams = 0;
  1080. npMCI->nOtherStreams = 0;
  1081. npMCI->wEarlyVideo = 0;
  1082. npMCI->wEarlyAudio = 0;
  1083. npMCI->wEarlyRecords = 0;
  1084. //!!! I bet we need to clear more
  1085. npMCI->dwFlags &= ~(MCIAVI_NOTINTERLEAVED |
  1086. MCIAVI_ANIMATEPALETTE |
  1087. MCIAVI_CANDRAW |
  1088. MCIAVI_HASINDEX);
  1089. return TRUE;
  1090. }
  1091. /***************************************************************************
  1092. *
  1093. * @doc INTERNAL MCIAVI
  1094. *
  1095. * @api BOOL | CloseStream | Close an StreamAVI file.
  1096. *
  1097. ***************************************************************************/
  1098. void NEAR PASCAL CloseStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1099. {
  1100. psi->dwFlags &= ~STREAM_ENABLED;
  1101. ////psi->sh.fccType = 0;
  1102. ////psi->sh.fccHandler = 0;
  1103. if (psi->lpFormat)
  1104. GlobalFreePtr(psi->lpFormat);
  1105. psi->lpFormat = NULL;
  1106. if (psi->lpData)
  1107. GlobalFreePtr(psi->lpData);
  1108. psi->lpData = NULL;
  1109. if (psi->hicDraw)
  1110. ICClose(psi->hicDraw);
  1111. psi->hicDraw = NULL;
  1112. #ifdef USEAVIFILE
  1113. if (psi->ps)
  1114. AVIStreamClose(psi->ps);
  1115. psi->ps = NULL;
  1116. #endif
  1117. }
  1118. /***************************************************************************
  1119. *
  1120. * @doc INTERNAL MCIAVI
  1121. *
  1122. * @api BOOL | InitStream | initialize a stream
  1123. *
  1124. ***************************************************************************/
  1125. BOOL NEAR PASCAL InitStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1126. {
  1127. BOOL f;
  1128. //
  1129. // set flags
  1130. //
  1131. if (psi->sh.dwFlags & AVISF_VIDEO_PALCHANGES)
  1132. psi->dwFlags |= STREAM_PALCHANGES;
  1133. psi->lStart = (LONG)psi->sh.dwStart;
  1134. psi->lEnd = (LONG)psi->sh.dwStart + psi->sh.dwLength;
  1135. if (psi->sh.fccType == streamtypeVIDEO &&
  1136. !(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED))
  1137. psi->lStart -= (LONG)psi->sh.dwInitialFrames;
  1138. switch(psi->sh.fccType) {
  1139. case streamtypeVIDEO:
  1140. f = InitVideoStream(npMCI, psi);
  1141. break;
  1142. case streamtypeAUDIO:
  1143. f = InitAudioStream(npMCI, psi);
  1144. break;
  1145. default:
  1146. f = InitOtherStream(npMCI, psi);
  1147. break;
  1148. }
  1149. if (!f) {
  1150. psi->dwFlags |= STREAM_ERROR;
  1151. npMCI->nErrorStreams++;
  1152. CloseStream(npMCI, psi);
  1153. }
  1154. //
  1155. // disable the stream if the file header says to
  1156. //
  1157. if (psi->sh.dwFlags & AVISF_DISABLED) {
  1158. psi->dwFlags &= ~STREAM_ENABLED;
  1159. }
  1160. return f;
  1161. }
  1162. /***************************************************************************
  1163. *
  1164. * @doc INTERNAL MCIAVI
  1165. *
  1166. * @api BOOL | InitVideoStream | initialize a video stream
  1167. *
  1168. ***************************************************************************/
  1169. BOOL NEAR PASCAL InitVideoStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1170. {
  1171. LPBITMAPINFOHEADER lpbi;
  1172. int stream = psi - npMCI->paStreamInfo;
  1173. npMCI->wEarlyVideo = (UINT)psi->sh.dwInitialFrames;
  1174. if (psi->sh.dwFlags & AVISF_VIDEO_PALCHANGES) {
  1175. //!!! is this right.
  1176. npMCI->dwFlags |= MCIAVI_ANIMATEPALETTE;
  1177. }
  1178. if (IsRectBogus(&psi->sh.rcFrame)) {
  1179. DPF(("BOGUS Stream rectangle [%d %d %d %d]\n", psi->sh.rcFrame));
  1180. SetRectEmpty(&psi->sh.rcFrame);
  1181. }
  1182. // In case the rectangle is totally wrong, chop it down to size....
  1183. // !!! What if the user _wants_ a zero-size RECT?
  1184. IntersectRect(&psi->sh.rcFrame, &psi->sh.rcFrame, &npMCI->rcMovie);
  1185. if (IsRectEmpty(&psi->sh.rcFrame)) {
  1186. DPF(("Video stream rect is empty, correcting\n"));
  1187. SetRect(&psi->sh.rcFrame, 0, 0,
  1188. (int)((LPBITMAPINFOHEADER)psi->lpFormat)->biWidth,
  1189. (int)((LPBITMAPINFOHEADER)psi->lpFormat)->biHeight);
  1190. }
  1191. //
  1192. // make sure the biCompression is right for RLE files.
  1193. //
  1194. lpbi = (LPBITMAPINFOHEADER)psi->lpFormat;
  1195. if (psi->sh.fccHandler == 0) {
  1196. if (lpbi->biCompression == 0)
  1197. psi->sh.fccHandler = comptypeDIB;
  1198. if (lpbi->biCompression == BI_RLE8 && lpbi->biBitCount == 8)
  1199. psi->sh.fccHandler = comptypeRLE;
  1200. if (lpbi->biCompression > 256)
  1201. psi->sh.fccHandler = lpbi->biCompression;
  1202. }
  1203. if (lpbi->biCompression <= BI_RLE8 && lpbi->biBitCount == 8) {
  1204. if (psi->sh.fccHandler == comptypeRLE0 ||
  1205. psi->sh.fccHandler == comptypeRLE)
  1206. lpbi->biCompression = BI_RLE8;
  1207. // Assuming a DIB handler has RGB data will blow up files that have RLE data.
  1208. // Unfortunately, VidEdit writes out files like this.
  1209. // if (psi->sh.fccHandler == comptypeDIB)
  1210. // lpbi->biCompression = BI_RGB;
  1211. }
  1212. //
  1213. // make sure the color table is set to the right size
  1214. //
  1215. if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
  1216. lpbi->biClrUsed = (1 << (int)lpbi->biBitCount);
  1217. //
  1218. // try to open draw handler
  1219. //
  1220. if (psi->sh.fccHandler) {
  1221. psi->hicDraw = ICDrawOpen(psi->sh.fccType,psi->sh.fccHandler,psi->lpFormat);
  1222. if (psi->hicDraw)
  1223. DPF(("Opened draw handler %4.4s:%4.4s\n", (LPSTR)&psi->sh.fccType,(LPSTR)&psi->sh.fccHandler));
  1224. }
  1225. //
  1226. // one video stream is the master, he controls the palette etc
  1227. // for lack of a better default the first video stream will
  1228. // become the master.
  1229. //
  1230. if (npMCI->pbiFormat == NULL) {
  1231. npMCI->nVideoStream = stream;
  1232. npMCI->psiVideo = psi;
  1233. npMCI->pbiFormat = (LPBITMAPINFOHEADER)
  1234. GlobalAllocPtr(GMEM_MOVEABLE, psi->cbFormat);
  1235. if (!npMCI->pbiFormat) {
  1236. npMCI->dwTaskError = MCIERR_OUT_OF_MEMORY;
  1237. return FALSE;
  1238. }
  1239. //
  1240. // copy the entire format over
  1241. //
  1242. hmemcpy(npMCI->pbiFormat,psi->lpFormat,psi->cbFormat);
  1243. npMCI->bih = *npMCI->pbiFormat;
  1244. npMCI->bih.biSize = sizeof(BITMAPINFOHEADER);
  1245. npMCI->bih.biCompression = BI_RGB;
  1246. if (npMCI->bih.biClrUsed) {
  1247. /* save the original colors. */
  1248. hmemcpy(npMCI->argb, (LPBYTE)npMCI->pbiFormat + npMCI->pbiFormat->biSize,
  1249. (int)npMCI->bih.biClrUsed * sizeof(RGBQUAD));
  1250. hmemcpy(npMCI->argbOriginal, (LPSTR) npMCI->pbiFormat + npMCI->pbiFormat->biSize,
  1251. (int)npMCI->bih.biClrUsed * sizeof(RGBQUAD));
  1252. }
  1253. //
  1254. // now open the decompressor, try fastdecompress if it will do it.
  1255. //
  1256. npMCI->hicDecompress = ICLocate(ICTYPE_VIDEO,psi->sh.fccHandler,
  1257. psi->lpFormat,NULL,ICMODE_FASTDECOMPRESS);
  1258. // fast decompress may not be supported
  1259. if (npMCI->hicDecompress == NULL) {
  1260. npMCI->hicDecompress = ICDecompressOpen(ICTYPE_VIDEO,
  1261. psi->sh.fccHandler,psi->lpFormat,NULL);
  1262. }
  1263. //
  1264. // set any state data.
  1265. //
  1266. if (npMCI->hicDecompress && psi->cbData) {
  1267. ICSetState(npMCI->hicDecompress, psi->lpData, psi->cbData);
  1268. }
  1269. if (psi->hicDraw == NULL && npMCI->hicDecompress == NULL &&
  1270. psi->sh.fccHandler != comptypeRLE0 &&
  1271. psi->sh.fccHandler != comptypeNONE &&
  1272. psi->sh.fccHandler != comptypeDIB &&
  1273. psi->sh.fccHandler != comptypeRLE &&
  1274. psi->sh.fccHandler != 0) {
  1275. DPF(("Unable to open compressor '%4.4ls'!!!\n", (LPSTR) &psi->sh.fccHandler));
  1276. npMCI->nVideoStream = -1;
  1277. npMCI->psiVideo = NULL;
  1278. GlobalFreePtr(npMCI->pbiFormat);
  1279. npMCI->pbiFormat = NULL;
  1280. //
  1281. // we would like to return a custom, error but MCI will not
  1282. // find the error string because it has unloaded us (because
  1283. // the open failed), so we return a bogus generic error.
  1284. //
  1285. if (npMCI->streams == 1) // this is the only stream
  1286. npMCI->dwTaskError = MMSYSERR_NODRIVER; // MCIERR_AVI_NOCOMPRESSOR;
  1287. return FALSE; // cant load this video stream
  1288. }
  1289. }
  1290. else {
  1291. //
  1292. // this is not the default video stream find a draw handler that
  1293. // can deal with the stream.
  1294. //
  1295. //
  1296. // try VIDS.DRAW
  1297. //
  1298. // if that fails open a draw handler not-specific to the format
  1299. //
  1300. if (psi->hicDraw == NULL) {
  1301. psi->hicDraw = ICOpen(psi->sh.fccType,FOURCC_AVIDraw,ICMODE_DRAW);
  1302. if (psi->hicDraw)
  1303. DOUT("Opened draw handler VIDS.DRAW\n");
  1304. if (psi->hicDraw && ICDrawQuery(psi->hicDraw,psi->lpFormat) != ICERR_OK) {
  1305. DOUT("Closing VIDS.DRAW because it cant handle this format");
  1306. ICClose(psi->hicDraw);
  1307. psi->hicDraw = NULL;
  1308. }
  1309. }
  1310. //
  1311. // if that fails open our internal handler.
  1312. //
  1313. if (psi->hicDraw == NULL) {
  1314. psi->hicDraw = ICOpenFunction(psi->sh.fccType,
  1315. FOURCC_AVIDraw,ICMODE_DRAW,(FARPROC)ICAVIDrawProc);
  1316. if (psi->hicDraw)
  1317. DOUT("Opened Internal draw handler\n");
  1318. }
  1319. }
  1320. npMCI->dwFlags |= MCIAVI_NEEDDRAWBEGIN;
  1321. psi->dwFlags |= STREAM_VIDEO; // is a video stream
  1322. psi->dwFlags |= STREAM_ENABLED;
  1323. npMCI->nVideoStreams++;
  1324. return TRUE;
  1325. }
  1326. /***************************************************************************
  1327. *
  1328. * @doc INTERNAL MCIAVI
  1329. *
  1330. * @api BOOL | InitAudioStream | initialize a audio stream
  1331. *
  1332. ***************************************************************************/
  1333. BOOL NEAR PASCAL InitAudioStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1334. {
  1335. int stream = psi - npMCI->paStreamInfo;
  1336. LPWAVEFORMAT pwf;
  1337. npMCI->wEarlyAudio = (UINT)psi->sh.dwInitialFrames;
  1338. pwf = (LPWAVEFORMAT)psi->lpFormat;
  1339. if (pwf->nChannels == 0 || pwf->nSamplesPerSec == 0) {
  1340. return FALSE;
  1341. }
  1342. if (pwf->wFormatTag == WAVE_FORMAT_PCM) {
  1343. pwf->nBlockAlign = pwf->nChannels *
  1344. ((((LPPCMWAVEFORMAT)pwf)->wBitsPerSample + 7) / 8);
  1345. pwf->nAvgBytesPerSec = pwf->nBlockAlign * pwf->nSamplesPerSec;
  1346. }
  1347. psi->sh.dwSampleSize = pwf->nBlockAlign;
  1348. psi->dwFlags |= STREAM_AUDIO; // audio stream
  1349. psi->dwFlags |= STREAM_ENABLED; // enabled by default.
  1350. //
  1351. // make sure dwRate and dwScale are right
  1352. // dwRate/dwScale should be blocks/sec
  1353. //
  1354. Assert(muldiv32(pwf->nAvgBytesPerSec,1000,pwf->nBlockAlign) ==
  1355. muldiv32(psi->sh.dwRate, 1000, psi->sh.dwScale));
  1356. //
  1357. // just to be safe set these ourself to the right value.
  1358. //
  1359. psi->sh.dwRate = pwf->nAvgBytesPerSec;
  1360. psi->sh.dwScale = pwf->nBlockAlign;
  1361. //
  1362. // only one audio stream can be active at once
  1363. // for lack of a better default the first audio stream will
  1364. // become the active one.
  1365. //
  1366. if (npMCI->nAudioStreams == 0) {
  1367. npMCI->nAudioStream = stream;
  1368. npMCI->psiAudio = psi;
  1369. }
  1370. npMCI->nAudioStreams++;
  1371. return TRUE;
  1372. }
  1373. /***************************************************************************
  1374. *
  1375. * @doc INTERNAL MCIAVI
  1376. *
  1377. * @api BOOL | InitOtherStream | initialize a random stream
  1378. *
  1379. ***************************************************************************/
  1380. BOOL NEAR PASCAL InitOtherStream(NPMCIGRAPHIC npMCI, STREAMINFO *psi)
  1381. {
  1382. int stream = psi - npMCI->paStreamInfo;
  1383. /* Open the specified video compressor */
  1384. psi->hicDraw = ICDrawOpen(psi->sh.fccType,psi->sh.fccHandler,psi->lpFormat);
  1385. if (psi->hicDraw == NULL) {
  1386. DPF(("Unable to play stream!\n"));
  1387. return FALSE;
  1388. }
  1389. if (psi->cbData > 0) {
  1390. ICSetState(psi->hicDraw, psi->lpData, psi->cbData);
  1391. }
  1392. psi->dwFlags |= STREAM_ENABLED;
  1393. ////psi->dwFlags |= STREAM_OTHER;
  1394. npMCI->nOtherStreams++;
  1395. return TRUE;
  1396. }
  1397. /***************************************************************************
  1398. *
  1399. * @doc INTERNAL MCIAVI
  1400. *
  1401. * @api BOOL | CleanIndex | This function cleans up the index loaded by
  1402. * ReadIndex() it does the following when cleaning up the index:
  1403. *
  1404. * converts all offsets to be absolute
  1405. *
  1406. * converts "alpha" format index's into new format.
  1407. *
  1408. * computes the max buffer size needed to read this file.
  1409. *
  1410. ***************************************************************************/
  1411. static BOOL NEAR CleanIndex(NPMCIGRAPHIC npMCI)
  1412. {
  1413. LONG lScan;
  1414. AVIINDEXENTRY FAR * px;
  1415. AVIINDEXENTRY FAR * pxRec=NULL;
  1416. DWORD lIndexAdjust;
  1417. Assert(npMCI->hpIndex != NULL);
  1418. px = (AVIINDEXENTRY FAR *)npMCI->hpIndex;
  1419. #ifdef ALPHAFILES
  1420. if (npMCI->dwFlags & MCIAVI_USINGALPHAFORMAT)
  1421. lIndexAdjust = 0;
  1422. else
  1423. #endif
  1424. if (// (avihdr.dwFlags & AVIF_MUSTUSEINDEX) ||
  1425. (px->dwChunkOffset < 100))
  1426. lIndexAdjust = npMCI->dwMovieListOffset;
  1427. else
  1428. lIndexAdjust = (npMCI->dwMovieListOffset + sizeof(DWORD)) -
  1429. px->dwChunkOffset;
  1430. //!!! only compute this for the video stream! (or interleaved...)
  1431. npMCI->dwSuggestedBufferSize = 0; // lets get this exactly right
  1432. DPF(("Adjusting index by %ld bytes....\n", lIndexAdjust));
  1433. /* Can we do anything to see if the index is valid? */
  1434. for (lScan = 0; lScan < (LONG)npMCI->macIndex;
  1435. lScan++, ++((AVIINDEXENTRY _huge *)px)) {
  1436. DWORD ckid;
  1437. //
  1438. // adjust the offset to be absolute
  1439. //
  1440. px->dwChunkOffset += lIndexAdjust;
  1441. // get ckid
  1442. ckid = px->ckid;
  1443. //
  1444. // make sure the buffer size is right, ignore audio chunks because
  1445. // they are either in a 'rec' or we will be reading them into
  1446. // internal buffers not the main buffer.365
  1447. //
  1448. if (((npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) ||
  1449. ckid == listtypeAVIRECORD) &&
  1450. TWOCCFromFOURCC(ckid) != cktypeWAVEbytes) {
  1451. if (px->dwChunkLength + 8 > npMCI->dwSuggestedBufferSize)
  1452. npMCI->dwSuggestedBufferSize = px->dwChunkLength + 12;
  1453. }
  1454. #ifdef ALPHAFILES
  1455. //
  1456. // convert a "old" index to a new index
  1457. //
  1458. if (npMCI->dwFlags & MCIAVI_USINGALPHAFORMAT) {
  1459. switch(ckid) {
  1460. case ckidDIBbits:
  1461. px->dwFlags |= AVIIF_KEYFRAME;
  1462. px->ckid = MAKEAVICKID(cktypeDIBbits, 0);
  1463. break;
  1464. case ckidDIBcompressed:
  1465. px->ckid = MAKEAVICKID(cktypeDIBcompressed, 0);
  1466. break;
  1467. case ckidDIBhalfframe:
  1468. px->ckid = MAKEAVICKID(cktypeDIBhalf, 0);
  1469. break;
  1470. case ckidPALchange:
  1471. px->ckid = MAKEAVICKID(cktypePALchange, 0);
  1472. break;
  1473. case ckidWAVEbytes:
  1474. px->ckid = MAKEAVICKID(cktypeWAVEbytes, 1);
  1475. break;
  1476. case ckidWAVEsilence:
  1477. px->ckid = MAKEAVICKID(cktypeWAVEsilence, 1);
  1478. break;
  1479. case ckidAVIPADDING:
  1480. case ckidOLDPADDING:
  1481. px->ckid = ckidAVIPADDING;
  1482. break;
  1483. }
  1484. ckid = px->ckid;
  1485. }
  1486. #endif
  1487. //
  1488. // do special things with the video stream.
  1489. //
  1490. if (StreamFromFOURCC(ckid) == (UINT)npMCI->nVideoStream) {
  1491. //
  1492. // fix up bogus index's by adding any missing AVIIF_KEYFRAME
  1493. // bits. ie this only applies for RLE files.
  1494. //
  1495. if (TWOCCFromFOURCC(ckid) == cktypeDIBbits &&
  1496. VIDFMT(npMCI->nVideoStream)->biCompression <= BI_RLE8)
  1497. px->dwFlags |= AVIIF_KEYFRAME;
  1498. //
  1499. // for video streams, make sure the palette changes are marked
  1500. // as a no time chunk
  1501. //
  1502. if (TWOCCFromFOURCC(ckid) == cktypePALchange)
  1503. px->dwFlags |= AVIIF_NOTIME/*|AVIIF_PALCHANGE*/;
  1504. //
  1505. // make sure the 'REC ' list has the right flags.
  1506. //
  1507. if (pxRec) {
  1508. if ((px->dwFlags & AVIIF_KEYFRAME) !=
  1509. (pxRec->dwFlags & AVIIF_KEYFRAME)) {
  1510. // Record list does not have correct flags
  1511. pxRec->dwFlags &= ~AVIIF_KEYFRAME;
  1512. pxRec->dwFlags |= (px->dwFlags & AVIIF_KEYFRAME);
  1513. }
  1514. }
  1515. }
  1516. if (ckid == listtypeAVIRECORD) {
  1517. pxRec = px;
  1518. if (npMCI->dwFlags & MCIAVI_NOTINTERLEAVED) {
  1519. DPF(("Non interleaved file with a 'REC ' in it?\n"));
  1520. npMCI->wEarlyRecords = max(npMCI->wEarlyVideo, npMCI->wEarlyAudio);
  1521. if (npMCI->wEarlyRecords > 0) {
  1522. DPF(("Interlaved file with bad header\n"));
  1523. npMCI->dwFlags &= ~MCIAVI_NOTINTERLEAVED;
  1524. }
  1525. }
  1526. }
  1527. }
  1528. return TRUE;
  1529. }
  1530. /***************************************************************************
  1531. *
  1532. * @doc INTERNAL MCIAVI
  1533. *
  1534. * @api BOOL | MakeFrameIndex | makes the frame index
  1535. *
  1536. * the frame index is a array of AVIFRAMEINDEX entries, one for each
  1537. * frame in the movie. using the frame index we can easily find
  1538. * a given frame, along with it's keyframe and palette.
  1539. *
  1540. ***************************************************************************/
  1541. static BOOL NEAR MakeFrameIndex(NPMCIGRAPHIC npMCI)
  1542. {
  1543. LONG nFrames;
  1544. LONG iFrameStart;
  1545. LONG iFrame;
  1546. LONG iKeyFrame;
  1547. LONG nKeyFrames;
  1548. LONG iScan;
  1549. LONG iNewIndex;
  1550. LONG iPalette;
  1551. BOOL fInterleaved;
  1552. DWORD ckid;
  1553. STREAMINFO *psi;
  1554. AVIINDEXENTRY _huge * pNewIndex;
  1555. AVIINDEXENTRY _huge * pIndexEntry;
  1556. AVIFRAMEINDEX _huge * pFrameIndex;
  1557. if (npMCI->nVideoStreams == 0)
  1558. return TRUE;
  1559. if (npMCI->hpFrameIndex != NULL)
  1560. return TRUE;
  1561. psi = npMCI->psiVideo;
  1562. Assert(psi != NULL);
  1563. fInterleaved = !(npMCI->dwFlags & MCIAVI_NOTINTERLEAVED);
  1564. if (fInterleaved &&
  1565. muldiv32(npMCI->dwRate, 1000, npMCI->dwScale) !=
  1566. muldiv32(psi->sh.dwRate, 1000, psi->sh.dwScale)) {
  1567. //
  1568. // master video stream should match the movie rate!
  1569. //
  1570. AssertSz(FALSE, "Video stream differnet rate than movie");
  1571. npMCI->dwRate = psi->sh.dwRate;
  1572. npMCI->dwScale = psi->sh.dwScale;
  1573. }
  1574. if (fInterleaved)
  1575. iFrameStart = -(LONG)npMCI->wEarlyRecords;
  1576. else
  1577. iFrameStart = -(LONG)npMCI->wEarlyVideo;
  1578. nFrames = npMCI->lFrames - iFrameStart;
  1579. npMCI->hpFrameIndex = (LPVOID)GlobalAllocPtr(GMEM_SHARE|GHND,
  1580. (DWORD)(nFrames+1) * sizeof(AVIFRAMEINDEX));
  1581. if (npMCI->hpFrameIndex == NULL) {
  1582. DPF(("Couldn't allocate memory for frame index!\n"));
  1583. return FALSE;
  1584. }
  1585. //
  1586. // do this so we can just index the array with the frame number
  1587. // (positive or neg)
  1588. //
  1589. npMCI->hpFrameIndex += (-iFrameStart);
  1590. pFrameIndex = npMCI->hpFrameIndex;
  1591. iFrame = iFrameStart;
  1592. iKeyFrame = -(LONG)npMCI->wEarlyVideo; // iFrameStart;
  1593. iNewIndex = 0;
  1594. iPalette = -1; // first palette
  1595. nKeyFrames= 0;
  1596. #ifdef USEAVIFILE
  1597. if (npMCI->pf) {
  1598. PAVISTREAM ps = SI(npMCI->nVideoStream)->ps;
  1599. for (iFrame = 0; iFrame < npMCI->lFrames; iFrame++) {
  1600. LONG iKey;
  1601. iKey = AVIStreamFindSample(ps,iFrame,FIND_PREV|FIND_KEY);
  1602. iPalette = AVIStreamFindSample(ps,iFrame,FIND_PREV|FIND_FORMAT);
  1603. if (iKey != -1)
  1604. iKeyFrame = iKey;
  1605. if (iPalette == -1)
  1606. iPalette = 0;
  1607. pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
  1608. pFrameIndex[iFrame].iNextKey = 0;
  1609. pFrameIndex[iFrame].iPalette = (WORD)iPalette;
  1610. pFrameIndex[iFrame].dwOffset = 0;
  1611. pFrameIndex[iFrame].dwLength = 0;
  1612. Assert(iPalette <= 0xFFFF);
  1613. if (iFrame - iKeyFrame > 0xFFFF) {
  1614. //!!! we need to set a flag!
  1615. //!!! we need to throw out the index!
  1616. AssertSz(FALSE, "File has too few key frames");
  1617. pFrameIndex[iFrame].iPrevKey = 0;
  1618. }
  1619. }
  1620. goto ack;
  1621. }
  1622. #endif
  1623. Assert(npMCI->hpIndex != NULL);
  1624. Assert(npMCI->macIndex != 0L);
  1625. pNewIndex = npMCI->hpIndex;
  1626. pIndexEntry = npMCI->hpIndex;
  1627. for (iScan = 0; iScan < (LONG)npMCI->macIndex; iScan++, pIndexEntry++) {
  1628. ckid = pIndexEntry->ckid;
  1629. //
  1630. // check for palette changes.
  1631. //
  1632. if (StreamFromFOURCC(ckid) == (UINT)npMCI->nVideoStream &&
  1633. TWOCCFromFOURCC(ckid) == cktypePALchange) {
  1634. iPalette = iNewIndex;
  1635. pNewIndex[iNewIndex++] = *pIndexEntry;
  1636. if (fInterleaved)
  1637. pFrameIndex[iFrame-1].iPalette = (WORD)iPalette;
  1638. }
  1639. //
  1640. // remove the video stream from the master index
  1641. //
  1642. if ((ckid != listtypeAVIRECORD) &&
  1643. (StreamFromFOURCC(ckid) != (UINT)npMCI->nVideoStream)) {
  1644. pNewIndex[iNewIndex++] = *pIndexEntry;
  1645. }
  1646. //
  1647. // in interleaved files a "frame" happens every list record
  1648. //
  1649. // in non-interleaved files a "frame" happens every piece of
  1650. // data in the video stream (except no time chunks)
  1651. //
  1652. if (fInterleaved) {
  1653. if (ckid != listtypeAVIRECORD)
  1654. continue;
  1655. } else {
  1656. if ((StreamFromFOURCC(ckid) != (UINT)npMCI->nVideoStream) ||
  1657. (pIndexEntry->dwFlags & AVIIF_NOTIME))
  1658. continue;
  1659. }
  1660. AssertSz(iFrame < npMCI->lFrames,"Too many frames in index!");
  1661. if (iFrame >= npMCI->lFrames) {
  1662. break;
  1663. }
  1664. if (pIndexEntry->dwFlags & AVIIF_KEYFRAME) {
  1665. iKeyFrame = iFrame;
  1666. nKeyFrames++;
  1667. }
  1668. pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
  1669. pFrameIndex[iFrame].iNextKey = 0;
  1670. pFrameIndex[iFrame].iPalette = (WORD)iPalette;
  1671. pFrameIndex[iFrame].dwOffset = pIndexEntry->dwChunkOffset;
  1672. pFrameIndex[iFrame].dwLength = pIndexEntry->dwChunkLength;
  1673. if (fInterleaved)
  1674. pFrameIndex[iFrame].dwOffset += 3 * sizeof(DWORD);
  1675. Assert(iPalette <= 0xFFFF);
  1676. if (iFrame - iKeyFrame > 0xFFFF) {
  1677. //!!! we need to set a flag!
  1678. //!!! we need to throw out the index!
  1679. AssertSz(FALSE, "File has too few key frames");
  1680. pFrameIndex[iFrame].iPrevKey = 0;
  1681. }
  1682. iFrame++;
  1683. }
  1684. ack:
  1685. //
  1686. // iFrame better equal npMCI->lFrames
  1687. //
  1688. Assert(iFrame == npMCI->lFrames);
  1689. if (iFrame < npMCI->lFrames)
  1690. npMCI->lFrames = iFrame;
  1691. //
  1692. // make a "dummy" last frame
  1693. //
  1694. pFrameIndex[iFrame].iPrevKey = (UINT)(iFrame - iKeyFrame);
  1695. pFrameIndex[iFrame].iNextKey = 0;
  1696. pFrameIndex[iFrame].iPalette = (WORD)iPalette;
  1697. pFrameIndex[iFrame].dwOffset = 0;
  1698. pFrameIndex[iFrame].dwLength = 0;
  1699. //
  1700. // compute the key frames every value
  1701. //
  1702. if (nKeyFrames) {
  1703. if (nKeyFrames > 1)
  1704. npMCI->dwKeyFrameInfo = (DWORD)((nFrames + nKeyFrames/2)/nKeyFrames);
  1705. else
  1706. npMCI->dwKeyFrameInfo = 0;
  1707. }
  1708. //
  1709. // now go through the index, and fix all the iNextKey fields
  1710. //
  1711. pFrameIndex = npMCI->hpFrameIndex;
  1712. ////iKeyFrame = npMCI->lFrames; //!!! what should this be set to? zero?
  1713. for (iFrame = npMCI->lFrames; iFrame>=iFrameStart; iFrame--)
  1714. {
  1715. if (pFrameIndex[iFrame].iPrevKey == 0)
  1716. iKeyFrame = iFrame;
  1717. if (iKeyFrame >= iFrame)
  1718. pFrameIndex[iFrame].iNextKey = (UINT)(iKeyFrame - iFrame);
  1719. else
  1720. pFrameIndex[iFrame].iNextKey = 0xFFFF; // way far away
  1721. if (iKeyFrame - iFrame > 0xFFFF) {
  1722. //!!! we need to set a flag!
  1723. //!!! we need to throw out the index!
  1724. AssertSz(FALSE, "File has too few key frames");
  1725. pFrameIndex[iFrame].iNextKey = 0;
  1726. }
  1727. }
  1728. //
  1729. // we dont need the index, if we are using AVIFile or
  1730. // we have a interleaved file. when the file is interleaved
  1731. // we never do random access (except for palette changes)
  1732. //
  1733. // !!!this is not true, we need the index iff we have a audio only
  1734. // file or we play a interleaved file real slow.
  1735. //
  1736. if (npMCI->pf /* ||
  1737. (fInterleaved && !(npMCI->dwFlags & MCIAVI_ANIMATEPALETTE))*/ ) {
  1738. DOUT("The Master index must go!\n");
  1739. iNewIndex = 0;
  1740. }
  1741. //
  1742. // now re-alloc the master index down to size.
  1743. //
  1744. // !!! do we even need the master index anymore, for interleaved files?
  1745. //
  1746. DPF(("Master index was %ld entries now %ld\n",npMCI->macIndex, iNewIndex));
  1747. npMCI->macIndex = iNewIndex;
  1748. if (iNewIndex > 0) {
  1749. npMCI->hpIndex = (AVIINDEXENTRY _huge *)
  1750. GlobalReAllocPtr(npMCI->hpIndex,
  1751. (LONG)iNewIndex * sizeof(AVIINDEXENTRY),
  1752. GMEM_MOVEABLE | GMEM_SHARE);
  1753. Assert(npMCI->hpIndex != NULL);
  1754. }
  1755. else {
  1756. if (npMCI->hpIndex)
  1757. GlobalFreePtr(npMCI->hpIndex);
  1758. npMCI->hpIndex = NULL;
  1759. }
  1760. return TRUE;
  1761. }
  1762. /***************************************************************************
  1763. *
  1764. * @doc INTERNAL MCIAVI
  1765. *
  1766. * @api BOOL | ReadIndex | Read the index into npMCI->hpIndex. Should
  1767. * only be called if the HASINDEX flag is set.
  1768. *
  1769. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data
  1770. *
  1771. * @rdesc TRUE means no errors, false means unable to read index.
  1772. *
  1773. ***************************************************************************/
  1774. BOOL FAR PASCAL ReadIndex(NPMCIGRAPHIC npMCI)
  1775. {
  1776. MMCKINFO ck;
  1777. DWORD dwOldPos;
  1778. if (npMCI->hpIndex || npMCI->hpFrameIndex)
  1779. return TRUE;
  1780. if (!(npMCI->dwFlags & MCIAVI_HASINDEX))
  1781. return FALSE;
  1782. if (npMCI->pf) {
  1783. MakeFrameIndex(npMCI);
  1784. return TRUE;
  1785. }
  1786. #if 0
  1787. if (GetCurrentTask() != npMCI->hTask) {
  1788. /* this function is called (from GraphicStatus) when
  1789. * possibly playing - so we have to suspend play while we read
  1790. * the index.
  1791. */
  1792. TEMPORARYSTATE ts;
  1793. if (StopTemporarily(npMCI, &ts) == 0) {
  1794. mciaviTaskMessage(npMCI, TASKREADINDEX);
  1795. RestartAgain(npMCI, &ts);
  1796. return (npMCI->hpIndex != NULL);
  1797. }
  1798. return(FALSE);
  1799. }
  1800. #else
  1801. if (GetCurrentTask() != npMCI->hTask)
  1802. return FALSE;
  1803. #endif
  1804. dwOldPos = mmioSeek(npMCI->hmmio, 0, SEEK_CUR);
  1805. DPF(("Reading index: starting from %lx\n", npMCI->dwBigListEnd));
  1806. if (mmioSeek(npMCI->hmmio, npMCI->dwBigListEnd, SEEK_SET) == -1) {
  1807. IndexReadError:
  1808. DPF(("Error reading index!\n"));
  1809. npMCI->dwFlags &= ~(MCIAVI_HASINDEX);
  1810. mmioSeek(npMCI->hmmio, dwOldPos, SEEK_SET);
  1811. return FALSE;
  1812. }
  1813. ck.ckid = ckidAVINEWINDEX;
  1814. if (mmioDescend(npMCI->hmmio, &ck, NULL, MMIO_FINDCHUNK) != 0) {
  1815. goto IndexReadError;
  1816. }
  1817. /* A zero-size index isn't much good. */
  1818. if (ck.cksize == 0)
  1819. goto IndexReadError;
  1820. npMCI->macIndex = ck.cksize / sizeof(AVIINDEXENTRY);
  1821. npMCI->hpIndex = (AVIINDEXENTRY _huge *)
  1822. GlobalAllocPtr(GMEM_SHARE | GMEM_MOVEABLE, ck.cksize);
  1823. if (!npMCI->hpIndex) {
  1824. DPF(("Insufficient memory to read index.\n"));
  1825. goto IndexReadError;
  1826. }
  1827. #ifndef WIN32
  1828. Assert(OFFSETOF(npMCI->hpIndex) == 0);
  1829. #endif
  1830. if (mmioRead(npMCI->hmmio, (HPSTR) npMCI->hpIndex, ck.cksize) != (LONG) ck.cksize) {
  1831. Assert(0);
  1832. goto IndexReadError;
  1833. }
  1834. CleanIndex(npMCI);
  1835. MakeFrameIndex(npMCI);
  1836. ////should we do this for audio? remove video data?
  1837. ////MakeStreamIndex(npMCI, ???);
  1838. mmioSeek(npMCI->hmmio, dwOldPos, SEEK_SET);
  1839. return TRUE;
  1840. }
  1841. /***************************************************************************
  1842. * @doc INTERNAL MCIAVI
  1843. *
  1844. * @api BOOL | IsRectBogus | 'nuf said
  1845. *
  1846. ***************************************************************************/
  1847. static BOOL NEAR PASCAL IsRectBogus(LPRECT prc)
  1848. {
  1849. if (prc->right - prc->left <= 0 ||
  1850. prc->bottom - prc->top <= 0 ||
  1851. prc->bottom <= 0 ||
  1852. prc->right <= 0)
  1853. return TRUE;
  1854. else
  1855. return FALSE;
  1856. }
  1857. /***************************************************************************
  1858. *
  1859. * @doc INTERNAL MCIAVI
  1860. *
  1861. * @api LONG | atol | local version of atol
  1862. *
  1863. ***************************************************************************/
  1864. static LONG NEAR PASCAL atol(char *sz)
  1865. {
  1866. LONG l = 0;
  1867. while (*sz && *sz >= '0' && *sz <= '9')
  1868. l = l*10 + *sz++ - '0';
  1869. return l;
  1870. }
  1871. #ifndef WIN32
  1872. /*--------------------------------------------------------------------------
  1873. *
  1874. * IsCDROMDrive() -
  1875. *
  1876. * Purpose: Return non-zero if a RAM drive
  1877. *
  1878. * wDrive drive index (0=A, 1=B, ...)
  1879. *
  1880. * return TRUE/FALSE
  1881. *-------------------------------------------------------------------------*/
  1882. #pragma optimize("", off)
  1883. static BOOL NEAR PASCAL IsCDROMDrive(UINT wDrive)
  1884. {
  1885. BOOL f;
  1886. _asm {
  1887. mov ax, 1500h /* first test for presence of MSCDEX */
  1888. xor bx, bx
  1889. int 2fh
  1890. mov ax, bx /* MSCDEX is not there if bx is still zero */
  1891. or ax, ax /* ...so return FALSE from this function */
  1892. jz no_mscdex
  1893. mov ax, 150bh /* MSCDEX driver check API */
  1894. mov cx, wDrive /* ...cx is drive index */
  1895. int 2fh
  1896. no_mscdex:
  1897. mov f,ax
  1898. }
  1899. return f;
  1900. }
  1901. #pragma optimize("", on)
  1902. /***************************************************************************
  1903. *
  1904. * @doc INTERNAL MCIAVI
  1905. *
  1906. * @api BOOL | IsNetFile | is the passed file on a network drive?
  1907. *
  1908. ***************************************************************************/
  1909. static BOOL NEAR PASCAL IsNetFile(LPTSTR szFile)
  1910. {
  1911. OFSTRUCT of;
  1912. if (OpenFile(szFile, &of, OF_PARSE) == -1)
  1913. return FALSE;
  1914. AnsiUpper(of.szPathName);
  1915. if (of.szPathName[0] == '\\' && of.szPathName[1] == '\\')
  1916. return TRUE;
  1917. if (of.szPathName[0] == '/' && of.szPathName[1] == '/')
  1918. return TRUE;
  1919. if (of.szPathName[1] == ':' &&
  1920. GetDriveType(of.szPathName[0] - 'A') == DRIVE_REMOTE &&
  1921. !IsCDROMDrive(of.szPathName[0] - 'A'))
  1922. return TRUE;
  1923. return FALSE;
  1924. }
  1925. /***************************************************************************
  1926. *
  1927. * @doc INTERNAL MCIAVI
  1928. *
  1929. * @api BOOL | IsCDROMFile | is the passed file on a CD-ROM drive?
  1930. *
  1931. ***************************************************************************/
  1932. static BOOL NEAR PASCAL IsCDROMFile(LPTSTR szFile)
  1933. {
  1934. OFSTRUCT of;
  1935. if (OpenFile(szFile, &of, OF_PARSE) == -1)
  1936. return FALSE;
  1937. AnsiUpper(of.szPathName);
  1938. if (of.szPathName[0] == '\\' && of.szPathName[1] == '\\')
  1939. return FALSE;
  1940. if (of.szPathName[0] == '/' && of.szPathName[1] == '/')
  1941. return FALSE;
  1942. if (of.szPathName[1] == ':' &&
  1943. GetDriveType(of.szPathName[0] - 'A') == DRIVE_REMOTE &&
  1944. IsCDROMDrive(of.szPathName[0] - 'A'))
  1945. return TRUE;
  1946. return FALSE;
  1947. }
  1948. /***************************************************************************
  1949. *
  1950. * @doc INTERNAL MCIAVI
  1951. *
  1952. * @api UINT | GetFileDriveType | return drive type given a file
  1953. *
  1954. * DRIVE_CDROM
  1955. * DRIVE_REMOTE
  1956. * DRIVE_FIXED
  1957. * DRIVE_REMOVABLE
  1958. *
  1959. ***************************************************************************/
  1960. static UINT NEAR PASCAL GetFileDriveType(LPSTR szPath)
  1961. {
  1962. if (IsCDROMFile(szPath))
  1963. return DRIVE_CDROM;
  1964. if (IsNetFile(szPath))
  1965. return DRIVE_REMOTE;
  1966. if (szPath[1] == ':')
  1967. return GetDriveType(szPath[0] - 'A');
  1968. return 0;
  1969. }
  1970. #endif