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.

925 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. void updateWindowPosition (const Rectangle<int>& newBounds)
  176. {
  177. nativeWindow->setWindowPosition (newBounds);
  178. }
  179. void showWindow (bool shouldBeVisible)
  180. {
  181. nativeWindow->showWindow (shouldBeVisible);
  182. }
  183. //======================================================================
  184. void repaint()
  185. {
  186. if (hasVideo)
  187. videoRenderer->repaintVideo (nativeWindow->getHandle(), nativeWindow->getContext());
  188. }
  189. void updateVideoPosition()
  190. {
  191. if (hasVideo)
  192. videoRenderer->setVideoPosition (nativeWindow->getHandle(), videoWidth, videoHeight);
  193. }
  194. void displayResolutionChanged()
  195. {
  196. if (hasVideo)
  197. videoRenderer->displayModeChanged();
  198. }
  199. //======================================================================
  200. void peerChanged()
  201. {
  202. deleteNativeWindow();
  203. mediaEvent->SetNotifyWindow (0, 0, 0);
  204. if (videoRenderer != nullptr)
  205. videoRenderer->setVideoWindow (nullptr);
  206. createNativeWindow();
  207. mediaEvent->SetNotifyWindow ((OAHWND) hwnd, graphEventID, 0);
  208. if (videoRenderer != nullptr)
  209. videoRenderer->setVideoWindow (hwnd);
  210. }
  211. //======================================================================
  212. bool loadFile (const String& fileOrURLPath)
  213. {
  214. jassert (state == uninitializedState);
  215. if (! createNativeWindow())
  216. return false;
  217. HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph);
  218. // basic playback interfaces
  219. if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (mediaControl);
  220. if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (mediaPosition);
  221. if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (mediaEvent);
  222. if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (basicAudio);
  223. // video renderer interface
  224. if (SUCCEEDED (hr))
  225. {
  226. #if JUCE_MEDIAFOUNDATION
  227. if (type == dshowEVR)
  228. videoRenderer = new DirectShowHelpers::EVR();
  229. else
  230. #endif
  231. videoRenderer = new DirectShowHelpers::VMR7();
  232. hr = videoRenderer->create (graphBuilder, baseFilter, hwnd);
  233. }
  234. // build filter graph
  235. if (SUCCEEDED (hr))
  236. hr = graphBuilder->RenderFile (fileOrURLPath.toWideCharPointer(), nullptr);
  237. // remove video renderer if not connected (no video)
  238. if (SUCCEEDED (hr))
  239. {
  240. if (isRendererConnected())
  241. {
  242. hasVideo = true;
  243. hr = videoRenderer->getVideoSize (videoWidth, videoHeight);
  244. }
  245. else
  246. {
  247. hasVideo = false;
  248. graphBuilder->RemoveFilter (baseFilter);
  249. videoRenderer = nullptr;
  250. baseFilter = nullptr;
  251. }
  252. }
  253. // set window to receive events
  254. if (SUCCEEDED (hr))
  255. hr = mediaEvent->SetNotifyWindow ((OAHWND) hwnd, graphEventID, 0);
  256. if (SUCCEEDED (hr))
  257. {
  258. state = stoppedState;
  259. return true;
  260. }
  261. release();
  262. return false;
  263. }
  264. void release()
  265. {
  266. if (mediaControl != nullptr)
  267. mediaControl->Stop();
  268. if (mediaEvent != nullptr)
  269. mediaEvent->SetNotifyWindow (0, 0, 0);
  270. if (videoRenderer != nullptr)
  271. videoRenderer->setVideoWindow (0);
  272. hasVideo = false;
  273. videoRenderer = nullptr;
  274. baseFilter = nullptr;
  275. basicAudio = nullptr;
  276. mediaEvent = nullptr;
  277. mediaPosition = nullptr;
  278. mediaControl = nullptr;
  279. graphBuilder = nullptr;
  280. state = uninitializedState;
  281. videoWidth = 0;
  282. videoHeight = 0;
  283. if (nativeWindow != nullptr)
  284. deleteNativeWindow();
  285. }
  286. void graphEventProc()
  287. {
  288. LONG ec;
  289. LONG_PTR p1, p2;
  290. jassert (mediaEvent != nullptr);
  291. while (SUCCEEDED (mediaEvent->GetEvent (&ec, &p1, &p2, 0)))
  292. {
  293. switch (ec)
  294. {
  295. case EC_REPAINT:
  296. component.repaint();
  297. break;
  298. case EC_COMPLETE:
  299. if (component.isLooping())
  300. component.goToStart();
  301. else
  302. component.stop();
  303. break;
  304. case EC_USERABORT:
  305. case EC_ERRORABORT:
  306. case EC_ERRORABORTEX:
  307. component.closeMovie();
  308. break;
  309. default:
  310. break;
  311. }
  312. mediaEvent->FreeEventParams (ec, p1, p2);
  313. }
  314. }
  315. //======================================================================
  316. void run()
  317. {
  318. mediaControl->Run();
  319. state = runningState;
  320. }
  321. void stop()
  322. {
  323. mediaControl->Stop();
  324. state = stoppedState;
  325. }
  326. void pause()
  327. {
  328. mediaControl->Pause();
  329. state = pausedState;
  330. }
  331. //======================================================================
  332. bool isInitialised() const noexcept { return state != uninitializedState; }
  333. bool isRunning() const noexcept { return state == runningState; }
  334. bool isPaused() const noexcept { return state == pausedState; }
  335. bool isStopped() const noexcept { return state == stoppedState; }
  336. bool containsVideo() const noexcept { return hasVideo; }
  337. int getVideoWidth() const noexcept { return (int) videoWidth; }
  338. int getVideoHeight() const noexcept { return (int) videoHeight; }
  339. //======================================================================
  340. double getDuration() const
  341. {
  342. REFTIME duration;
  343. mediaPosition->get_Duration (&duration);
  344. return duration;
  345. }
  346. double getPosition() const
  347. {
  348. REFTIME seconds;
  349. mediaPosition->get_CurrentPosition (&seconds);
  350. return seconds;
  351. }
  352. //======================================================================
  353. void setSpeed (const float newSpeed) { mediaPosition->put_Rate (newSpeed); }
  354. void setPosition (const double seconds) { mediaPosition->put_CurrentPosition (seconds); }
  355. void setVolume (const float newVolume) { basicAudio->put_Volume (convertToDShowVolume (newVolume)); }
  356. // in DirectShow, full volume is 0, silence is -10000
  357. static long convertToDShowVolume (const float vol) noexcept
  358. {
  359. if (vol >= 1.0f) return 0;
  360. if (vol <= 0.0f) return -10000;
  361. return roundToInt ((vol * 10000.0f) - 10000.0f);
  362. }
  363. float getVolume() const
  364. {
  365. long volume;
  366. basicAudio->get_Volume (&volume);
  367. return (volume + 10000) / 10000.0f;
  368. }
  369. private:
  370. //======================================================================
  371. enum { graphEventID = WM_APP + 0x43f0 };
  372. DirectShowComponent& component;
  373. HWND hwnd;
  374. HDC hdc;
  375. enum State { uninitializedState, runningState, pausedState, stoppedState };
  376. State state;
  377. bool hasVideo;
  378. long videoWidth, videoHeight;
  379. VideoRendererType type;
  380. ComSmartPtr <IGraphBuilder> graphBuilder;
  381. ComSmartPtr <IMediaControl> mediaControl;
  382. ComSmartPtr <IMediaPosition> mediaPosition;
  383. ComSmartPtr <IMediaEventEx> mediaEvent;
  384. ComSmartPtr <IBasicAudio> basicAudio;
  385. ComSmartPtr <IBaseFilter> baseFilter;
  386. ScopedPointer <DirectShowHelpers::VideoRenderer> videoRenderer;
  387. //======================================================================
  388. class NativeWindowClass : private DeletedAtShutdown
  389. {
  390. private:
  391. NativeWindowClass()
  392. : atom (0)
  393. {
  394. String windowClassName ("JUCE_DIRECTSHOW_");
  395. windowClassName << (int) (Time::currentTimeMillis() & 0x7fffffff);
  396. HINSTANCE moduleHandle = (HINSTANCE) Process::getCurrentModuleInstanceHandle();
  397. TCHAR moduleFile [1024] = { 0 };
  398. GetModuleFileName (moduleHandle, moduleFile, 1024);
  399. WNDCLASSEX wcex = { 0 };
  400. wcex.cbSize = sizeof (wcex);
  401. wcex.style = CS_OWNDC;
  402. wcex.lpfnWndProc = (WNDPROC) wndProc;
  403. wcex.lpszClassName = windowClassName.toWideCharPointer();
  404. wcex.hInstance = moduleHandle;
  405. atom = RegisterClassEx (&wcex);
  406. jassert (atom != 0);
  407. }
  408. ~NativeWindowClass()
  409. {
  410. if (atom != 0)
  411. UnregisterClass (getWindowClassName(), (HINSTANCE) Process::getCurrentModuleInstanceHandle());
  412. clearSingletonInstance();
  413. }
  414. static LRESULT CALLBACK wndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  415. {
  416. DirectShowContext* c = (DirectShowContext*) GetWindowLongPtr (hwnd, GWLP_USERDATA);
  417. if (c != nullptr)
  418. {
  419. switch (msg)
  420. {
  421. case WM_ERASEBKGND: return 1;
  422. case WM_DISPLAYCHANGE: c->displayResolutionChanged(); break;
  423. case graphEventID: c->graphEventProc(); return 0;
  424. default: break;
  425. }
  426. }
  427. return DefWindowProc (hwnd, msg, wParam, lParam);
  428. }
  429. public:
  430. bool isRegistered() const noexcept { return atom != 0; }
  431. LPCTSTR getWindowClassName() const noexcept { return (LPCTSTR) MAKELONG (atom, 0); }
  432. juce_DeclareSingleton_SingleThreaded_Minimal (NativeWindowClass);
  433. private:
  434. ATOM atom;
  435. JUCE_DECLARE_NON_COPYABLE (NativeWindowClass);
  436. };
  437. //======================================================================
  438. class NativeWindow
  439. {
  440. public:
  441. NativeWindow (HWND parentToAddTo, void* const userData)
  442. : hwnd (0), hdc (0)
  443. {
  444. NativeWindowClass* const wc = NativeWindowClass::getInstance();
  445. if (wc->isRegistered())
  446. {
  447. DWORD exstyle = 0;
  448. DWORD type = WS_CHILD;
  449. hwnd = CreateWindowEx (exstyle, wc->getWindowClassName(),
  450. L"", type, 0, 0, 0, 0, parentToAddTo, 0,
  451. (HINSTANCE) Process::getCurrentModuleInstanceHandle(), 0);
  452. if (hwnd != 0)
  453. {
  454. hdc = GetDC (hwnd);
  455. SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) userData);
  456. }
  457. }
  458. jassert (hwnd != 0);
  459. }
  460. ~NativeWindow()
  461. {
  462. if (hwnd != 0)
  463. {
  464. SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) 0);
  465. DestroyWindow (hwnd);
  466. }
  467. }
  468. HWND getHandle() const noexcept { return hwnd; }
  469. HDC getContext() const noexcept { return hdc; }
  470. void setWindowPosition (const Rectangle<int>& newBounds)
  471. {
  472. SetWindowPos (hwnd, 0, newBounds.getX(), newBounds.getY(),
  473. newBounds.getWidth(), newBounds.getHeight(),
  474. SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
  475. }
  476. void showWindow (const bool shouldBeVisible)
  477. {
  478. ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
  479. }
  480. private:
  481. HWND hwnd;
  482. HDC hdc;
  483. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeWindow);
  484. };
  485. ScopedPointer<NativeWindow> nativeWindow;
  486. //======================================================================
  487. bool createNativeWindow()
  488. {
  489. jassert (nativeWindow == nullptr);
  490. ComponentPeer* topLevelPeer = component.getTopLevelComponent()->getPeer();
  491. jassert (topLevelPeer != nullptr);
  492. if (topLevelPeer != nullptr)
  493. {
  494. nativeWindow = new NativeWindow ((HWND) topLevelPeer->getNativeHandle(), this);
  495. hwnd = nativeWindow->getHandle();
  496. if (hwnd != 0)
  497. {
  498. hdc = GetDC (hwnd);
  499. component.updateContextPosition();
  500. component.showContext (component.isShowing());
  501. return true;
  502. }
  503. else
  504. {
  505. nativeWindow = nullptr;
  506. }
  507. }
  508. return false;
  509. }
  510. void deleteNativeWindow()
  511. {
  512. jassert (nativeWindow != nullptr);
  513. ReleaseDC (hwnd, hdc);
  514. hwnd = 0;
  515. hdc = 0;
  516. nativeWindow = nullptr;
  517. }
  518. bool isRendererConnected()
  519. {
  520. ComSmartPtr <IEnumPins> enumPins;
  521. HRESULT hr = baseFilter->EnumPins (enumPins.resetAndGetPointerAddress());
  522. if (SUCCEEDED (hr))
  523. hr = enumPins->Reset();
  524. ComSmartPtr<IPin> pin;
  525. while (SUCCEEDED (hr)
  526. && enumPins->Next (1, pin.resetAndGetPointerAddress(), nullptr) == S_OK)
  527. {
  528. ComSmartPtr<IPin> otherPin;
  529. hr = pin->ConnectedTo (otherPin.resetAndGetPointerAddress());
  530. if (SUCCEEDED (hr))
  531. {
  532. PIN_DIRECTION direction;
  533. hr = pin->QueryDirection (&direction);
  534. if (SUCCEEDED (hr) && direction == PINDIR_INPUT)
  535. return true;
  536. }
  537. else if (hr == VFW_E_NOT_CONNECTED)
  538. {
  539. hr = S_OK;
  540. }
  541. }
  542. return false;
  543. }
  544. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectShowContext);
  545. };
  546. juce_ImplementSingleton_SingleThreaded (DirectShowComponent::DirectShowContext::NativeWindowClass);
  547. //======================================================================
  548. class DirectShowComponent::DirectShowComponentWatcher : public ComponentMovementWatcher
  549. {
  550. public:
  551. DirectShowComponentWatcher (DirectShowComponent* const owner_)
  552. : ComponentMovementWatcher (owner_),
  553. owner (owner_)
  554. {
  555. }
  556. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/)
  557. {
  558. if (owner->videoLoaded)
  559. owner->updateContextPosition();
  560. }
  561. void componentPeerChanged()
  562. {
  563. if (owner->videoLoaded)
  564. owner->recreateNativeWindowAsync();
  565. }
  566. void componentVisibilityChanged()
  567. {
  568. if (owner->videoLoaded)
  569. owner->showContext (owner->isShowing());
  570. }
  571. //======================================================================
  572. private:
  573. DirectShowComponent* const owner;
  574. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectShowComponentWatcher);
  575. };
  576. //======================================================================
  577. DirectShowComponent::DirectShowComponent (VideoRendererType type)
  578. : videoLoaded (false),
  579. looping (false),
  580. needToUpdateViewport (true),
  581. needToRecreateNativeWindow (false)
  582. {
  583. setOpaque (true);
  584. context = new DirectShowContext (*this, type);
  585. componentWatcher = new DirectShowComponentWatcher (this);
  586. }
  587. DirectShowComponent::~DirectShowComponent()
  588. {
  589. componentWatcher = nullptr;
  590. }
  591. bool DirectShowComponent::isDirectShowAvailable()
  592. {
  593. static bool isDSAvailable = DirectShowHelpers::checkDShowAvailability();
  594. return isDSAvailable;
  595. }
  596. void DirectShowComponent::recreateNativeWindowAsync()
  597. {
  598. needToRecreateNativeWindow = true;
  599. repaint();
  600. }
  601. void DirectShowComponent::updateContextPosition()
  602. {
  603. needToUpdateViewport = true;
  604. if (getWidth() > 0 && getHeight() > 0)
  605. {
  606. Component* const topComp = getTopLevelComponent();
  607. if (topComp->getPeer() != nullptr)
  608. context->updateWindowPosition (topComp->getLocalArea (this, getLocalBounds()));
  609. }
  610. }
  611. void DirectShowComponent::showContext (const bool shouldBeVisible)
  612. {
  613. context->showWindow (shouldBeVisible);
  614. }
  615. void DirectShowComponent::paint (Graphics& g)
  616. {
  617. if (videoLoaded)
  618. {
  619. if (needToRecreateNativeWindow)
  620. {
  621. context->peerChanged();
  622. needToRecreateNativeWindow = false;
  623. }
  624. if (needToUpdateViewport)
  625. {
  626. context->updateVideoPosition();
  627. needToUpdateViewport = false;
  628. }
  629. context->repaint();
  630. ComponentPeer* const peer = getPeer();
  631. if (peer != nullptr)
  632. peer->addMaskedRegion (getScreenBounds() - peer->getScreenPosition());
  633. }
  634. else
  635. {
  636. g.fillAll (Colours::grey);
  637. }
  638. }
  639. //======================================================================
  640. bool DirectShowComponent::loadMovie (const String& fileOrURLPath)
  641. {
  642. closeMovie();
  643. videoLoaded = context->loadFile (fileOrURLPath);
  644. if (videoLoaded)
  645. {
  646. videoPath = fileOrURLPath;
  647. context->updateVideoPosition();
  648. }
  649. return videoLoaded;
  650. }
  651. bool DirectShowComponent::loadMovie (const File& videoFile)
  652. {
  653. return loadMovie (videoFile.getFullPathName());
  654. }
  655. bool DirectShowComponent::loadMovie (const URL& videoURL)
  656. {
  657. return loadMovie (videoURL.toString (false));
  658. }
  659. void DirectShowComponent::closeMovie()
  660. {
  661. if (videoLoaded)
  662. context->release();
  663. videoLoaded = false;
  664. videoPath = String::empty;
  665. }
  666. //======================================================================
  667. File DirectShowComponent::getCurrentMoviePath() const { return videoPath; }
  668. bool DirectShowComponent::isMovieOpen() const { return videoLoaded; }
  669. double DirectShowComponent::getMovieDuration() const { return videoLoaded ? context->getDuration() : 0.0; }
  670. void DirectShowComponent::setLooping (const bool shouldLoop) { looping = shouldLoop; }
  671. bool DirectShowComponent::isLooping() const { return looping; }
  672. void DirectShowComponent::getMovieNormalSize (int &width, int &height) const
  673. {
  674. width = context->getVideoWidth();
  675. height = context->getVideoHeight();
  676. }
  677. //======================================================================
  678. void DirectShowComponent::setBoundsWithCorrectAspectRatio (const Rectangle<int>& spaceToFitWithin,
  679. const RectanglePlacement& placement)
  680. {
  681. int normalWidth, normalHeight;
  682. getMovieNormalSize (normalWidth, normalHeight);
  683. const Rectangle<int> normalSize (0, 0, normalWidth, normalHeight);
  684. if (! (spaceToFitWithin.isEmpty() || normalSize.isEmpty()))
  685. setBounds (placement.appliedTo (normalSize, spaceToFitWithin));
  686. else
  687. setBounds (spaceToFitWithin);
  688. }
  689. //======================================================================
  690. void DirectShowComponent::play()
  691. {
  692. if (videoLoaded)
  693. context->run();
  694. }
  695. void DirectShowComponent::stop()
  696. {
  697. if (videoLoaded)
  698. context->pause();
  699. }
  700. bool DirectShowComponent::isPlaying() const
  701. {
  702. return context->isRunning();
  703. }
  704. void DirectShowComponent::goToStart()
  705. {
  706. setPosition (0.0);
  707. }
  708. void DirectShowComponent::setPosition (const double seconds)
  709. {
  710. if (videoLoaded)
  711. context->setPosition (seconds);
  712. }
  713. double DirectShowComponent::getPosition() const
  714. {
  715. return videoLoaded ? context->getPosition() : 0.0;
  716. }
  717. void DirectShowComponent::setSpeed (const float newSpeed)
  718. {
  719. if (videoLoaded)
  720. context->setSpeed (newSpeed);
  721. }
  722. void DirectShowComponent::setMovieVolume (const float newVolume)
  723. {
  724. if (videoLoaded)
  725. context->setVolume (newVolume);
  726. }
  727. float DirectShowComponent::getMovieVolume() const
  728. {
  729. return videoLoaded ? context->getVolume() : 0.0f;
  730. }