Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

743 lines
16 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997.
  5. //
  6. // File: M E D I A . C P P
  7. //
  8. // Contents: Control handlers and implementation for the media
  9. // player device
  10. //
  11. // Notes:
  12. //
  13. // Author: danielwe 6 Nov 1999
  14. //
  15. //----------------------------------------------------------------------------
  16. #include "pch.h"
  17. #pragma hdrstop
  18. #include "media.h"
  19. #include "ncbase.h"
  20. #include "updiagp.h"
  21. #include "ncinet.h"
  22. static const LPSTR c_rgszStates[] =
  23. {
  24. "Turned Off",
  25. "Uninitialized",
  26. "Stopped",
  27. "Paused",
  28. "Playing",
  29. };
  30. // Current multimedia variables
  31. Media media = {TurnedOff, NULL, 0, FALSE};
  32. DWORD Val_VolumeUp(DWORD cArgs, ARG *rgArgs)
  33. {
  34. return 1;
  35. }
  36. DWORD Do_VolumeUp(DWORD cArgs, ARG *rgArgs)
  37. {
  38. OnVolumeUpDown(TRUE);
  39. return 1;
  40. }
  41. DWORD Val_VolumeDown(DWORD cArgs, ARG *rgArgs)
  42. {
  43. return 1;
  44. }
  45. DWORD Do_VolumeDown(DWORD cArgs, ARG *rgArgs)
  46. {
  47. OnVolumeUpDown(FALSE);
  48. return 1;
  49. }
  50. DWORD Val_SetVolume(DWORD cArgs, ARG *rgArgs)
  51. {
  52. if (cArgs == 1)
  53. {
  54. return 1;
  55. }
  56. return 0;
  57. }
  58. DWORD Do_SetVolume(DWORD cArgs, ARG *rgArgs)
  59. {
  60. OnSetVolume(_tcstoul(rgArgs[0].szValue, NULL, 10));
  61. return 1;
  62. }
  63. DWORD Val_Mute(DWORD cArgs, ARG *rgArgs)
  64. {
  65. return 1;
  66. }
  67. DWORD Do_Mute(DWORD cArgs, ARG *rgArgs)
  68. {
  69. OnMediaMute();
  70. return 1;
  71. }
  72. DWORD Val_Power(DWORD cArgs, ARG *rgArgs)
  73. {
  74. return 1;
  75. }
  76. DWORD Do_Power(DWORD cArgs, ARG *rgArgs)
  77. {
  78. if (media.state == TurnedOff)
  79. {
  80. InitMedia();
  81. }
  82. else
  83. {
  84. DeleteContents();
  85. }
  86. return 1;
  87. }
  88. DWORD Val_LoadFile(DWORD cArgs, ARG *rgArgs)
  89. {
  90. if (cArgs != 1)
  91. {
  92. return 0;
  93. }
  94. else
  95. {
  96. // Check if the file exists
  97. if (GetFileAttributes(rgArgs[0].szValue) == -1)
  98. {
  99. return 0;
  100. }
  101. }
  102. return 1;
  103. }
  104. DWORD Do_LoadFile(DWORD cArgs, ARG *rgArgs)
  105. {
  106. OpenMediaFile(rgArgs[0].szValue);
  107. return 1;
  108. }
  109. DWORD Val_Play(DWORD cArgs, ARG *rgArgs)
  110. {
  111. return 1;
  112. }
  113. DWORD Do_Play(DWORD cArgs, ARG *rgArgs)
  114. {
  115. OnMediaPlay();
  116. return 1;
  117. }
  118. DWORD Val_Stop(DWORD cArgs, ARG *rgArgs)
  119. {
  120. return 1;
  121. }
  122. DWORD Do_Stop(DWORD cArgs, ARG *rgArgs)
  123. {
  124. OnMediaStop();
  125. return 1;
  126. }
  127. DWORD Val_Pause(DWORD cArgs, ARG *rgArgs)
  128. {
  129. return 1;
  130. }
  131. DWORD Do_Pause(DWORD cArgs, ARG *rgArgs)
  132. {
  133. OnMediaPause();
  134. return 1;
  135. }
  136. DWORD Val_SetPos(DWORD cArgs, ARG *rgArgs)
  137. {
  138. return 0;
  139. }
  140. DWORD Do_SetPos(DWORD cArgs, ARG *rgArgs)
  141. {
  142. return 0;
  143. }
  144. DWORD Val_SetTime(DWORD cArgs, ARG *rgArgs)
  145. {
  146. return 1;
  147. }
  148. DWORD Do_SetTime(DWORD cArgs, ARG *rgArgs)
  149. {
  150. return 1;
  151. }
  152. DWORD Val_Test(DWORD cArgs, ARG *rgArgs)
  153. {
  154. TraceTag(ttidUpdiag, "Testing 1 2 3...");
  155. return 1;
  156. }
  157. DWORD Do_Test(DWORD cArgs, ARG *rgArgs)
  158. {
  159. TraceTag(ttidUpdiag, "Doing a test!");
  160. return 1;
  161. }
  162. //
  163. // CanPlay
  164. //
  165. // Return true if we can go to a playing state from our current state
  166. //
  167. BOOL CanPlay()
  168. {
  169. return(media.state == Stopped || media.state == Paused);
  170. }
  171. BOOL CanSetVolume()
  172. {
  173. return(media.state == Stopped ||
  174. media.state == Paused ||
  175. media.state == Playing);
  176. }
  177. //
  178. // CanStop
  179. //
  180. // Return true if we can go to a stopped state from our current state
  181. //
  182. BOOL CanStop()
  183. {
  184. return(media.state == Playing || media.state == Paused);
  185. }
  186. //
  187. // CanPause
  188. //
  189. // Return true if we can go to a paused state from our current state
  190. //
  191. BOOL CanPause()
  192. {
  193. return(media.state == Playing || media.state == Stopped);
  194. }
  195. //
  196. // IsInitialized
  197. //
  198. // Return true if we have loaded and initialized a multimedia file
  199. //
  200. BOOL IsInitialized()
  201. {
  202. return(media.state != Uninitialized);
  203. }
  204. VOID SubmitMediaEvent(LPCSTR szEventSource, LPCSTR szProp, LPCSTR szValue)
  205. {
  206. UPNPSVC * psvc;
  207. CHAR szState[256];
  208. CHAR szUri[INTERNET_MAX_URL_LENGTH];
  209. LPCSTR szEvtSource = szEventSource ? szEventSource : g_pdata->szEventSource;
  210. LPTSTR pszEvtSource;
  211. UPNP_PROPERTY rgProps[] =
  212. {
  213. {(LPSTR)szProp, 0, (LPSTR)szValue}
  214. };
  215. pszEvtSource = TszFromSz(szEvtSource);
  216. if (pszEvtSource)
  217. {
  218. psvc = PSvcFromId(pszEvtSource);
  219. if (psvc)
  220. {
  221. LPSTR pszEvtUrl;
  222. pszEvtUrl = SzFromTsz(psvc->szEvtUrl);
  223. if (pszEvtUrl)
  224. {
  225. HRESULT hr;
  226. hr = HrGetRequestUriA(pszEvtUrl, INTERNET_MAX_URL_LENGTH, szUri);
  227. if (SUCCEEDED(hr))
  228. {
  229. if (SubmitUpnpPropertyEvent(szUri, 0, 1, rgProps))
  230. {
  231. TraceTag(ttidUpdiag, "Successfully submitted event for %s.",
  232. szUri);
  233. }
  234. else
  235. {
  236. TraceTag(ttidUpdiag, "Did not submit event for %s! Error %d.",
  237. g_pdata->szEventSource, GetLastError());
  238. }
  239. }
  240. delete [] pszEvtUrl;
  241. }
  242. else
  243. {
  244. TraceTag(ttidUpdiag, "SubmitMediaEvent: SzFromTsz");
  245. }
  246. }
  247. delete [] pszEvtSource;
  248. }
  249. else
  250. {
  251. TraceTag(ttidUpdiag, "SubmitMediaEvent: TszFromSz");
  252. }
  253. }
  254. //
  255. // ChangeStateTo
  256. //
  257. VOID ChangeStateTo(State newState)
  258. {
  259. media.state = newState;
  260. SubmitMediaEvent("xport", "State", c_rgszStates[newState]);
  261. TraceTag(ttidMedia, "Changed state to %d.", newState);
  262. }
  263. DWORD WINAPI TimeThreadProc(LPVOID lpvThreadParam)
  264. {
  265. while (TRUE)
  266. {
  267. if (WaitForSingleObject(g_hEventCleanup, 1000) == WAIT_OBJECT_0)
  268. {
  269. break;
  270. }
  271. else
  272. {
  273. SYSTEMTIME st;
  274. FILETIME ft;
  275. CHAR szLocalTime[255];
  276. CHAR szLocalDate[255];
  277. CHAR szBoth[513];
  278. // Stamp the current time into the subscription struct
  279. //
  280. GetSystemTimeAsFileTime(&ft);
  281. // Convert time to local time zone
  282. FileTimeToLocalFileTime(&ft, &ft);
  283. FileTimeToSystemTime(&ft, &st);
  284. GetTimeFormatA(LOCALE_USER_DEFAULT, LOCALE_USE_CP_ACP, &st, NULL,
  285. szLocalTime, 255);
  286. GetDateFormatA(LOCALE_USER_DEFAULT,
  287. LOCALE_USE_CP_ACP | DATE_SHORTDATE, &st, NULL,
  288. szLocalDate, 255);
  289. wsprintfA(szBoth, "%s %s", szLocalDate, szLocalTime);
  290. SubmitMediaEvent("clock", "DateTime", szBoth);
  291. }
  292. }
  293. return 0;
  294. }
  295. //
  296. // InitMedia
  297. //
  298. // Initialization
  299. //
  300. BOOL InitMedia()
  301. {
  302. DWORD dwTid;
  303. g_hThreadTime = CreateThread(NULL, 0, TimeThreadProc, NULL, 0, &dwTid);
  304. ChangeStateTo( Uninitialized );
  305. media.pGraph = NULL;
  306. TraceTag(ttidMedia, "Initialized...");
  307. return TRUE;
  308. }
  309. //
  310. // CreateFilterGraph
  311. //
  312. BOOL CreateFilterGraph()
  313. {
  314. IMediaEvent *pME;
  315. HRESULT hr;
  316. ASSERT(media.pGraph == NULL);
  317. hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER,
  318. IID_IGraphBuilder, (LPVOID *) &media.pGraph);
  319. TraceError("CreateFilterGraph: CoCreateInstance()", hr);
  320. if (FAILED(hr))
  321. {
  322. media.pGraph = NULL;
  323. return FALSE;
  324. }
  325. TraceTag(ttidMedia, "Created filter graph");
  326. return TRUE;
  327. } // CreateFilterGraph
  328. // Destruction
  329. //
  330. // DeleteContents
  331. //
  332. VOID DeleteContents()
  333. {
  334. ReleaseObj(media.pGraph);
  335. media.pGraph = NULL;
  336. ChangeStateTo(Uninitialized);
  337. TraceTag(ttidMedia, "Deleted contents.");
  338. }
  339. //
  340. // RenderFile
  341. //
  342. BOOL RenderFile(LPCTSTR szFileName)
  343. {
  344. HRESULT hr;
  345. WCHAR wPath[MAX_PATH];
  346. CHAR aPath[MAX_PATH];
  347. DeleteContents();
  348. TszToWszBuf(wPath, szFileName, MAX_PATH);
  349. if (!CreateFilterGraph())
  350. {
  351. TraceTag(ttidMedia, "Couldn't render");
  352. return FALSE;
  353. }
  354. TszToWszBuf(wPath, szFileName, MAX_PATH);
  355. hr = media.pGraph->RenderFile(wPath, NULL);
  356. TraceError("RenderFile: RenderFile()", hr);
  357. if (FAILED(hr))
  358. {
  359. return FALSE;
  360. }
  361. TszToSzBuf(aPath, szFileName, MAX_PATH);
  362. SubmitMediaEvent("app", "File", aPath);
  363. TraceTag(ttidMedia, "Rendered file %s.", szFileName);
  364. return TRUE;
  365. } // RenderFile
  366. //
  367. // OpenMediaFile
  368. //
  369. // File..Open has been selected
  370. //
  371. VOID OpenMediaFile(LPCTSTR szFile)
  372. {
  373. if ( szFile != NULL && RenderFile(szFile))
  374. {
  375. TraceTag(ttidMedia, "Opened file %s." , szFile);
  376. ChangeStateTo(Stopped);
  377. }
  378. } // OpenMediaFile
  379. VOID OnSetVolume(DWORD dwVol)
  380. {
  381. if (CanSetVolume())
  382. {
  383. HRESULT hr;
  384. IBasicAudio * pAudio;
  385. hr = media.pGraph->QueryInterface(IID_IBasicAudio, (LPVOID *) &pAudio);
  386. TraceError("OnSetVolume: QueryInterface()", hr);
  387. if (SUCCEEDED(hr))
  388. {
  389. LONG lVol;
  390. CHAR szVol[32];
  391. dwVol = max(0, dwVol);
  392. dwVol = min(7, dwVol);
  393. lVol = (7 - dwVol) * -300;
  394. TraceTag(ttidMedia, "Putting new volume of %d.", lVol);
  395. pAudio->put_Volume(lVol);
  396. wsprintfA(szVol, "%ld", lVol);
  397. SubmitMediaEvent("app", "Volume", szVol);
  398. ReleaseObj(pAudio);
  399. }
  400. }
  401. }
  402. VOID OnVolumeUpDown(BOOL fUp)
  403. {
  404. if (CanSetVolume())
  405. {
  406. HRESULT hr;
  407. IBasicAudio * pAudio;
  408. hr = media.pGraph->QueryInterface(IID_IBasicAudio, (LPVOID *) &pAudio);
  409. TraceError("OnVolumeUp: QueryInterface()", hr);
  410. if (SUCCEEDED(hr))
  411. {
  412. LONG lVol;
  413. const LONG lStep = 300;
  414. pAudio->get_Volume(&lVol);
  415. TraceTag(ttidMedia, "Current volume is %d.", lVol);
  416. if (fUp)
  417. {
  418. lVol += lStep;
  419. lVol = min(lVol, 0);
  420. }
  421. else
  422. {
  423. lVol -= lStep;
  424. lVol = max(lVol, -10000);
  425. }
  426. if (media.fIsMuted)
  427. {
  428. media.lOldVol = lVol;
  429. TraceTag(ttidMedia, "MUTED: Saving new volume of %d.", lVol);
  430. }
  431. else
  432. {
  433. CHAR szVol[32];
  434. TraceTag(ttidMedia, "Putting new volume of %d.", lVol);
  435. pAudio->put_Volume(lVol);
  436. wsprintfA(szVol, "%ld", lVol);
  437. SubmitMediaEvent("app", "Volume", szVol);
  438. }
  439. ReleaseObj(pAudio);
  440. }
  441. }
  442. }
  443. VOID OnMediaMute()
  444. {
  445. HRESULT hr;
  446. IBasicAudio * pAudio;
  447. hr = media.pGraph->QueryInterface(IID_IBasicAudio, (LPVOID *) &pAudio);
  448. TraceError("OnVolumeUp: QueryInterface()", hr);
  449. if (SUCCEEDED(hr))
  450. {
  451. if (media.fIsMuted)
  452. {
  453. TraceTag(ttidMedia, "Unmuting... Restoring volume of %d.",
  454. media.lOldVol);
  455. pAudio->put_Volume(media.lOldVol);
  456. media.fIsMuted = FALSE;
  457. }
  458. else
  459. {
  460. pAudio->get_Volume(&media.lOldVol);
  461. TraceTag(ttidMedia, "Muting volume. Was %d.", media.lOldVol);
  462. pAudio->put_Volume(-10000);
  463. media.fIsMuted = TRUE;
  464. }
  465. ReleaseObj(pAudio);
  466. }
  467. }
  468. //
  469. // OnMediaPlay
  470. //
  471. // There are two possible UI strategies for an application: you either play
  472. // from the start each time you stop, or you play from the current position,
  473. // in which case you have to explicitly rewind at the end. If you want the
  474. // play from current and rewind at end, enable this code, if you want the
  475. // other option, then enable FROM_START in both OnMediaStop and OnAbortStop.
  476. #undef REWIND
  477. #define FROM_START
  478. VOID OnMediaPlay()
  479. {
  480. if (CanPlay())
  481. {
  482. HRESULT hr;
  483. IMediaControl *pMC;
  484. // Obtain the interface to our filter graph
  485. hr = media.pGraph->QueryInterface(IID_IMediaControl, (LPVOID *) &pMC);
  486. TraceError("OnMediaPlay: QueryInterface()", hr);
  487. if (SUCCEEDED(hr))
  488. {
  489. #ifdef REWIND
  490. IMediaPosition * pMP;
  491. hr = media.pGraph->lpVtbl->QueryInterface(media.pGraph,
  492. &IID_IMediaPosition,
  493. (VOID**) &pMP);
  494. if (SUCCEEDED(hr))
  495. {
  496. // start from last position, but rewind if near the end
  497. REFTIME tCurrent, tLength;
  498. hr = pMP->lpVtbl->get_Duration(pMP, &tLength);
  499. if (SUCCEEDED(hr))
  500. {
  501. hr = pMP->lpVtbl->get_CurrentPosition(pMP, &tCurrent);
  502. if (SUCCEEDED(hr))
  503. {
  504. // within 1sec of end? (or past end?)
  505. if ((tLength - tCurrent) < 1)
  506. {
  507. pMP->lpVtbl->put_CurrentPosition(pMP, 0);
  508. }
  509. }
  510. }
  511. pMP->lpVtbl->Release(pMP);
  512. }
  513. #endif
  514. // Ask the filter graph to play and release the interface
  515. hr = pMC->Run();
  516. TraceError("OnMediaPlay: Run()", hr);
  517. ReleaseObj(pMC);
  518. if (SUCCEEDED(hr))
  519. {
  520. ChangeStateTo(Playing);
  521. TraceTag(ttidMedia, "Playing...");
  522. return;
  523. }
  524. }
  525. TraceTag(ttidMedia, "Can't play!");
  526. }
  527. else
  528. {
  529. TraceTag(ttidMedia, "Not valid state for playing right now: %d.",
  530. media.state);
  531. }
  532. } // OnMediaPlay
  533. //
  534. // OnMediaPause
  535. //
  536. VOID OnMediaPause()
  537. {
  538. if (CanPause())
  539. {
  540. HRESULT hr;
  541. IMediaControl *pMC;
  542. // Obtain the interface to our filter graph
  543. hr = media.pGraph->QueryInterface(IID_IMediaControl, (LPVOID *) &pMC);
  544. TraceError("OnMediaPause: QueryInterface()", hr);
  545. if (SUCCEEDED(hr))
  546. {
  547. // Ask the filter graph to pause and release the interface
  548. hr = pMC->Pause();
  549. TraceError("OnMediaPause: Pause()", hr);
  550. ReleaseObj(pMC);
  551. if (SUCCEEDED(hr))
  552. {
  553. ChangeStateTo(Paused);
  554. return;
  555. }
  556. }
  557. }
  558. else
  559. {
  560. TraceTag(ttidMedia, "Not valid state for pausing right now: %d.",
  561. media.state);
  562. }
  563. } // OnMediaPause
  564. //
  565. // OnMediaStop
  566. //
  567. // There are two different ways to stop a graph. We can stop and then when we
  568. // are later paused or run continue from the same position. Alternatively the
  569. // graph can be set back to the start of the media when it is stopped to have
  570. // a more CDPLAYER style interface. These are both offered here conditionally
  571. // compiled using the FROM_START definition. The main difference is that in
  572. // the latter case we put the current position to zero while we change states
  573. //
  574. VOID OnMediaStop()
  575. {
  576. if (CanStop())
  577. {
  578. HRESULT hr;
  579. IMediaControl *pMC;
  580. // Obtain the interface to our filter graph
  581. hr = media.pGraph->QueryInterface(IID_IMediaControl, (LPVOID *) &pMC);
  582. TraceError("OnMediaStop: QueryInterface(IID_IMediaControl)", hr);
  583. if (SUCCEEDED(hr))
  584. {
  585. #ifdef FROM_START
  586. IMediaPosition * pMP;
  587. OAFilterState state;
  588. // Ask the filter graph to pause
  589. hr = pMC->Pause();
  590. TraceError("OnMediaStop: Pause()", hr);
  591. // if we want always to play from the beginning
  592. // then we should seek back to the start here
  593. // (on app or user stop request, and also after EC_COMPLETE).
  594. hr = media.pGraph->QueryInterface(IID_IMediaPosition, (LPVOID *) &pMP);
  595. TraceError("OnMediaStop: QueryInterface(IID_IMediaPosition)", hr);
  596. if (SUCCEEDED(hr))
  597. {
  598. pMP->put_CurrentPosition(0);
  599. ReleaseObj(pMP);
  600. }
  601. // wait for pause to complete
  602. pMC->GetState(INFINITE, &state);
  603. #endif
  604. // now really do the stop
  605. pMC->Stop();
  606. ReleaseObj(pMC);
  607. ChangeStateTo(Stopped);
  608. return;
  609. }
  610. }
  611. else
  612. {
  613. TraceTag(ttidMedia, "Not valid state for pausing right now: %d.",
  614. media.state);
  615. }
  616. } // OnMediaStop