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.

929 lines
28KB

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