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.

944 lines
29KB

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