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