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.

927 lines
28KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2015 - ROLI 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. // Note that if you're trying to open a file and this method fails, you may
  292. // just need to install a suitable codec. It seems that by default DirectShow
  293. // doesn't support a very good range of formats.
  294. release();
  295. return false;
  296. }
  297. void release()
  298. {
  299. if (mediaControl != nullptr)
  300. mediaControl->Stop();
  301. if (mediaEvent != nullptr)
  302. mediaEvent->SetNotifyWindow (0, 0, 0);
  303. if (videoRenderer != nullptr)
  304. videoRenderer->setVideoWindow (0);
  305. hasVideo = false;
  306. videoRenderer = nullptr;
  307. baseFilter = nullptr;
  308. basicAudio = nullptr;
  309. mediaEvent = nullptr;
  310. mediaPosition = nullptr;
  311. mediaControl = nullptr;
  312. graphBuilder = nullptr;
  313. state = uninitializedState;
  314. videoWidth = 0;
  315. videoHeight = 0;
  316. if (nativeWindow != nullptr)
  317. deleteNativeWindow();
  318. }
  319. void graphEventProc()
  320. {
  321. LONG ec;
  322. LONG_PTR p1, p2;
  323. jassert (mediaEvent != nullptr);
  324. while (SUCCEEDED (mediaEvent->GetEvent (&ec, &p1, &p2, 0)))
  325. {
  326. switch (ec)
  327. {
  328. case EC_REPAINT:
  329. component.repaint();
  330. break;
  331. case EC_COMPLETE:
  332. if (component.isLooping())
  333. component.goToStart();
  334. else
  335. component.stop();
  336. break;
  337. case EC_USERABORT:
  338. case EC_ERRORABORT:
  339. case EC_ERRORABORTEX:
  340. component.closeMovie();
  341. break;
  342. default:
  343. break;
  344. }
  345. mediaEvent->FreeEventParams (ec, p1, p2);
  346. }
  347. }
  348. //======================================================================
  349. void run()
  350. {
  351. mediaControl->Run();
  352. state = runningState;
  353. }
  354. void stop()
  355. {
  356. mediaControl->Stop();
  357. state = stoppedState;
  358. }
  359. void pause()
  360. {
  361. mediaControl->Pause();
  362. state = pausedState;
  363. }
  364. //======================================================================
  365. bool isInitialised() const noexcept { return state != uninitializedState; }
  366. bool isRunning() const noexcept { return state == runningState; }
  367. bool isPaused() const noexcept { return state == pausedState; }
  368. bool isStopped() const noexcept { return state == stoppedState; }
  369. bool containsVideo() const noexcept { return hasVideo; }
  370. int getVideoWidth() const noexcept { return (int) videoWidth; }
  371. int getVideoHeight() const noexcept { return (int) videoHeight; }
  372. //======================================================================
  373. double getDuration() const
  374. {
  375. REFTIME duration;
  376. mediaPosition->get_Duration (&duration);
  377. return duration;
  378. }
  379. double getPosition() const
  380. {
  381. REFTIME seconds;
  382. mediaPosition->get_CurrentPosition (&seconds);
  383. return seconds;
  384. }
  385. //======================================================================
  386. void setSpeed (const float newSpeed) { mediaPosition->put_Rate (newSpeed); }
  387. void setPosition (const double seconds) { mediaPosition->put_CurrentPosition (seconds); }
  388. void setVolume (const float newVolume) { basicAudio->put_Volume (convertToDShowVolume (newVolume)); }
  389. // in DirectShow, full volume is 0, silence is -10000
  390. static long convertToDShowVolume (const float vol) noexcept
  391. {
  392. if (vol >= 1.0f) return 0;
  393. if (vol <= 0.0f) return -10000;
  394. return roundToInt ((vol * 10000.0f) - 10000.0f);
  395. }
  396. float getVolume() const
  397. {
  398. long volume;
  399. basicAudio->get_Volume (&volume);
  400. return (volume + 10000) / 10000.0f;
  401. }
  402. private:
  403. //======================================================================
  404. enum { graphEventID = WM_APP + 0x43f0 };
  405. DirectShowComponent& component;
  406. HWND hwnd;
  407. HDC hdc;
  408. enum State { uninitializedState, runningState, pausedState, stoppedState };
  409. State state;
  410. bool hasVideo;
  411. long videoWidth, videoHeight;
  412. VideoRendererType type;
  413. ComSmartPtr <IGraphBuilder> graphBuilder;
  414. ComSmartPtr <IMediaControl> mediaControl;
  415. ComSmartPtr <IMediaPosition> mediaPosition;
  416. ComSmartPtr <IMediaEventEx> mediaEvent;
  417. ComSmartPtr <IBasicAudio> basicAudio;
  418. ComSmartPtr <IBaseFilter> baseFilter;
  419. ScopedPointer <DirectShowHelpers::VideoRenderer> videoRenderer;
  420. bool needToUpdateViewport, needToRecreateNativeWindow;
  421. //======================================================================
  422. class NativeWindowClass : private DeletedAtShutdown
  423. {
  424. public:
  425. bool isRegistered() const noexcept { return atom != 0; }
  426. LPCTSTR getWindowClassName() const noexcept { return (LPCTSTR) MAKELONG (atom, 0); }
  427. juce_DeclareSingleton_SingleThreaded_Minimal (NativeWindowClass)
  428. private:
  429. NativeWindowClass()
  430. : atom (0)
  431. {
  432. String windowClassName ("JUCE_DIRECTSHOW_");
  433. windowClassName << (int) (Time::currentTimeMillis() & 0x7fffffff);
  434. HINSTANCE moduleHandle = (HINSTANCE) Process::getCurrentModuleInstanceHandle();
  435. TCHAR moduleFile [1024] = { 0 };
  436. GetModuleFileName (moduleHandle, moduleFile, 1024);
  437. WNDCLASSEX wcex = { 0 };
  438. wcex.cbSize = sizeof (wcex);
  439. wcex.style = CS_OWNDC;
  440. wcex.lpfnWndProc = (WNDPROC) wndProc;
  441. wcex.lpszClassName = windowClassName.toWideCharPointer();
  442. wcex.hInstance = moduleHandle;
  443. atom = RegisterClassEx (&wcex);
  444. jassert (atom != 0);
  445. }
  446. ~NativeWindowClass()
  447. {
  448. if (atom != 0)
  449. UnregisterClass (getWindowClassName(), (HINSTANCE) Process::getCurrentModuleInstanceHandle());
  450. clearSingletonInstance();
  451. }
  452. static LRESULT CALLBACK wndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  453. {
  454. if (DirectShowContext* const c = (DirectShowContext*) GetWindowLongPtr (hwnd, GWLP_USERDATA))
  455. {
  456. switch (msg)
  457. {
  458. case WM_NCHITTEST: return HTTRANSPARENT;
  459. case WM_ERASEBKGND: return 1;
  460. case WM_DISPLAYCHANGE: c->displayResolutionChanged(); break;
  461. case graphEventID: c->graphEventProc(); return 0;
  462. default: break;
  463. }
  464. }
  465. return DefWindowProc (hwnd, msg, wParam, lParam);
  466. }
  467. ATOM atom;
  468. JUCE_DECLARE_NON_COPYABLE (NativeWindowClass)
  469. };
  470. //======================================================================
  471. class NativeWindow
  472. {
  473. public:
  474. NativeWindow (HWND parentToAddTo, void* const userData)
  475. : hwnd (0), hdc (0)
  476. {
  477. NativeWindowClass* const wc = NativeWindowClass::getInstance();
  478. if (wc->isRegistered())
  479. {
  480. DWORD exstyle = 0;
  481. DWORD type = WS_CHILD;
  482. hwnd = CreateWindowEx (exstyle, wc->getWindowClassName(),
  483. L"", type, 0, 0, 0, 0, parentToAddTo, 0,
  484. (HINSTANCE) Process::getCurrentModuleInstanceHandle(), 0);
  485. if (hwnd != 0)
  486. {
  487. hdc = GetDC (hwnd);
  488. SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) userData);
  489. }
  490. }
  491. jassert (hwnd != 0);
  492. }
  493. ~NativeWindow()
  494. {
  495. if (hwnd != 0)
  496. {
  497. SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) 0);
  498. DestroyWindow (hwnd);
  499. }
  500. }
  501. HWND getHandle() const noexcept { return hwnd; }
  502. HDC getContext() const noexcept { return hdc; }
  503. void setWindowPosition (const Rectangle<int>& newBounds)
  504. {
  505. SetWindowPos (hwnd, 0, newBounds.getX(), newBounds.getY(),
  506. newBounds.getWidth(), newBounds.getHeight(),
  507. SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
  508. }
  509. void showWindow (const bool shouldBeVisible)
  510. {
  511. ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
  512. }
  513. private:
  514. HWND hwnd;
  515. HDC hdc;
  516. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeWindow)
  517. };
  518. ScopedPointer<NativeWindow> nativeWindow;
  519. //======================================================================
  520. bool createNativeWindow()
  521. {
  522. jassert (nativeWindow == nullptr);
  523. if (ComponentPeer* const topLevelPeer = component.getTopLevelComponent()->getPeer())
  524. {
  525. nativeWindow = new NativeWindow ((HWND) topLevelPeer->getNativeHandle(), this);
  526. hwnd = nativeWindow->getHandle();
  527. if (hwnd != 0)
  528. {
  529. hdc = GetDC (hwnd);
  530. component.updateContextPosition();
  531. component.showContext (component.isShowing());
  532. return true;
  533. }
  534. else
  535. {
  536. nativeWindow = nullptr;
  537. }
  538. }
  539. else
  540. {
  541. jassertfalse;
  542. }
  543. return false;
  544. }
  545. void deleteNativeWindow()
  546. {
  547. jassert (nativeWindow != nullptr);
  548. ReleaseDC (hwnd, hdc);
  549. hwnd = 0;
  550. hdc = 0;
  551. nativeWindow = nullptr;
  552. }
  553. bool isRendererConnected()
  554. {
  555. ComSmartPtr <IEnumPins> enumPins;
  556. HRESULT hr = baseFilter->EnumPins (enumPins.resetAndGetPointerAddress());
  557. if (SUCCEEDED (hr))
  558. hr = enumPins->Reset();
  559. ComSmartPtr<IPin> pin;
  560. while (SUCCEEDED (hr)
  561. && enumPins->Next (1, pin.resetAndGetPointerAddress(), nullptr) == S_OK)
  562. {
  563. ComSmartPtr<IPin> otherPin;
  564. hr = pin->ConnectedTo (otherPin.resetAndGetPointerAddress());
  565. if (SUCCEEDED (hr))
  566. {
  567. PIN_DIRECTION direction;
  568. hr = pin->QueryDirection (&direction);
  569. if (SUCCEEDED (hr) && direction == PINDIR_INPUT)
  570. return true;
  571. }
  572. else if (hr == VFW_E_NOT_CONNECTED)
  573. {
  574. hr = S_OK;
  575. }
  576. }
  577. return false;
  578. }
  579. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectShowContext)
  580. };
  581. juce_ImplementSingleton_SingleThreaded (DirectShowComponent::DirectShowContext::NativeWindowClass)
  582. //======================================================================
  583. class DirectShowComponent::DirectShowComponentWatcher : public ComponentMovementWatcher
  584. {
  585. public:
  586. DirectShowComponentWatcher (DirectShowComponent* const c)
  587. : ComponentMovementWatcher (c),
  588. owner (c)
  589. {
  590. }
  591. void componentMovedOrResized (bool /*wasMoved*/, bool /*wasResized*/) override
  592. {
  593. if (owner->videoLoaded)
  594. owner->updateContextPosition();
  595. }
  596. void componentPeerChanged() override
  597. {
  598. if (owner->videoLoaded)
  599. owner->recreateNativeWindowAsync();
  600. }
  601. void componentVisibilityChanged() override
  602. {
  603. if (owner->videoLoaded)
  604. owner->showContext (owner->isShowing());
  605. }
  606. private:
  607. DirectShowComponent* const owner;
  608. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectShowComponentWatcher)
  609. };
  610. //======================================================================
  611. DirectShowComponent::DirectShowComponent (VideoRendererType type)
  612. : videoLoaded (false),
  613. looping (false)
  614. {
  615. setOpaque (true);
  616. context = new DirectShowContext (*this, type);
  617. componentWatcher = new DirectShowComponentWatcher (this);
  618. }
  619. DirectShowComponent::~DirectShowComponent()
  620. {
  621. componentWatcher = nullptr;
  622. }
  623. bool DirectShowComponent::isDirectShowAvailable()
  624. {
  625. static bool isDSAvailable = DirectShowHelpers::checkDShowAvailability();
  626. return isDSAvailable;
  627. }
  628. void DirectShowComponent::recreateNativeWindowAsync()
  629. {
  630. context->recreateNativeWindowAsync();
  631. repaint();
  632. }
  633. void DirectShowComponent::updateContextPosition()
  634. {
  635. context->updateContextPosition();
  636. if (getWidth() > 0 && getHeight() > 0)
  637. if (ComponentPeer* peer = getTopLevelComponent()->getPeer())
  638. context->updateWindowPosition (peer->getAreaCoveredBy (*this));
  639. }
  640. void DirectShowComponent::showContext (const bool shouldBeVisible)
  641. {
  642. context->showWindow (shouldBeVisible);
  643. }
  644. void DirectShowComponent::paint (Graphics& g)
  645. {
  646. if (videoLoaded)
  647. context->handleUpdateNowIfNeeded();
  648. else
  649. g.fillAll (Colours::grey);
  650. }
  651. //======================================================================
  652. bool DirectShowComponent::loadMovie (const String& fileOrURLPath)
  653. {
  654. closeMovie();
  655. videoLoaded = context->loadFile (fileOrURLPath);
  656. if (videoLoaded)
  657. {
  658. videoPath = fileOrURLPath;
  659. context->updateVideoPosition();
  660. }
  661. return videoLoaded;
  662. }
  663. bool DirectShowComponent::loadMovie (const File& videoFile)
  664. {
  665. return loadMovie (videoFile.getFullPathName());
  666. }
  667. bool DirectShowComponent::loadMovie (const URL& videoURL)
  668. {
  669. return loadMovie (videoURL.toString (false));
  670. }
  671. void DirectShowComponent::closeMovie()
  672. {
  673. if (videoLoaded)
  674. context->release();
  675. videoLoaded = false;
  676. videoPath.clear();
  677. }
  678. //======================================================================
  679. File DirectShowComponent::getCurrentMoviePath() const { return videoPath; }
  680. bool DirectShowComponent::isMovieOpen() const { return videoLoaded; }
  681. double DirectShowComponent::getMovieDuration() const { return videoLoaded ? context->getDuration() : 0.0; }
  682. void DirectShowComponent::setLooping (const bool shouldLoop) { looping = shouldLoop; }
  683. bool DirectShowComponent::isLooping() const { return looping; }
  684. void DirectShowComponent::getMovieNormalSize (int &width, int &height) const
  685. {
  686. width = context->getVideoWidth();
  687. height = context->getVideoHeight();
  688. }
  689. //======================================================================
  690. void DirectShowComponent::setBoundsWithCorrectAspectRatio (const Rectangle<int>& spaceToFitWithin,
  691. RectanglePlacement placement)
  692. {
  693. int normalWidth, normalHeight;
  694. getMovieNormalSize (normalWidth, normalHeight);
  695. const Rectangle<int> normalSize (0, 0, normalWidth, normalHeight);
  696. if (! (spaceToFitWithin.isEmpty() || normalSize.isEmpty()))
  697. setBounds (placement.appliedTo (normalSize, spaceToFitWithin));
  698. else
  699. setBounds (spaceToFitWithin);
  700. }
  701. //======================================================================
  702. void DirectShowComponent::play()
  703. {
  704. if (videoLoaded)
  705. context->run();
  706. }
  707. void DirectShowComponent::stop()
  708. {
  709. if (videoLoaded)
  710. context->pause();
  711. }
  712. bool DirectShowComponent::isPlaying() const
  713. {
  714. return context->isRunning();
  715. }
  716. void DirectShowComponent::goToStart()
  717. {
  718. setPosition (0.0);
  719. }
  720. void DirectShowComponent::setPosition (const double seconds)
  721. {
  722. if (videoLoaded)
  723. context->setPosition (seconds);
  724. }
  725. double DirectShowComponent::getPosition() const
  726. {
  727. return videoLoaded ? context->getPosition() : 0.0;
  728. }
  729. void DirectShowComponent::setSpeed (const float newSpeed)
  730. {
  731. if (videoLoaded)
  732. context->setSpeed (newSpeed);
  733. }
  734. void DirectShowComponent::setMovieVolume (const float newVolume)
  735. {
  736. if (videoLoaded)
  737. context->setVolume (newVolume);
  738. }
  739. float DirectShowComponent::getMovieVolume() const
  740. {
  741. return videoLoaded ? context->getVolume() : 0.0f;
  742. }