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.

1136 lines
33 KiB

  1. /******************************************************************************
  2. Copyright (C) Microsoft Corporation 1985-1995. All rights reserved.
  3. Title: device.c - Multimedia Systems Media Control Interface
  4. driver for AVI.
  5. *****************************************************************************/
  6. #include "graphic.h"
  7. #include "avitask.h"
  8. #define ALIGNULONG(i) ((i+3)&(~3)) /* ULONG aligned ! */
  9. #define WIDTHBYTES(i) ((unsigned)((i+31)&(~31))/8) /* ULONG aligned ! */
  10. #define DIBWIDTHBYTES(bi) (DWORD)WIDTHBYTES((int)(bi).biWidth * (int)(bi).biBitCount)
  11. // from wownt32.h
  12. VOID (WINAPI * pWOWYield16)(VOID);
  13. #ifdef DEBUG
  14. #define AssertUserThread(npMCI) \
  15. { \
  16. DWORD thread = GetCurrentThreadId(); \
  17. Assert((npMCI)->hTask != (HTASK)thread); \
  18. Assert(!((npMCI)->hwndDefault) || ((DWORD_PTR)GetWindowTask((npMCI)->hwndDefault) != thread));\
  19. }
  20. #else
  21. #define AssertUserThread(npMCI)
  22. #endif
  23. /*
  24. * send a request to the worker thread, and wait for it to complete,
  25. * then return the result
  26. *
  27. * We must hold the CmdCritSec to stop other threads from making requests.
  28. *
  29. * if bDelayedComplete is true, the request is one that has two phases:
  30. *
  31. * phase 1: initiating the operation (eg starting play). No other
  32. * requests are permitted during this phase, so we hold the
  33. * critical section and wait. No yielding of any sort is safe
  34. * at this point, since re-entry on the same thread is not
  35. * something we can handle well. This means that the worker
  36. * thread must not do anything before setting hEventResponse
  37. * that could block on our processing a sendmessage
  38. *
  39. * phase 2: while the play is taking place, we must process messages,
  40. * yield to the app and allow other requests (eg stop).
  41. * For this, we wait on a second event, timing out and yielding
  42. * to the driver 10 times a second.
  43. *
  44. */
  45. DWORD
  46. mciaviTaskRequest(
  47. NPMCIGRAPHIC npMCI,
  48. UINT message,
  49. DWORD dwFlags,
  50. LPARAM lParam,
  51. LPARAM dwCallback,
  52. BOOL bDelayedComplete
  53. )
  54. {
  55. DWORD dwRet;
  56. MSG msg;
  57. #ifdef _WIN32
  58. // the gdi request queue is per-thread. We must flush the
  59. // app thread q here, or updates done to the window at the apps
  60. // request may appear before updates done by the app itself beforehand
  61. GdiFlush();
  62. #endif
  63. // get the critsec that controls sending requests
  64. EnterCriticalSection(&npMCI->CmdCritSec);
  65. if (IsBadReadPtr(npMCI, sizeof(MCIGRAPHIC))) {
  66. // device has been closed beneath us!
  67. DPF(("help - npMCI has gone away"));
  68. // not safe to leave critsec or dec count
  69. return MCIERR_DEVICE_NOT_READY;
  70. }
  71. if (npMCI->EntryCount++ > 0) {
  72. DPF(("re-entering requestor on same thread (SendMessage?)"));
  73. //DebugBreak();
  74. npMCI->EntryCount--;
  75. LeaveCriticalSection(&npMCI->CmdCritSec);
  76. return MCIERR_DEVICE_NOT_READY;
  77. //return 0;
  78. }
  79. if (!IsTask(npMCI->hTask)) {
  80. // worker thread has gone away (previous close ?)
  81. npMCI->EntryCount--;
  82. LeaveCriticalSection(&npMCI->CmdCritSec);
  83. DPF(("worker thread has gone away"));
  84. return MCIERR_DEVICE_NOT_READY;
  85. }
  86. // the response event should not be set yet!
  87. Assert(WaitForSingleObject(npMCI->hEventResponse, 0) == WAIT_TIMEOUT);
  88. // write the params
  89. npMCI->message = message;
  90. npMCI->dwParamFlags = dwFlags;
  91. npMCI->lParam = lParam;
  92. npMCI->dwReqCallback = dwCallback;
  93. npMCI->bDelayedComplete = bDelayedComplete;
  94. // we are the requesting task (we will be thrown out if this is
  95. // bDelayedComplete and there is an outstanding bDelayedComplete
  96. // from someone else)
  97. npMCI->hRequestor = GetCurrentTask();
  98. // signal that there is a request
  99. SetEvent(npMCI->hEventSend);
  100. // and wait for the response.
  101. //
  102. // in the play-wait case, this wait will complete once the play
  103. // has started. So at this point, no yields.
  104. // send-message processing needed for RealizePalette on worker thread
  105. #if 1
  106. // this could cause re-entry on this thread, and the critical section
  107. // will not prevent that. Hence the EntryCount checks.
  108. while (MsgWaitForMultipleObjects(1, &npMCI->hEventResponse, FALSE,
  109. INFINITE, QS_SENDMESSAGE) != WAIT_OBJECT_0) {
  110. DPF2(("rec'd sendmessage during wait\n"));
  111. // this peekmessage allows an inter-thread sendmessage to complete.
  112. // no message needs to be removed or processed- the range filtering is
  113. // essentially irrelevant for this.
  114. PeekMessage(&msg, NULL, WM_QUERYNEWPALETTE, WM_QUERYNEWPALETTE, PM_NOREMOVE);
  115. }
  116. #else
  117. WaitForSingleObject(npMCI->hEventResponse, INFINITE);
  118. #endif
  119. // pick up the return value
  120. dwRet = npMCI->dwReturn;
  121. DPF2(("Task returns %d\n", dwRet));
  122. // release the critsec now that request is all done
  123. if (--npMCI->EntryCount != 0) {
  124. DPF(("EntryCount not 0 on exit"));
  125. }
  126. LeaveCriticalSection(&npMCI->CmdCritSec);
  127. // if this is a two-phased operation such as play + wait
  128. // we must do the yielding wait here
  129. if (!dwRet && bDelayedComplete) {
  130. DWORD dw;
  131. UINT nYieldInterval = 300;
  132. #ifdef DEBUG
  133. nYieldInterval = mmGetProfileInt(szIni, TEXT("YieldInterval"), nYieldInterval);
  134. #endif
  135. do {
  136. if (mciDriverYield(npMCI->wDevID)) {
  137. // app says we must stop now. do this by issuing a stop
  138. // request and carry on waiting for the play+wait to finish
  139. mciaviTaskRequest(npMCI, AVI_STOP, 0, 0, 0, FALSE);
  140. }
  141. dw = WaitForSingleObject(npMCI->hEventAllDone, nYieldInterval);
  142. // this peekmessage allows an inter-thread sendmessage to complete.
  143. // no message needs to be removed or processed- the range filtering is
  144. // essentially irrelevant for this.
  145. PeekMessage(&msg, NULL, WM_QUERYNEWPALETTE, WM_QUERYNEWPALETTE, PM_NOREMOVE);
  146. } while(dw != WAIT_OBJECT_0);
  147. // until this is cleared, no other task can issue delayed requests
  148. npMCI->hWaiter = 0;
  149. }
  150. return dwRet;
  151. }
  152. /***************************************************************************
  153. *
  154. * @doc INTERNAL MCIAVI
  155. *
  156. * @api DWORD | DeviceOpen | Open an AVI file.
  157. *
  158. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  159. *
  160. * @parm LPSTR | lpName | file name.
  161. *
  162. * @parm DWORD | dwFlags | Open flags.
  163. *
  164. * @rdesc 0 means OK, otherwise mci error
  165. *
  166. ***************************************************************************/
  167. DWORD PASCAL DeviceOpen(NPMCIGRAPHIC npMCI, DWORD dwFlags)
  168. {
  169. DWORD dwRet;
  170. AssertUserThread(npMCI);
  171. // init the yield proc we will need for wow yielding
  172. if (IsNTWOW()) {
  173. if (pWOWYield16 == 0) {
  174. HMODULE hmod;
  175. hmod = GetModuleHandle(TEXT("wow32.dll"));
  176. if (hmod != NULL) {
  177. (FARPROC)pWOWYield16 = GetProcAddress(hmod, "WOWYield16");
  178. }
  179. }
  180. }
  181. // note that DeviceClose *will* be called anyway, even if DeviceOpen
  182. // fails, so make sure that allocations and events can be cleaned up
  183. // correctly.
  184. npMCI->uErrorMode = SetErrorMode(SEM_FAILCRITICALERRORS |
  185. SEM_NOOPENFILEERRORBOX);
  186. // must open the file on this app thread for ole reasons
  187. if (!mciaviOpenFile(npMCI)) {
  188. SetErrorMode(npMCI->uErrorMode);
  189. return npMCI->dwTaskError;
  190. }
  191. // OpenFileInit() on worker thread completes this open later.
  192. // create the communication channel to the worker thread, and
  193. // then start the thread
  194. // do this first, so that whenever we call DeviceClose we can always
  195. // safely do the Delete..
  196. InitializeCriticalSection(&npMCI->CmdCritSec);
  197. SetNTFlags(npMCI, NTF_DELETECMDCRITSEC); // Remember to do the delete
  198. npMCI->EntryCount = 0;
  199. // must be manual-reset to allow polling during play.
  200. npMCI->hEventSend = CreateEvent(NULL, TRUE, FALSE, NULL);
  201. npMCI->hEventResponse = CreateEvent(NULL, FALSE, FALSE, NULL);
  202. npMCI->hEventAllDone = CreateEvent(NULL, FALSE, FALSE, NULL);
  203. if (!npMCI->hEventSend || !npMCI->hEventResponse || !npMCI->hEventAllDone) {
  204. // cleanup of events actually allocated will be done in DeviceClose
  205. return MCIERR_OUT_OF_MEMORY;
  206. }
  207. // create the worker thread
  208. #if 0
  209. if (mmTaskCreate(mciaviTask, &npMCI->hThreadTermination,
  210. (DWORD)(UINT)npMCI) == 0)
  211. #else
  212. // We do not want the thread id, but CreateThread blows up if we pass
  213. // a null parameter. Hence overload dwRet...
  214. if (npMCI->hThreadTermination = CreateThread(NULL, 0,
  215. (LPTHREAD_START_ROUTINE)mciaviTask,
  216. (LPVOID)npMCI, 0, &dwRet))
  217. #endif
  218. {
  219. // check that the thread is actually created
  220. // either hEventResponse will be set, indicating that
  221. // the thread completed, or hThreadTermination will be
  222. // set, indicating that the thread aborted.
  223. #if 0
  224. if (WaitForMultipleObjects(2,
  225. &npMCI->hEventResponse, FALSE, INFINITE) == WAIT_OBJECT_0)
  226. {
  227. // task completed ok
  228. Assert(IsTask(npMCI->hTask));
  229. }
  230. #else
  231. // We must process messages during this phase... IF messages
  232. // must be processed by this thread before the AVI window can
  233. // be created. The most likely case is when a parent window
  234. // is passed and the parent window belongs to this (the UI)
  235. // thread. If no messages need to be processed (i.e. the AVI
  236. // window being created has no parent) then we could use the
  237. // simpler code above. DO THIS LATER.
  238. UINT n;
  239. while (WAIT_OBJECT_0+2 <= (n = MsgWaitForMultipleObjects(2,
  240. &npMCI->hEventResponse, FALSE, INFINITE, QS_SENDMESSAGE)))
  241. {
  242. MSG msg;
  243. if (n!=WAIT_OBJECT_0+2) {
  244. DPF0(("MsgWaitForMultipleObjects gave an unexpected return of %d\n", n));
  245. }
  246. PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  247. // PeekMessage with PM_NOREMOVE causes the inter thread
  248. // sent messages to be processed
  249. }
  250. dwRet = 0;
  251. if (n == WAIT_OBJECT_0) {
  252. // task completed ok
  253. Assert(IsTask(npMCI->hTask));
  254. }
  255. #endif
  256. else {
  257. // hThreadTermination has been signalled - abort
  258. CloseHandle(npMCI->hThreadTermination);
  259. npMCI->hThreadTermination = 0;
  260. dwRet = npMCI->dwTaskError;
  261. Assert(dwRet);
  262. }
  263. } else {
  264. npMCI->hTask = 0;
  265. dwRet = MCIERR_OUT_OF_MEMORY;
  266. npMCI->dwTaskError = GetLastError();
  267. }
  268. SetErrorMode(npMCI->uErrorMode);
  269. if (dwRet != 0) {
  270. // open failed - the necessary cleanup will be done in DeviceClose
  271. // which will be called after a bad return from DeviceOpen. In
  272. // fact graphic.c (which calls DeviceOpen) will call GraphicClose
  273. // when DeviceOpen fails. GraphicClose will then call DeviceClose
  274. // which will delete the cmdCritSec
  275. }
  276. return dwRet;
  277. }
  278. /***************************************************************************
  279. *
  280. * @doc INTERNAL MCIAVI
  281. *
  282. * @api DWORD | DeviceClose | Close an AVI file.
  283. *
  284. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  285. *
  286. * @rdesc 0 means OK, otherwise mci error
  287. *
  288. ***************************************************************************/
  289. DWORD PASCAL DeviceClose (NPMCIGRAPHIC npMCI)
  290. {
  291. DWORD dw = 0L;
  292. if (npMCI && IsTask(npMCI->hTask)) {
  293. AssertUserThread(npMCI);
  294. // tell the worker to close and wait for it to happen
  295. mciaviTaskRequest(npMCI, AVI_CLOSE, 0, 0, 0, FALSE);
  296. }
  297. // must wait for thread to exit
  298. if (npMCI->hThreadTermination != 0) {
  299. /*
  300. ** Wait for the thread to complete so the DLL doesn't get unloaded
  301. ** while it's still executing code in that thread
  302. */
  303. // we must allow sendmessage at this point since the winproc thread
  304. // will block until it can send messages to our thread, and we are
  305. // waiting for the winproc thread to exit.
  306. // do not do this between setting hEventSend and receiving hEventResponse
  307. // though or we could re-enter the Request block and get confused
  308. // about whether we have seen hEventResponse.
  309. // we also need to yield in case we are on a wow thread - any
  310. // interthread sendmessage to another wow thread will block until
  311. // we yield here allowing other wow threads to run
  312. DWORD dw;
  313. do {
  314. if (pWOWYield16) {
  315. pWOWYield16();
  316. }
  317. dw = MsgWaitForMultipleObjects(
  318. 1,
  319. &npMCI->hThreadTermination,
  320. FALSE,
  321. 100,
  322. QS_SENDMESSAGE);
  323. if (dw == WAIT_OBJECT_0 + 1) {
  324. MSG msg;
  325. DPF2(("rec'd sendmessage during shutdown wait\n"));
  326. // just a single peekmessage with NOREMOVE will
  327. // process the inter-thread send and not affect the queue
  328. PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  329. }
  330. } while (dw != WAIT_OBJECT_0);
  331. CloseHandle(npMCI->hThreadTermination);
  332. npMCI->hThreadTermination = 0;
  333. }
  334. if (TestNTFlags(npMCI, NTF_DELETECMDCRITSEC)) {
  335. DeleteCriticalSection(&npMCI->CmdCritSec);
  336. }
  337. if (npMCI->hEventSend) {
  338. CloseHandle(npMCI->hEventSend);
  339. }
  340. if (npMCI->hEventAllDone) {
  341. CloseHandle(npMCI->hEventAllDone);
  342. }
  343. if (npMCI->hEventResponse) {
  344. CloseHandle(npMCI->hEventResponse);
  345. }
  346. // uninitialize AVIFile and hence OLE - must be done on app thread
  347. #ifdef USEAVIFILE
  348. //
  349. // we must do this so COMPOBJ will shut down right.
  350. //
  351. FreeAVIFile(npMCI);
  352. #endif
  353. return dw;
  354. }
  355. /***************************************************************************
  356. *
  357. * @doc INTERNAL MCIAVI
  358. *
  359. * @api DWORD | DevicePlay | Play an AVI movie.
  360. *
  361. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  362. *
  363. * @parm DWORD | dwFlags | MCI flags from command.
  364. *
  365. * @parm LPMCI_DGV_PLAY_PARMS | lpPlay | Parameters for the play message.
  366. *
  367. * @rdesc 0 means OK, otherwise mci error
  368. *
  369. ***************************************************************************/
  370. DWORD PASCAL
  371. DevicePlay(
  372. NPMCIGRAPHIC npMCI,
  373. DWORD dwFlags,
  374. LPMCI_DGV_PLAY_PARMS lpPlay,
  375. LPARAM dwCallback
  376. )
  377. {
  378. BOOL bWait = FALSE;
  379. DWORD dwErr;
  380. if (!IsTask(npMCI->hTask))
  381. return MCIERR_DEVICE_NOT_READY;
  382. // all handled by the worker thread
  383. AssertUserThread(npMCI);
  384. if (dwFlags & MCI_WAIT) {
  385. bWait = TRUE;
  386. }
  387. dwErr = mciaviTaskRequest(npMCI,
  388. AVI_PLAY, dwFlags, (LPARAM) lpPlay, dwCallback, bWait);
  389. if (dwFlags & (MCI_MCIAVI_PLAY_FULLSCREEN | MCI_MCIAVI_PLAY_FULLBY2)) {
  390. MSG msg;
  391. DPF(("DevicePlay, removing stray messages\n"));
  392. /* Remove stray mouse and keyboard events after DispDib. */
  393. while (PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST,
  394. PM_NOYIELD | PM_REMOVE) ||
  395. PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST,
  396. PM_NOYIELD | PM_REMOVE))
  397. ;
  398. }
  399. return dwErr;
  400. }
  401. /***************************************************************************
  402. *
  403. * @doc INTERNAL MCIAVI
  404. *
  405. * @api DWORD | DeviceRealize | Updates the frame into the given DC
  406. *
  407. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block.
  408. *
  409. * @parm BOOL | fForceBackground | Realize as background palette?
  410. *
  411. * @rdesc 0 means OK, otherwise mci error
  412. *
  413. ***************************************************************************/
  414. DWORD PASCAL DeviceRealize(NPMCIGRAPHIC npMCI)
  415. {
  416. BOOL bWait = FALSE;
  417. if (!IsTask(npMCI->hTask))
  418. return MCIERR_DEVICE_NOT_READY;
  419. // all handled by the worker thread
  420. AssertUserThread(npMCI);
  421. return mciaviTaskRequest(npMCI, AVI_REALIZE, 0, 0, 0, FALSE);
  422. }
  423. /***************************************************************************
  424. *
  425. * @doc INTERNAL MCIAVI
  426. *
  427. * @api DWORD | DeviceStop | Stop an AVI movie.
  428. *
  429. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  430. *
  431. * @parm DWORD | dwFlags | Flags.
  432. *
  433. * @rdesc 0 means OK, otherwise mci error
  434. *
  435. ***************************************************************************/
  436. DWORD PASCAL DeviceStop(NPMCIGRAPHIC npMCI, DWORD dwFlags)
  437. {
  438. DWORD dw = 0L;
  439. /* Stop the record or playback if the task is currently playing */
  440. if (!IsTask(npMCI->hTask)) {
  441. DPF0(("DeviceStop called on a dead task, npMCI=%8x\n", npMCI));
  442. return MCIERR_DEVICE_NOT_READY;
  443. }
  444. AssertUserThread(npMCI);
  445. return mciaviTaskRequest(npMCI, AVI_STOP, 0, 0, 0, FALSE);
  446. }
  447. /***************************************************************************
  448. *
  449. * @doc INTERNAL MCIAVI
  450. *
  451. * @api DWORD | DeviceUpdate | Updates the frame into the given DC
  452. *
  453. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block.
  454. *
  455. * @rdesc 0 means OK, otherwise mci error
  456. *
  457. ***************************************************************************/
  458. DWORD PASCAL DeviceUpdate(
  459. NPMCIGRAPHIC npMCI,
  460. DWORD dwFlags,
  461. LPMCI_DGV_UPDATE_PARMS lpParms)
  462. {
  463. if (!IsTask(npMCI->hTask))
  464. return MCIERR_DEVICE_NOT_READY;
  465. AssertUserThread(npMCI);
  466. return mciaviTaskRequest(npMCI, AVI_UPDATE, dwFlags, (LPARAM) lpParms, 0, FALSE);
  467. }
  468. /***************************************************************************
  469. *
  470. * @doc INTERNAL MCIAVI
  471. *
  472. * @api DWORD | DevicePause | Pause an AVI movie.
  473. *
  474. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  475. *
  476. * @parm DWORD | dwFlags | Flags.
  477. *
  478. * @rdesc 0 means OK, otherwise mci error
  479. *
  480. ***************************************************************************/
  481. DWORD PASCAL DevicePause(NPMCIGRAPHIC npMCI, DWORD dwFlags, LPARAM dwCallback)
  482. {
  483. if (!IsTask(npMCI->hTask))
  484. return MCIERR_DEVICE_NOT_READY;
  485. AssertUserThread(npMCI);
  486. return mciaviTaskRequest(npMCI, AVI_PAUSE, dwFlags, 0, dwCallback,
  487. (dwFlags & MCI_WAIT));
  488. }
  489. /***************************************************************************
  490. *
  491. * @doc INTERNAL MCIAVI
  492. *
  493. * @api DWORD | DeviceCue | Cue an AVI movie for playing.
  494. *
  495. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  496. *
  497. * @parm LONG | lTo | Frame to seek to, if MCI_TO set in <p dwFlags>.
  498. *
  499. * @parm DWORD | dwFlags | MCI flags from command.
  500. *
  501. * @rdesc 0 means OK, otherwise mci error
  502. *
  503. ***************************************************************************/
  504. DWORD PASCAL DeviceCue(NPMCIGRAPHIC npMCI, LONG lTo, DWORD dwFlags, LPARAM dwCallback)
  505. {
  506. if (!IsTask(npMCI->hTask))
  507. return MCIERR_DEVICE_NOT_READY;
  508. AssertUserThread(npMCI);
  509. return mciaviTaskRequest(npMCI, AVI_CUE, dwFlags, lTo, dwCallback,
  510. (dwFlags & MCI_WAIT));
  511. }
  512. /***************************************************************************
  513. *
  514. * @doc INTERNAL MCIAVI
  515. *
  516. * @api DWORD | DeviceResume | Play an AVI movie.
  517. *
  518. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  519. *
  520. * @parm DWORD | dwFlags | MCI flags from command.
  521. *
  522. * @rdesc 0 means OK, otherwise mci error
  523. *
  524. ***************************************************************************/
  525. DWORD PASCAL DeviceResume(NPMCIGRAPHIC npMCI, DWORD dwFlags, LPARAM dwCallback)
  526. {
  527. DWORD dw = 0L;
  528. BOOL bWait = FALSE;
  529. if (!IsTask(npMCI->hTask))
  530. return MCIERR_DEVICE_NOT_READY;
  531. // all handled by the worker thread
  532. AssertUserThread(npMCI);
  533. if (dwFlags & MCI_WAIT) {
  534. bWait = TRUE;
  535. }
  536. return mciaviTaskRequest(npMCI,
  537. AVI_RESUME, dwFlags, 0, dwCallback, bWait);
  538. }
  539. /***************************************************************************
  540. *
  541. * @doc INTERNAL MCIAVI
  542. *
  543. * @api DWORD | DeviceSeek | Seek to a position in an AVI movie.
  544. *
  545. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  546. *
  547. * @parm LONG | lTo | Frame to seek to.
  548. *
  549. * @parm DWORD | dwFlags | MCI flags from command.
  550. *
  551. * @rdesc 0 means OK, otherwise mci error
  552. *
  553. ***************************************************************************/
  554. DWORD PASCAL DeviceSeek(NPMCIGRAPHIC npMCI, LONG lTo, DWORD dwFlags, LPARAM dwCallback)
  555. {
  556. if (!IsTask(npMCI->hTask))
  557. return MCIERR_DEVICE_NOT_READY;
  558. AssertUserThread(npMCI);
  559. return mciaviTaskRequest(npMCI, AVI_SEEK, dwFlags, lTo, dwCallback,
  560. (dwFlags & MCI_WAIT));
  561. }
  562. /***************************************************************************
  563. *
  564. * @doc INTERNAL MCIAVI
  565. *
  566. * @api DWORD | DeviceSetActive | is the movie active?
  567. *
  568. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block.
  569. *
  570. * @rdesc 0 means OK, otherwise mci error
  571. *
  572. ***************************************************************************/
  573. DWORD PASCAL DeviceSetActive(NPMCIGRAPHIC npMCI, BOOL fActive)
  574. {
  575. // We cannot call AssertUserThread(npMCI);
  576. // This routine is called on the winproc thread, as well as the user
  577. // thread.
  578. if (fActive)
  579. // We must explicitly request a unicode string. %s will not
  580. // work as dprintf uses wvsprintfA
  581. DPF(("**** '%ls' is active.\n", (LPTSTR)npMCI->szFilename));
  582. return 0;
  583. }
  584. /***************************************************************************
  585. *
  586. * @doc INTERNAL MCIAVI
  587. *
  588. * @api DWORD | DeviceStatus | Returns the current status
  589. *
  590. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block.
  591. *
  592. * @rdesc Returns value for MCI's return value
  593. *
  594. ***************************************************************************/
  595. UINT PASCAL DeviceMode(NPMCIGRAPHIC npMCI)
  596. {
  597. if (!IsTask(npMCI->hTask)) {
  598. return MCI_MODE_NOT_READY;
  599. }
  600. // there is no point in synchronizing with the worker thread for
  601. // this since the task state will be transient anyway.
  602. // just grab a snapshot and return that.
  603. AssertUserThread(npMCI);
  604. switch (npMCI->wTaskState) {
  605. case TASKIDLE:
  606. return MCI_MODE_STOP;
  607. case TASKCUEING:
  608. // problem: some apps (notably mplayer) will be surprised to
  609. // get MCI_MODE_SEEK immediately after issuing a PLAY command.
  610. // on win-16 the yielding model meant that the app would not
  611. // normally get control back until after the play proper had
  612. // started and so would never see the cueing state.
  613. // to avoid this confusion (and the bugs that arise from it), we
  614. // never return MCI_MODE_SEEK: we report this mode as playing.
  615. // this is often what would be seen on win-16 anyway (even in the
  616. // case of a PLAY command ?).
  617. // Except... for apps that really do seek this can fool them into
  618. // thinking that they are playing. So... we modify the algorithm
  619. // to return MODE_SEEK if lTo==lFrom (why is obvious) OR if
  620. // lRealStart==lTo. This latter is because if you seek in mplayer
  621. // by dragging the thumb the image is only updated every key frame.
  622. // lRealStart is updated to this key frame while seeking
  623. //DPF0(("F: %8x, To=%d, From=%d lReal=%d lDrawn=%d Current=%d\n",
  624. // npMCI->dwFlags, npMCI->lTo, npMCI->lFrom, npMCI->lRealStart, npMCI->lFrameDrawn, npMCI->lCurrentFrame));
  625. if ((npMCI->lTo == npMCI->lFrom)
  626. || (npMCI->lTo == npMCI->lRealStart)) {
  627. return(MCI_MODE_SEEK);
  628. }
  629. return MCI_MODE_PLAY;
  630. case TASKSTARTING: // ready? of course we're ready
  631. case TASKPLAYING:
  632. return MCI_MODE_PLAY;
  633. case TASKPAUSED:
  634. return MCI_MODE_PAUSE;
  635. default:
  636. DPF(("Unexpected state %d in DeviceMode()\n", npMCI->wTaskState));
  637. // fall through to the known states
  638. //case TASKBEINGCREATED:
  639. //case TASKINIT:
  640. case TASKCLOSE:
  641. //case TASKREADINDEX:
  642. return MCI_MODE_NOT_READY;
  643. }
  644. }
  645. /***************************************************************************
  646. *
  647. * @doc INTERNAL MCIAVI
  648. *
  649. * @api DWORD | DevicePosition | Returns the current frame
  650. *
  651. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data block.
  652. *
  653. * @parm LPLONG | lpl | returns current frame
  654. *
  655. * @rdesc 0 means OK, otherwise mci error
  656. *
  657. ***************************************************************************/
  658. DWORD PASCAL DevicePosition(NPMCIGRAPHIC npMCI, LPLONG lpl)
  659. {
  660. // read a snapshot of the current state without
  661. // synchronising with the worker thread!
  662. AssertUserThread(npMCI);
  663. return InternalGetPosition(npMCI, lpl);
  664. }
  665. /***************************************************************************
  666. *
  667. * @doc INTERNAL MCIAVI
  668. *
  669. * @api DWORD | DeviceSetWindow | Set window for display
  670. *
  671. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  672. *
  673. * @parm HWND | hwnd | Window to display into.
  674. *
  675. * @rdesc 0 means OK, otherwise mci error
  676. *
  677. * @comm Should this only take effect at time of next play?
  678. *
  679. ***************************************************************************/
  680. DWORD PASCAL DeviceSetWindow(NPMCIGRAPHIC npMCI, HWND hwnd)
  681. {
  682. if (!IsTask(npMCI->hTask))
  683. return MCIERR_DEVICE_NOT_READY;
  684. AssertUserThread(npMCI);
  685. return mciaviTaskRequest(npMCI, AVI_WINDOW, 0, (LPARAM) hwnd, 0, FALSE);
  686. }
  687. /***************************************************************************
  688. *
  689. * @doc INTERNAL MCIAVI
  690. *
  691. * @api DWORD | DeviceSpeed | Adjust the playback speed of an AVI movie.
  692. *
  693. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  694. *
  695. * @parm DWORD | dwNewSpeed | New speed, where 1000 is 'normal' speed.
  696. *
  697. * @rdesc 0 means OK, otherwise mci error
  698. *
  699. * @comm If we are currently playing, we stop the device, set our flag,
  700. * and start playing again where we left off. If we were paused,
  701. * we end up stopped. Is this bad?
  702. * ** It is if you paused to change the speed then try and resume **
  703. *
  704. ***************************************************************************/
  705. DWORD PASCAL DeviceSetSpeed(NPMCIGRAPHIC npMCI, DWORD dwNewSpeed)
  706. {
  707. if (!IsTask(npMCI->hTask))
  708. return MCIERR_DEVICE_NOT_READY;
  709. AssertUserThread(npMCI);
  710. return mciaviTaskRequest(npMCI, AVI_SETSPEED, 0, (LPARAM) dwNewSpeed, 0, FALSE);
  711. }
  712. /***************************************************************************
  713. *
  714. * @doc INTERNAL MCIAVI
  715. *
  716. * @api DWORD | DeviceMute | Turn AVI sound on/off.
  717. *
  718. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  719. *
  720. * @parm BOOL | fMute | TRUE If sound should be turned off, FALSE
  721. * if sound should stay on.
  722. *
  723. * @rdesc 0 means OK, otherwise mci error
  724. *
  725. * @comm If we are currently playing, we stop the device, set our flag,
  726. * and start playing again where we left off. If we were paused,
  727. * we end up stopped. Is this bad?
  728. *
  729. ***************************************************************************/
  730. DWORD PASCAL DeviceMute(NPMCIGRAPHIC npMCI, BOOL fMute)
  731. {
  732. if (!IsTask(npMCI->hTask))
  733. return MCIERR_DEVICE_NOT_READY;
  734. AssertUserThread(npMCI);
  735. return mciaviTaskRequest(npMCI, AVI_MUTE, 0, (LPARAM) fMute, 0, FALSE);
  736. }
  737. /***************************************************************************
  738. *
  739. * @doc INTERNAL MCIAVI
  740. *
  741. * @api DWORD | DeviceSetVolume | Set AVI volume.
  742. *
  743. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  744. *
  745. * @parm DWORD | dwVolume | ranges from 0 to 1000.
  746. *
  747. * @rdesc 0 means OK, otherwise mci error
  748. *
  749. * @comm If we are currently playing, we try to change the volume of the
  750. * wave out device.
  751. *
  752. ***************************************************************************/
  753. DWORD PASCAL DeviceSetVolume(NPMCIGRAPHIC npMCI, DWORD dwVolume)
  754. {
  755. DWORD dw = 0L;
  756. // switch audio off completely if setting volume level to 0, and back on
  757. // again if not.
  758. dw = DeviceMute(npMCI, (dwVolume == 0));
  759. if (dw != 0) {
  760. return dw;
  761. }
  762. AssertUserThread(npMCI);
  763. return mciaviTaskRequest(npMCI, AVI_SETVOLUME, 0, (LPARAM) dwVolume, 0, FALSE);
  764. }
  765. /***************************************************************************
  766. *
  767. * @doc INTERNAL MCIAVI
  768. *
  769. * @api DWORD | DeviceGetVolume | Check the wave output device's current
  770. * volume.
  771. *
  772. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  773. *
  774. * @rdesc 0 means OK, otherwise mci error
  775. *
  776. * @comm The volume is left in npMCI->dwVolume
  777. *
  778. * Issue: On devices with global volume control, like an SBPro, how should
  779. * things work?
  780. *
  781. ***************************************************************************/
  782. DWORD PASCAL DeviceGetVolume(NPMCIGRAPHIC npMCI)
  783. {
  784. // all reference to the hWave *must* be done on the worker thread
  785. AssertUserThread(npMCI);
  786. return mciaviTaskRequest(npMCI, AVI_GETVOLUME, 0, 0, 0, FALSE);
  787. }
  788. /***************************************************************************
  789. *
  790. * @doc INTERNAL MCIAVI
  791. *
  792. * @api DWORD | DeviceSetAudioStream | Choose which audio stream to use.
  793. *
  794. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  795. *
  796. * @parm WORD | wStream | ranges from 1 to the number of streams.
  797. *
  798. * @rdesc 0 means OK, otherwise mci error
  799. *
  800. ***************************************************************************/
  801. DWORD PASCAL DeviceSetAudioStream(NPMCIGRAPHIC npMCI, UINT wAudioStream)
  802. {
  803. AssertUserThread(npMCI);
  804. return mciaviTaskRequest(npMCI, AVI_AUDIOSTREAM, wAudioStream, 0, 0, FALSE);
  805. }
  806. /***************************************************************************
  807. *
  808. * @doc INTERNAL MCIAVI
  809. *
  810. * @api DWORD | DeviceSetVideoStream | Choose which video stream is the
  811. * "default". Also can enable/disable a stream. this works for both
  812. * video and "other" streams.
  813. *
  814. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  815. *
  816. * @parm WORD | wStream | ranges from 1 to the number of streams.
  817. *
  818. * @rdesc 0 means OK, otherwise mci error
  819. *
  820. ***************************************************************************/
  821. DWORD PASCAL DeviceSetVideoStream(NPMCIGRAPHIC npMCI, UINT uStream, BOOL fOn)
  822. {
  823. AssertUserThread(npMCI);
  824. return mciaviTaskRequest(npMCI, AVI_AUDIOSTREAM, uStream, (BOOL)fOn, 0, FALSE);
  825. }
  826. /***************************************************************************
  827. *
  828. * @doc INTERNAL MCIAVI
  829. *
  830. * @api DWORD | DevicePut | Change source or destination rectangle
  831. *
  832. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  833. *
  834. * @parm LPRECT | lprc | Pointer to new rectangle to use.
  835. *
  836. * @parm DWORD | dwFlags | Flags: will be either MCI_DGV_PUT_DESTINATION
  837. * or MCI_DGV_PUT_SOURCE.
  838. *
  839. * @rdesc 0 means OK, otherwise mci error
  840. *
  841. * @comm
  842. * If we end up using a custom stretch buffer, it would go here.
  843. *
  844. ***************************************************************************/
  845. DWORD FAR PASCAL DevicePut(NPMCIGRAPHIC npMCI, LPRECT lprc, DWORD dwFlags)
  846. {
  847. AssertUserThread(npMCI);
  848. return mciaviTaskRequest(npMCI, AVI_PUT, dwFlags, (LPARAM)lprc, 0, FALSE);
  849. }
  850. /***************************************************************************
  851. *
  852. * @doc INTERNAL MCIAVI
  853. *
  854. * @api DWORD | DeviceSetPalette | Changes the override palette.
  855. *
  856. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  857. *
  858. * @parm HPALETTE | hpal | New palette to use.
  859. *
  860. * @rdesc 0 means OK, otherwise mci error
  861. *
  862. ***************************************************************************/
  863. DWORD FAR PASCAL DeviceSetPalette(NPMCIGRAPHIC npMCI, HPALETTE hpal)
  864. {
  865. AssertUserThread(npMCI);
  866. return mciaviTaskRequest(npMCI, AVI_PALETTE, 0, (LPARAM) hpal, 0, FALSE);
  867. }
  868. /***************************************************************************
  869. *
  870. * @doc INTERNAL MCIAVI
  871. *
  872. * @api DWORD | DeviceSetPaletteColor | Changes the a single color
  873. * in the movie's palette.
  874. *
  875. * @parm NPMCIGRAPHIC | npMCI | Pointer to instance data.
  876. *
  877. * @parm DWORD | index | color index to change
  878. *
  879. * @parm DWORD | color | color value to use
  880. *
  881. * @rdesc 0 means OK, otherwise mci error
  882. *
  883. ***************************************************************************/
  884. DWORD FAR PASCAL DeviceSetPaletteColor(NPMCIGRAPHIC npMCI, DWORD index, DWORD color)
  885. {
  886. AssertUserThread(npMCI);
  887. return mciaviTaskRequest(npMCI, AVI_PALETTECOLOR, color, (LPARAM) index, 0, FALSE);
  888. }
  889. //
  890. // user-thread version of ResetDestRect - note that there is a similar
  891. // winproc-thread-only version in window.c
  892. //
  893. void FAR PASCAL ResetDestRect(NPMCIGRAPHIC npMCI, BOOL fUseDefaultSizing)
  894. {
  895. RECT rc;
  896. /* WM_SIZE messages (on NT at least) are sometimes sent
  897. * during CreateWindow processing (eg if the initial window size
  898. * is not CW_DEFAULT). Some fields in npMCI are only filled in
  899. * after CreateWindow has returned. So there is a danger that at this
  900. * point some fields are not valid.
  901. */
  902. if (npMCI->hwndPlayback &&
  903. npMCI->hwndPlayback == npMCI->hwndDefault &&
  904. (npMCI->dwOptionFlags & MCIAVIO_STRETCHTOWINDOW)) {
  905. GetClientRect(npMCI->hwndPlayback, &rc);
  906. }
  907. // Only allow ZOOMBY2 and fixed % defaults for our default playback window
  908. else if ((npMCI->streams > 0) && (npMCI->hwndPlayback == npMCI->hwndDefault)) {
  909. rc = npMCI->rcMovie;
  910. if (fUseDefaultSizing)
  911. AlterRectUsingDefaults(npMCI, &rc);
  912. }
  913. else {
  914. return;
  915. }
  916. if (!IsRectEmpty(&rc)) {
  917. DevicePut(npMCI, &rc, MCI_DGV_PUT_DESTINATION);
  918. }
  919. }