The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

936 lines
27KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. //======================================================================
  19. namespace DirectShowHelpers
  20. {
  21. bool checkDShowAvailability()
  22. {
  23. ComSmartPtr <IGraphBuilder> graph;
  24. return SUCCEEDED (graph.CoCreateInstance (CLSID_FilterGraph));
  25. }
  26. //======================================================================
  27. class VideoRenderer
  28. {
  29. public:
  30. VideoRenderer() {}
  31. virtual ~VideoRenderer() {}
  32. virtual HRESULT create (ComSmartPtr <IGraphBuilder>& graphBuilder,
  33. ComSmartPtr <IBaseFilter>& baseFilter, HWND hwnd) = 0;
  34. virtual void setVideoWindow (HWND hwnd) = 0;
  35. virtual void setVideoPosition (HWND hwnd, long videoWidth, long videoHeight) = 0;
  36. virtual void repaintVideo (HWND hwnd, HDC hdc) = 0;
  37. virtual void displayModeChanged() = 0;
  38. virtual HRESULT getVideoSize (long& videoWidth, long& videoHeight) = 0;
  39. };
  40. //======================================================================
  41. class VMR7 : public VideoRenderer
  42. {
  43. public:
  44. VMR7() {}
  45. HRESULT create (ComSmartPtr <IGraphBuilder>& graphBuilder,
  46. ComSmartPtr <IBaseFilter>& baseFilter, HWND hwnd)
  47. {
  48. ComSmartPtr <IVMRFilterConfig> filterConfig;
  49. HRESULT hr = baseFilter.CoCreateInstance (CLSID_VideoMixingRenderer);
  50. if (SUCCEEDED (hr))
  51. hr = graphBuilder->AddFilter (baseFilter, L"VMR-7");
  52. if (SUCCEEDED (hr))
  53. hr = baseFilter.QueryInterface (filterConfig);
  54. if (SUCCEEDED (hr))
  55. hr = filterConfig->SetRenderingMode (VMRMode_Windowless);
  56. if (SUCCEEDED (hr))
  57. hr = baseFilter.QueryInterface (windowlessControl);
  58. if (SUCCEEDED (hr))
  59. hr = windowlessControl->SetVideoClippingWindow (hwnd);
  60. if (SUCCEEDED (hr))
  61. hr = windowlessControl->SetAspectRatioMode (VMR_ARMODE_LETTER_BOX);
  62. return hr;
  63. }
  64. void setVideoWindow (HWND hwnd)
  65. {
  66. windowlessControl->SetVideoClippingWindow (hwnd);
  67. }
  68. void setVideoPosition (HWND hwnd, long videoWidth, long videoHeight)
  69. {
  70. RECT src, dest;
  71. SetRect (&src, 0, 0, videoWidth, videoHeight);
  72. GetClientRect (hwnd, &dest);
  73. windowlessControl->SetVideoPosition (&src, &dest);
  74. }
  75. void repaintVideo (HWND hwnd, HDC hdc)
  76. {
  77. windowlessControl->RepaintVideo (hwnd, hdc);
  78. }
  79. void displayModeChanged()
  80. {
  81. windowlessControl->DisplayModeChanged();
  82. }
  83. HRESULT getVideoSize (long& videoWidth, long& videoHeight)
  84. {
  85. return windowlessControl->GetNativeVideoSize (&videoWidth, &videoHeight, nullptr, nullptr);
  86. }
  87. private:
  88. ComSmartPtr <IVMRWindowlessControl> windowlessControl;
  89. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VMR7);
  90. };
  91. //======================================================================
  92. #if JUCE_MEDIAFOUNDATION
  93. class EVR : public VideoRenderer
  94. {
  95. public:
  96. EVR() {}
  97. HRESULT create (ComSmartPtr <IGraphBuilder>& graphBuilder,
  98. ComSmartPtr <IBaseFilter>& baseFilter, HWND hwnd)
  99. {
  100. ComSmartPtr <IMFGetService> getService;
  101. HRESULT hr = baseFilter.CoCreateInstance (CLSID_EnhancedVideoRenderer);
  102. if (SUCCEEDED (hr))
  103. hr = graphBuilder->AddFilter (baseFilter, L"EVR");
  104. if (SUCCEEDED (hr))
  105. hr = baseFilter.QueryInterface (getService);
  106. if (SUCCEEDED (hr))
  107. hr = getService->GetService (MR_VIDEO_RENDER_SERVICE, IID_IMFVideoDisplayControl,
  108. (LPVOID*) videoDisplayControl.resetAndGetPointerAddress());
  109. if (SUCCEEDED (hr))
  110. hr = videoDisplayControl->SetVideoWindow (hwnd);
  111. if (SUCCEEDED (hr))
  112. hr = videoDisplayControl->SetAspectRatioMode (MFVideoARMode_PreservePicture);
  113. return hr;
  114. }
  115. void setVideoWindow (HWND hwnd)
  116. {
  117. videoDisplayControl->SetVideoWindow (hwnd);
  118. }
  119. void setVideoPosition (HWND hwnd, long /*videoWidth*/, long /*videoHeight*/)
  120. {
  121. const MFVideoNormalizedRect src = { 0.0f, 0.0f, 1.0f, 1.0f };
  122. RECT dest;
  123. GetClientRect (hwnd, &dest);
  124. videoDisplayControl->SetVideoPosition (&src, &dest);
  125. }
  126. void repaintVideo (HWND /*hwnd*/, HDC /*hdc*/)
  127. {
  128. videoDisplayControl->RepaintVideo();
  129. }
  130. void displayModeChanged() {}
  131. HRESULT getVideoSize (long& videoWidth, long& videoHeight)
  132. {
  133. SIZE sz;
  134. HRESULT hr = videoDisplayControl->GetNativeVideoSize (&sz, nullptr);
  135. videoWidth = sz.cx;
  136. videoHeight = sz.cy;
  137. return hr;
  138. }
  139. private:
  140. ComSmartPtr <IMFVideoDisplayControl> videoDisplayControl;
  141. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EVR);
  142. };
  143. #endif
  144. }
  145. //======================================================================
  146. class DirectShowComponent::DirectShowContext
  147. {
  148. public:
  149. DirectShowContext (DirectShowComponent& component_, VideoRendererType type_)
  150. : component (component_),
  151. hwnd (0),
  152. hdc (0),
  153. state (uninitializedState),
  154. hasVideo (false),
  155. videoWidth (0),
  156. videoHeight (0),
  157. type (type_)
  158. {
  159. CoInitialize (0);
  160. if (type == dshowDefault)
  161. {
  162. type = dshowVMR7;
  163. #if JUCE_MEDIAFOUNDATION
  164. if (SystemStats::getOperatingSystemType() >= SystemStats::WinVista)
  165. type = dshowEVR;
  166. #endif
  167. }
  168. }
  169. ~DirectShowContext()
  170. {
  171. release();
  172. CoUninitialize();
  173. }
  174. //======================================================================
  175. HWND getNativeWindowHandle() const
  176. {
  177. return nativeWindow != nullptr ? nativeWindow->getHandle() : 0;
  178. }
  179. //======================================================================
  180. void updateWindowPosition (const Rectangle<int>& newBounds)
  181. {
  182. nativeWindow->setWindowPosition (newBounds);
  183. }
  184. void showWindow (bool shouldBeVisible)
  185. {
  186. nativeWindow->showWindow (shouldBeVisible);
  187. }
  188. //======================================================================
  189. void repaint()
  190. {
  191. if (hasVideo)
  192. videoRenderer->repaintVideo (nativeWindow->getHandle(), nativeWindow->getContext());
  193. }
  194. void updateVideoPosition()
  195. {
  196. if (hasVideo)
  197. videoRenderer->setVideoPosition (nativeWindow->getHandle(), videoWidth, videoHeight);
  198. }
  199. void displayResolutionChanged()
  200. {
  201. if (hasVideo)
  202. videoRenderer->displayModeChanged();
  203. }
  204. //======================================================================
  205. void peerChanged()
  206. {
  207. deleteNativeWindow();
  208. mediaEvent->SetNotifyWindow (0, 0, 0);
  209. if (videoRenderer != nullptr)
  210. videoRenderer->setVideoWindow (nullptr);
  211. createNativeWindow();
  212. mediaEvent->SetNotifyWindow ((OAHWND) hwnd, graphEventID, 0);
  213. if (videoRenderer != nullptr)
  214. videoRenderer->setVideoWindow (hwnd);
  215. }
  216. //======================================================================
  217. bool loadFile (const String& fileOrURLPath)
  218. {
  219. jassert (state == uninitializedState);
  220. if (! createNativeWindow())
  221. return false;
  222. HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph);
  223. // basic playback interfaces
  224. if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (mediaControl);
  225. if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (mediaPosition);
  226. if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (mediaEvent);
  227. if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (basicAudio);
  228. // video renderer interface
  229. if (SUCCEEDED (hr))
  230. {
  231. #if JUCE_MEDIAFOUNDATION
  232. if (type == dshowEVR)
  233. videoRenderer = new DirectShowHelpers::EVR();
  234. else
  235. #endif
  236. videoRenderer = new DirectShowHelpers::VMR7();
  237. hr = videoRenderer->create (graphBuilder, baseFilter, hwnd);
  238. }
  239. // build filter graph
  240. if (SUCCEEDED (hr))
  241. hr = graphBuilder->RenderFile (fileOrURLPath.toWideCharPointer(), nullptr);
  242. // remove video renderer if not connected (no video)
  243. if (SUCCEEDED (hr))
  244. {
  245. if (isRendererConnected())
  246. {
  247. hasVideo = true;
  248. hr = videoRenderer->getVideoSize (videoWidth, videoHeight);
  249. }
  250. else
  251. {
  252. hasVideo = false;
  253. graphBuilder->RemoveFilter (baseFilter);
  254. videoRenderer = nullptr;
  255. baseFilter = nullptr;
  256. }
  257. }
  258. // set window to receive events
  259. if (SUCCEEDED (hr))
  260. hr = mediaEvent->SetNotifyWindow ((OAHWND) hwnd, graphEventID, 0);
  261. if (SUCCEEDED (hr))
  262. {
  263. state = stoppedState;
  264. return true;
  265. }
  266. release();
  267. return false;
  268. }
  269. void release()
  270. {
  271. if (mediaControl != nullptr)
  272. mediaControl->Stop();
  273. if (mediaEvent != nullptr)
  274. mediaEvent->SetNotifyWindow (0, 0, 0);
  275. if (videoRenderer != nullptr)
  276. videoRenderer->setVideoWindow (0);
  277. hasVideo = false;
  278. videoRenderer = nullptr;
  279. baseFilter = nullptr;
  280. basicAudio = nullptr;
  281. mediaEvent = nullptr;
  282. mediaPosition = nullptr;
  283. mediaControl = nullptr;
  284. graphBuilder = nullptr;
  285. state = uninitializedState;
  286. videoWidth = 0;
  287. videoHeight = 0;
  288. if (nativeWindow != nullptr)
  289. deleteNativeWindow();
  290. }
  291. void graphEventProc()
  292. {
  293. LONG ec;
  294. LONG_PTR p1, p2;
  295. jassert (mediaEvent != nullptr);
  296. while (SUCCEEDED (mediaEvent->GetEvent (&ec, &p1, &p2, 0)))
  297. {
  298. switch (ec)
  299. {
  300. case EC_REPAINT:
  301. component.repaint();
  302. break;
  303. case EC_COMPLETE:
  304. if (component.isLooping())
  305. component.goToStart();
  306. else
  307. component.stop();
  308. break;
  309. case EC_USERABORT:
  310. case EC_ERRORABORT:
  311. case EC_ERRORABORTEX:
  312. component.closeMovie();
  313. break;
  314. default:
  315. break;
  316. }
  317. mediaEvent->FreeEventParams (ec, p1, p2);
  318. }
  319. }
  320. //======================================================================
  321. void run()
  322. {
  323. mediaControl->Run();
  324. state = runningState;
  325. }
  326. void stop()
  327. {
  328. mediaControl->Stop();
  329. state = stoppedState;
  330. }
  331. void pause()
  332. {
  333. mediaControl->Pause();
  334. state = pausedState;
  335. }
  336. //======================================================================
  337. bool isInitialised() const noexcept { return state != uninitializedState; }
  338. bool isRunning() const noexcept { return state == runningState; }
  339. bool isPaused() const noexcept { return state == pausedState; }
  340. bool isStopped() const noexcept { return state == stoppedState; }
  341. bool containsVideo() const noexcept { return hasVideo; }
  342. int getVideoWidth() const noexcept { return (int) videoWidth; }
  343. int getVideoHeight() const noexcept { return (int) videoHeight; }
  344. //======================================================================
  345. double getDuration() const
  346. {
  347. REFTIME duration;
  348. mediaPosition->get_Duration (&duration);
  349. return duration;
  350. }
  351. double getPosition() const
  352. {
  353. REFTIME seconds;
  354. mediaPosition->get_CurrentPosition (&seconds);
  355. return seconds;
  356. }
  357. //======================================================================
  358. void setSpeed (const float newSpeed) { mediaPosition->put_Rate (newSpeed); }
  359. void setPosition (const double seconds) { mediaPosition->put_CurrentPosition (seconds); }
  360. void setVolume (const float newVolume) { basicAudio->put_Volume (convertToDShowVolume (newVolume)); }
  361. // in DirectShow, full volume is 0, silence is -10000
  362. static long convertToDShowVolume (const float vol) noexcept
  363. {
  364. if (vol >= 1.0f) return 0;
  365. if (vol <= 0.0f) return -10000;
  366. return roundToInt ((vol * 10000.0f) - 10000.0f);
  367. }
  368. float getVolume() const
  369. {
  370. long volume;
  371. basicAudio->get_Volume (&volume);
  372. return (volume + 10000) / 10000.0f;
  373. }
  374. private:
  375. //======================================================================
  376. enum { graphEventID = WM_APP + 0x43f0 };
  377. DirectShowComponent& component;
  378. HWND hwnd;
  379. HDC hdc;
  380. enum State { uninitializedState, runningState, pausedState, stoppedState };
  381. State state;
  382. bool hasVideo;
  383. long videoWidth, videoHeight;
  384. VideoRendererType type;
  385. ComSmartPtr <IGraphBuilder> graphBuilder;
  386. ComSmartPtr <IMediaControl> mediaControl;
  387. ComSmartPtr <IMediaPosition> mediaPosition;
  388. ComSmartPtr <IMediaEventEx> mediaEvent;
  389. ComSmartPtr <IBasicAudio> basicAudio;
  390. ComSmartPtr <IBaseFilter> baseFilter;
  391. ScopedPointer <DirectShowHelpers::VideoRenderer> videoRenderer;
  392. //======================================================================
  393. class NativeWindowClass : public DeletedAtShutdown
  394. {
  395. private:
  396. NativeWindowClass()
  397. : atom (0)
  398. {
  399. String windowClassName ("JUCE_DIRECTSHOW_");
  400. windowClassName << (int) (Time::currentTimeMillis() & 0x7fffffff);
  401. HINSTANCE moduleHandle = (HINSTANCE) Process::getCurrentModuleInstanceHandle();
  402. TCHAR moduleFile [1024] = { 0 };
  403. GetModuleFileName (moduleHandle, moduleFile, 1024);
  404. WNDCLASSEX wcex = { 0 };
  405. wcex.cbSize = sizeof (wcex);
  406. wcex.style = CS_OWNDC;
  407. wcex.lpfnWndProc = (WNDPROC) wndProc;
  408. wcex.lpszClassName = windowClassName.toWideCharPointer();
  409. wcex.hInstance = moduleHandle;
  410. atom = RegisterClassEx (&wcex);
  411. jassert (atom != 0);
  412. }
  413. ~NativeWindowClass()
  414. {
  415. if (atom != 0)
  416. UnregisterClass (getWindowClassName(), (HINSTANCE) Process::getCurrentModuleInstanceHandle());
  417. clearSingletonInstance();
  418. }
  419. static LRESULT CALLBACK wndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  420. {
  421. DirectShowContext* c = (DirectShowContext*) GetWindowLongPtr (hwnd, GWLP_USERDATA);
  422. if (c != nullptr)
  423. {
  424. jassert (c->getNativeWindowHandle() == hwnd);
  425. switch (msg)
  426. {
  427. case WM_ERASEBKGND: return 1;
  428. case WM_DISPLAYCHANGE: c->displayResolutionChanged(); break;
  429. case graphEventID: c->graphEventProc(); return 0;
  430. default: break;
  431. }
  432. }
  433. return DefWindowProc (hwnd, msg, wParam, lParam);
  434. }
  435. public:
  436. bool isRegistered() const noexcept { return atom != 0; }
  437. LPCTSTR getWindowClassName() const noexcept { return (LPCTSTR) MAKELONG (atom, 0); }
  438. juce_DeclareSingleton_SingleThreaded_Minimal (NativeWindowClass);
  439. private:
  440. ATOM atom;
  441. JUCE_DECLARE_NON_COPYABLE (NativeWindowClass);
  442. };
  443. //======================================================================
  444. class NativeWindow
  445. {
  446. public:
  447. NativeWindow (HWND parentToAddTo, void* const userData)
  448. : hwnd (0), hdc (0)
  449. {
  450. NativeWindowClass* const wc = NativeWindowClass::getInstance();
  451. if (wc->isRegistered())
  452. {
  453. DWORD exstyle = 0;
  454. DWORD type = WS_CHILD;
  455. hwnd = CreateWindowEx (exstyle, wc->getWindowClassName(),
  456. L"", type, 0, 0, 0, 0, parentToAddTo, 0,
  457. (HINSTANCE) Process::getCurrentModuleInstanceHandle(), 0);
  458. if (hwnd != 0)
  459. {
  460. hdc = GetDC (hwnd);
  461. SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) userData);
  462. }
  463. }
  464. jassert (hwnd != 0);
  465. }
  466. ~NativeWindow()
  467. {
  468. if (hwnd != 0)
  469. {
  470. SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) 0);
  471. DestroyWindow (hwnd);
  472. }
  473. }
  474. HWND getHandle() const noexcept { return hwnd; }
  475. HDC getContext() const noexcept { return hdc; }
  476. void setWindowPosition (const Rectangle<int>& newBounds)
  477. {
  478. SetWindowPos (hwnd, 0, newBounds.getX(), newBounds.getY(),
  479. newBounds.getWidth(), newBounds.getHeight(),
  480. SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
  481. }
  482. void showWindow (const bool shouldBeVisible)
  483. {
  484. ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
  485. }
  486. private:
  487. HWND hwnd;
  488. HDC hdc;
  489. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeWindow);
  490. };
  491. ScopedPointer<NativeWindow> nativeWindow;
  492. //======================================================================
  493. bool createNativeWindow()
  494. {
  495. jassert (nativeWindow == nullptr);
  496. ComponentPeer* topLevelPeer = component.getTopLevelComponent()->getPeer();
  497. jassert (topLevelPeer != nullptr);
  498. if (topLevelPeer != nullptr)
  499. {
  500. nativeWindow = new NativeWindow ((HWND) topLevelPeer->getNativeHandle(), this);
  501. hwnd = nativeWindow->getHandle();
  502. if (hwnd != 0)
  503. {
  504. hdc = GetDC (hwnd);
  505. component.updateContextPosition();
  506. component.showContext (component.isShowing());
  507. return true;
  508. }
  509. else
  510. {
  511. nativeWindow = nullptr;
  512. }
  513. }
  514. return false;
  515. }
  516. void deleteNativeWindow()
  517. {
  518. jassert (nativeWindow != nullptr);
  519. ReleaseDC (hwnd, hdc);
  520. hwnd = 0;
  521. hdc = 0;
  522. nativeWindow = nullptr;
  523. }
  524. bool isRendererConnected()
  525. {
  526. ComSmartPtr <IEnumPins> enumPins;
  527. HRESULT hr = baseFilter->EnumPins (enumPins.resetAndGetPointerAddress());
  528. if (SUCCEEDED (hr))
  529. hr = enumPins->Reset();
  530. ComSmartPtr<IPin> pin;
  531. while (SUCCEEDED (hr)
  532. && enumPins->Next (1, pin.resetAndGetPointerAddress(), nullptr) == S_OK)
  533. {
  534. ComSmartPtr<IPin> otherPin;
  535. hr = pin->ConnectedTo (otherPin.resetAndGetPointerAddress());
  536. if (SUCCEEDED (hr))
  537. {
  538. PIN_DIRECTION direction;
  539. hr = pin->QueryDirection (&direction);
  540. if (SUCCEEDED (hr) && direction == PINDIR_INPUT)
  541. return true;
  542. }
  543. else if (hr == VFW_E_NOT_CONNECTED)
  544. {
  545. hr = S_OK;
  546. }
  547. }
  548. return false;
  549. }
  550. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectShowContext);
  551. };
  552. juce_ImplementSingleton_SingleThreaded (DirectShowComponent::DirectShowContext::NativeWindowClass);
  553. //======================================================================
  554. class DirectShowComponent::DirectShowComponentWatcher : public ComponentMovementWatcher
  555. {
  556. public:
  557. DirectShowComponentWatcher (DirectShowComponent* const owner_)
  558. : ComponentMovementWatcher (owner_),
  559. owner (owner_)
  560. {
  561. }
  562. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
  563. {
  564. if (owner->videoLoaded)
  565. owner->updateContextPosition();
  566. }
  567. void componentPeerChanged()
  568. {
  569. if (owner->videoLoaded)
  570. owner->recreateNativeWindowAsync();
  571. }
  572. void componentVisibilityChanged()
  573. {
  574. if (owner->videoLoaded)
  575. owner->showContext (owner->isShowing());
  576. }
  577. //======================================================================
  578. private:
  579. DirectShowComponent* const owner;
  580. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectShowComponentWatcher);
  581. };
  582. //======================================================================
  583. DirectShowComponent::DirectShowComponent (VideoRendererType type)
  584. : videoLoaded (false),
  585. looping (false),
  586. needToUpdateViewport (true),
  587. needToRecreateNativeWindow (false)
  588. {
  589. setOpaque (true);
  590. context = new DirectShowContext (*this, type);
  591. componentWatcher = new DirectShowComponentWatcher (this);
  592. }
  593. DirectShowComponent::~DirectShowComponent()
  594. {
  595. componentWatcher = nullptr;
  596. }
  597. bool DirectShowComponent::isDirectShowAvailable()
  598. {
  599. static bool isDSAvailable = DirectShowHelpers::checkDShowAvailability();
  600. return isDSAvailable;
  601. }
  602. void DirectShowComponent::recreateNativeWindowAsync()
  603. {
  604. needToRecreateNativeWindow = true;
  605. repaint();
  606. }
  607. void DirectShowComponent::updateContextPosition()
  608. {
  609. needToUpdateViewport = true;
  610. if (getWidth() > 0 && getHeight() > 0)
  611. {
  612. Component* const topComp = getTopLevelComponent();
  613. if (topComp->getPeer() != nullptr)
  614. context->updateWindowPosition (topComp->getLocalArea (this, getLocalBounds()));
  615. }
  616. }
  617. void DirectShowComponent::showContext (const bool shouldBeVisible)
  618. {
  619. context->showWindow (shouldBeVisible);
  620. }
  621. void DirectShowComponent::paint (Graphics& g)
  622. {
  623. if (videoLoaded)
  624. {
  625. if (needToRecreateNativeWindow)
  626. {
  627. context->peerChanged();
  628. needToRecreateNativeWindow = false;
  629. }
  630. if (needToUpdateViewport)
  631. {
  632. context->updateVideoPosition();
  633. needToUpdateViewport = false;
  634. }
  635. context->repaint();
  636. ComponentPeer* const peer = getPeer();
  637. if (peer != nullptr)
  638. {
  639. const Point<int> topLeft (getScreenPosition() - peer->getScreenPosition());
  640. peer->addMaskedRegion (topLeft.getX(), topLeft.getY(), getWidth(), getHeight());
  641. }
  642. }
  643. else
  644. {
  645. g.fillAll (Colours::grey);
  646. }
  647. }
  648. //======================================================================
  649. bool DirectShowComponent::loadMovie (const String& fileOrURLPath)
  650. {
  651. closeMovie();
  652. videoLoaded = context->loadFile (fileOrURLPath);
  653. if (videoLoaded)
  654. {
  655. videoPath = fileOrURLPath;
  656. context->updateVideoPosition();
  657. }
  658. return videoLoaded;
  659. }
  660. bool DirectShowComponent::loadMovie (const File& videoFile)
  661. {
  662. return loadMovie (videoFile.getFullPathName());
  663. }
  664. bool DirectShowComponent::loadMovie (const URL& videoURL)
  665. {
  666. return loadMovie (videoURL.toString (false));
  667. }
  668. void DirectShowComponent::closeMovie()
  669. {
  670. if (videoLoaded)
  671. context->release();
  672. videoLoaded = false;
  673. videoPath = String::empty;
  674. }
  675. //======================================================================
  676. File DirectShowComponent::getCurrentMoviePath() const { return videoPath; }
  677. bool DirectShowComponent::isMovieOpen() const { return videoLoaded; }
  678. double DirectShowComponent::getMovieDuration() const { return videoLoaded ? context->getDuration() : 0.0; }
  679. void DirectShowComponent::setLooping (const bool shouldLoop) { looping = shouldLoop; }
  680. bool DirectShowComponent::isLooping() const { return looping; }
  681. void DirectShowComponent::getMovieNormalSize (int &width, int &height) const
  682. {
  683. width = context->getVideoWidth();
  684. height = context->getVideoHeight();
  685. }
  686. //======================================================================
  687. void DirectShowComponent::setBoundsWithCorrectAspectRatio (const Rectangle<int>& spaceToFitWithin,
  688. const RectanglePlacement& placement)
  689. {
  690. int normalWidth, normalHeight;
  691. getMovieNormalSize (normalWidth, normalHeight);
  692. const Rectangle<int> normalSize (0, 0, normalWidth, normalHeight);
  693. if (! (spaceToFitWithin.isEmpty() || normalSize.isEmpty()))
  694. setBounds (placement.appliedTo (normalSize, spaceToFitWithin));
  695. else
  696. setBounds (spaceToFitWithin);
  697. }
  698. //======================================================================
  699. void DirectShowComponent::play()
  700. {
  701. if (videoLoaded)
  702. context->run();
  703. }
  704. void DirectShowComponent::stop()
  705. {
  706. if (videoLoaded)
  707. context->pause();
  708. }
  709. bool DirectShowComponent::isPlaying() const
  710. {
  711. return context->isRunning();
  712. }
  713. void DirectShowComponent::goToStart()
  714. {
  715. setPosition (0.0);
  716. }
  717. void DirectShowComponent::setPosition (const double seconds)
  718. {
  719. if (videoLoaded)
  720. context->setPosition (seconds);
  721. }
  722. double DirectShowComponent::getPosition() const
  723. {
  724. return videoLoaded ? context->getPosition() : 0.0;
  725. }
  726. void DirectShowComponent::setSpeed (const float newSpeed)
  727. {
  728. if (videoLoaded)
  729. context->setSpeed (newSpeed);
  730. }
  731. void DirectShowComponent::setMovieVolume (const float newVolume)
  732. {
  733. if (videoLoaded)
  734. context->setVolume (newVolume);
  735. }
  736. float DirectShowComponent::getMovieVolume() const
  737. {
  738. return videoLoaded ? context->getVolume() : 0.0f;
  739. }