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.

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