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.

926 lines
27KB

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