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.

924 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& c, VideoRendererType renderType)
  137. : component (c),
  138. hwnd (0),
  139. hdc (0),
  140. state (uninitializedState),
  141. hasVideo (false),
  142. videoWidth (0),
  143. videoHeight (0),
  144. type (renderType),
  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() override
  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. {
  257. hr = graphBuilder->RenderFile (fileOrURLPath.toWideCharPointer(), nullptr);
  258. if (FAILED (hr))
  259. {
  260. // Annoyingly, if we don't run the msg loop between failing and deleting the window, the
  261. // whole OS message-dispatch system gets itself into a state, and refuses to deliver any
  262. // more messages for the whole app. (That's what happens in Win7, anyway)
  263. MessageManager::getInstance()->runDispatchLoopUntil (200);
  264. }
  265. }
  266. // remove video renderer if not connected (no video)
  267. if (SUCCEEDED (hr))
  268. {
  269. if (isRendererConnected())
  270. {
  271. hasVideo = true;
  272. hr = videoRenderer->getVideoSize (videoWidth, videoHeight);
  273. }
  274. else
  275. {
  276. hasVideo = false;
  277. graphBuilder->RemoveFilter (baseFilter);
  278. videoRenderer = nullptr;
  279. baseFilter = nullptr;
  280. }
  281. }
  282. // set window to receive events
  283. if (SUCCEEDED (hr))
  284. hr = mediaEvent->SetNotifyWindow ((OAHWND) hwnd, graphEventID, 0);
  285. if (SUCCEEDED (hr))
  286. {
  287. state = stoppedState;
  288. pause();
  289. return true;
  290. }
  291. release();
  292. return false;
  293. }
  294. void release()
  295. {
  296. if (mediaControl != nullptr)
  297. mediaControl->Stop();
  298. if (mediaEvent != nullptr)
  299. mediaEvent->SetNotifyWindow (0, 0, 0);
  300. if (videoRenderer != nullptr)
  301. videoRenderer->setVideoWindow (0);
  302. hasVideo = false;
  303. videoRenderer = nullptr;
  304. baseFilter = nullptr;
  305. basicAudio = nullptr;
  306. mediaEvent = nullptr;
  307. mediaPosition = nullptr;
  308. mediaControl = nullptr;
  309. graphBuilder = nullptr;
  310. state = uninitializedState;
  311. videoWidth = 0;
  312. videoHeight = 0;
  313. if (nativeWindow != nullptr)
  314. deleteNativeWindow();
  315. }
  316. void graphEventProc()
  317. {
  318. LONG ec;
  319. LONG_PTR p1, p2;
  320. jassert (mediaEvent != nullptr);
  321. while (SUCCEEDED (mediaEvent->GetEvent (&ec, &p1, &p2, 0)))
  322. {
  323. switch (ec)
  324. {
  325. case EC_REPAINT:
  326. component.repaint();
  327. break;
  328. case EC_COMPLETE:
  329. if (component.isLooping())
  330. component.goToStart();
  331. else
  332. component.stop();
  333. break;
  334. case EC_USERABORT:
  335. case EC_ERRORABORT:
  336. case EC_ERRORABORTEX:
  337. component.closeMovie();
  338. break;
  339. default:
  340. break;
  341. }
  342. mediaEvent->FreeEventParams (ec, p1, p2);
  343. }
  344. }
  345. //======================================================================
  346. void run()
  347. {
  348. mediaControl->Run();
  349. state = runningState;
  350. }
  351. void stop()
  352. {
  353. mediaControl->Stop();
  354. state = stoppedState;
  355. }
  356. void pause()
  357. {
  358. mediaControl->Pause();
  359. state = pausedState;
  360. }
  361. //======================================================================
  362. bool isInitialised() const noexcept { return state != uninitializedState; }
  363. bool isRunning() const noexcept { return state == runningState; }
  364. bool isPaused() const noexcept { return state == pausedState; }
  365. bool isStopped() const noexcept { return state == stoppedState; }
  366. bool containsVideo() const noexcept { return hasVideo; }
  367. int getVideoWidth() const noexcept { return (int) videoWidth; }
  368. int getVideoHeight() const noexcept { return (int) videoHeight; }
  369. //======================================================================
  370. double getDuration() const
  371. {
  372. REFTIME duration;
  373. mediaPosition->get_Duration (&duration);
  374. return duration;
  375. }
  376. double getPosition() const
  377. {
  378. REFTIME seconds;
  379. mediaPosition->get_CurrentPosition (&seconds);
  380. return seconds;
  381. }
  382. //======================================================================
  383. void setSpeed (const float newSpeed) { mediaPosition->put_Rate (newSpeed); }
  384. void setPosition (const double seconds) { mediaPosition->put_CurrentPosition (seconds); }
  385. void setVolume (const float newVolume) { basicAudio->put_Volume (convertToDShowVolume (newVolume)); }
  386. // in DirectShow, full volume is 0, silence is -10000
  387. static long convertToDShowVolume (const float vol) noexcept
  388. {
  389. if (vol >= 1.0f) return 0;
  390. if (vol <= 0.0f) return -10000;
  391. return roundToInt ((vol * 10000.0f) - 10000.0f);
  392. }
  393. float getVolume() const
  394. {
  395. long volume;
  396. basicAudio->get_Volume (&volume);
  397. return (volume + 10000) / 10000.0f;
  398. }
  399. private:
  400. //======================================================================
  401. enum { graphEventID = WM_APP + 0x43f0 };
  402. DirectShowComponent& component;
  403. HWND hwnd;
  404. HDC hdc;
  405. enum State { uninitializedState, runningState, pausedState, stoppedState };
  406. State state;
  407. bool hasVideo;
  408. long videoWidth, videoHeight;
  409. VideoRendererType type;
  410. ComSmartPtr <IGraphBuilder> graphBuilder;
  411. ComSmartPtr <IMediaControl> mediaControl;
  412. ComSmartPtr <IMediaPosition> mediaPosition;
  413. ComSmartPtr <IMediaEventEx> mediaEvent;
  414. ComSmartPtr <IBasicAudio> basicAudio;
  415. ComSmartPtr <IBaseFilter> baseFilter;
  416. ScopedPointer <DirectShowHelpers::VideoRenderer> videoRenderer;
  417. bool needToUpdateViewport, needToRecreateNativeWindow;
  418. //======================================================================
  419. class NativeWindowClass : private DeletedAtShutdown
  420. {
  421. public:
  422. bool isRegistered() const noexcept { return atom != 0; }
  423. LPCTSTR getWindowClassName() const noexcept { return (LPCTSTR) MAKELONG (atom, 0); }
  424. juce_DeclareSingleton_SingleThreaded_Minimal (NativeWindowClass);
  425. private:
  426. NativeWindowClass()
  427. : atom (0)
  428. {
  429. String windowClassName ("JUCE_DIRECTSHOW_");
  430. windowClassName << (int) (Time::currentTimeMillis() & 0x7fffffff);
  431. HINSTANCE moduleHandle = (HINSTANCE) Process::getCurrentModuleInstanceHandle();
  432. TCHAR moduleFile [1024] = { 0 };
  433. GetModuleFileName (moduleHandle, moduleFile, 1024);
  434. WNDCLASSEX wcex = { 0 };
  435. wcex.cbSize = sizeof (wcex);
  436. wcex.style = CS_OWNDC;
  437. wcex.lpfnWndProc = (WNDPROC) wndProc;
  438. wcex.lpszClassName = windowClassName.toWideCharPointer();
  439. wcex.hInstance = moduleHandle;
  440. atom = RegisterClassEx (&wcex);
  441. jassert (atom != 0);
  442. }
  443. ~NativeWindowClass()
  444. {
  445. if (atom != 0)
  446. UnregisterClass (getWindowClassName(), (HINSTANCE) Process::getCurrentModuleInstanceHandle());
  447. clearSingletonInstance();
  448. }
  449. static LRESULT CALLBACK wndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  450. {
  451. if (DirectShowContext* const c = (DirectShowContext*) GetWindowLongPtr (hwnd, GWLP_USERDATA))
  452. {
  453. switch (msg)
  454. {
  455. case WM_NCHITTEST: return HTTRANSPARENT;
  456. case WM_ERASEBKGND: return 1;
  457. case WM_DISPLAYCHANGE: c->displayResolutionChanged(); break;
  458. case graphEventID: c->graphEventProc(); return 0;
  459. default: break;
  460. }
  461. }
  462. return DefWindowProc (hwnd, msg, wParam, lParam);
  463. }
  464. ATOM atom;
  465. JUCE_DECLARE_NON_COPYABLE (NativeWindowClass)
  466. };
  467. //======================================================================
  468. class NativeWindow
  469. {
  470. public:
  471. NativeWindow (HWND parentToAddTo, void* const userData)
  472. : hwnd (0), hdc (0)
  473. {
  474. NativeWindowClass* const wc = NativeWindowClass::getInstance();
  475. if (wc->isRegistered())
  476. {
  477. DWORD exstyle = 0;
  478. DWORD type = WS_CHILD;
  479. hwnd = CreateWindowEx (exstyle, wc->getWindowClassName(),
  480. L"", type, 0, 0, 0, 0, parentToAddTo, 0,
  481. (HINSTANCE) Process::getCurrentModuleInstanceHandle(), 0);
  482. if (hwnd != 0)
  483. {
  484. hdc = GetDC (hwnd);
  485. SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) userData);
  486. }
  487. }
  488. jassert (hwnd != 0);
  489. }
  490. ~NativeWindow()
  491. {
  492. if (hwnd != 0)
  493. {
  494. SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) 0);
  495. DestroyWindow (hwnd);
  496. }
  497. }
  498. HWND getHandle() const noexcept { return hwnd; }
  499. HDC getContext() const noexcept { return hdc; }
  500. void setWindowPosition (const Rectangle<int>& newBounds)
  501. {
  502. SetWindowPos (hwnd, 0, newBounds.getX(), newBounds.getY(),
  503. newBounds.getWidth(), newBounds.getHeight(),
  504. SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
  505. }
  506. void showWindow (const bool shouldBeVisible)
  507. {
  508. ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
  509. }
  510. private:
  511. HWND hwnd;
  512. HDC hdc;
  513. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeWindow)
  514. };
  515. ScopedPointer<NativeWindow> nativeWindow;
  516. //======================================================================
  517. bool createNativeWindow()
  518. {
  519. jassert (nativeWindow == nullptr);
  520. if (ComponentPeer* const topLevelPeer = component.getTopLevelComponent()->getPeer())
  521. {
  522. nativeWindow = new NativeWindow ((HWND) topLevelPeer->getNativeHandle(), this);
  523. hwnd = nativeWindow->getHandle();
  524. if (hwnd != 0)
  525. {
  526. hdc = GetDC (hwnd);
  527. component.updateContextPosition();
  528. component.showContext (component.isShowing());
  529. return true;
  530. }
  531. else
  532. {
  533. nativeWindow = nullptr;
  534. }
  535. }
  536. else
  537. {
  538. jassertfalse;
  539. }
  540. return false;
  541. }
  542. void deleteNativeWindow()
  543. {
  544. jassert (nativeWindow != nullptr);
  545. ReleaseDC (hwnd, hdc);
  546. hwnd = 0;
  547. hdc = 0;
  548. nativeWindow = nullptr;
  549. }
  550. bool isRendererConnected()
  551. {
  552. ComSmartPtr <IEnumPins> enumPins;
  553. HRESULT hr = baseFilter->EnumPins (enumPins.resetAndGetPointerAddress());
  554. if (SUCCEEDED (hr))
  555. hr = enumPins->Reset();
  556. ComSmartPtr<IPin> pin;
  557. while (SUCCEEDED (hr)
  558. && enumPins->Next (1, pin.resetAndGetPointerAddress(), nullptr) == S_OK)
  559. {
  560. ComSmartPtr<IPin> otherPin;
  561. hr = pin->ConnectedTo (otherPin.resetAndGetPointerAddress());
  562. if (SUCCEEDED (hr))
  563. {
  564. PIN_DIRECTION direction;
  565. hr = pin->QueryDirection (&direction);
  566. if (SUCCEEDED (hr) && direction == PINDIR_INPUT)
  567. return true;
  568. }
  569. else if (hr == VFW_E_NOT_CONNECTED)
  570. {
  571. hr = S_OK;
  572. }
  573. }
  574. return false;
  575. }
  576. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectShowContext)
  577. };
  578. juce_ImplementSingleton_SingleThreaded (DirectShowComponent::DirectShowContext::NativeWindowClass);
  579. //======================================================================
  580. class DirectShowComponent::DirectShowComponentWatcher : public ComponentMovementWatcher
  581. {
  582. public:
  583. DirectShowComponentWatcher (DirectShowComponent* const c)
  584. : ComponentMovementWatcher (c),
  585. owner (c)
  586. {
  587. }
  588. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  589. {
  590. if (owner->videoLoaded)
  591. owner->updateContextPosition();
  592. }
  593. void componentPeerChanged() override
  594. {
  595. if (owner->videoLoaded)
  596. owner->recreateNativeWindowAsync();
  597. }
  598. void componentVisibilityChanged() override
  599. {
  600. if (owner->videoLoaded)
  601. owner->showContext (owner->isShowing());
  602. }
  603. private:
  604. DirectShowComponent* const owner;
  605. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectShowComponentWatcher)
  606. };
  607. //======================================================================
  608. DirectShowComponent::DirectShowComponent (VideoRendererType type)
  609. : videoLoaded (false),
  610. looping (false)
  611. {
  612. setOpaque (true);
  613. context = new DirectShowContext (*this, type);
  614. componentWatcher = new DirectShowComponentWatcher (this);
  615. }
  616. DirectShowComponent::~DirectShowComponent()
  617. {
  618. componentWatcher = nullptr;
  619. }
  620. bool DirectShowComponent::isDirectShowAvailable()
  621. {
  622. static bool isDSAvailable = DirectShowHelpers::checkDShowAvailability();
  623. return isDSAvailable;
  624. }
  625. void DirectShowComponent::recreateNativeWindowAsync()
  626. {
  627. context->recreateNativeWindowAsync();
  628. repaint();
  629. }
  630. void DirectShowComponent::updateContextPosition()
  631. {
  632. context->updateContextPosition();
  633. if (getWidth() > 0 && getHeight() > 0)
  634. if (ComponentPeer* peer = getTopLevelComponent()->getPeer())
  635. context->updateWindowPosition (peer->getAreaCoveredBy (*this));
  636. }
  637. void DirectShowComponent::showContext (const bool shouldBeVisible)
  638. {
  639. context->showWindow (shouldBeVisible);
  640. }
  641. void DirectShowComponent::paint (Graphics& g)
  642. {
  643. if (videoLoaded)
  644. context->handleUpdateNowIfNeeded();
  645. else
  646. g.fillAll (Colours::grey);
  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.clear();
  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. 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. }