|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2020 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- By using JUCE, you agree to the terms of both the JUCE 6 End-User License
- Agreement and JUCE Privacy Policy (both effective as of the 16th June 2020).
-
- End User License Agreement: www.juce.com/juce-6-licence
- Privacy Policy: www.juce.com/juce-privacy-policy
-
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- namespace VideoRenderers
- {
- //==============================================================================
- struct Base
- {
- virtual ~Base() {}
-
- virtual HRESULT create (ComSmartPtr<IGraphBuilder>&, ComSmartPtr<IBaseFilter>&, HWND) = 0;
- virtual void setVideoWindow (HWND) = 0;
- virtual void setVideoPosition (HWND) = 0;
- virtual void repaintVideo (HWND, HDC) = 0;
- virtual void displayModeChanged() = 0;
- virtual HRESULT getVideoSize (long& videoWidth, long& videoHeight) = 0;
- };
-
- //==============================================================================
- struct VMR7 : public Base
- {
- VMR7() {}
-
- HRESULT create (ComSmartPtr<IGraphBuilder>& graphBuilder,
- ComSmartPtr<IBaseFilter>& baseFilter, HWND hwnd) override
- {
- ComSmartPtr<IVMRFilterConfig> filterConfig;
-
- HRESULT hr = baseFilter.CoCreateInstance (CLSID_VideoMixingRenderer);
-
- if (SUCCEEDED (hr)) hr = graphBuilder->AddFilter (baseFilter, L"VMR-7");
- if (SUCCEEDED (hr)) hr = baseFilter.QueryInterface (filterConfig);
- if (SUCCEEDED (hr)) hr = filterConfig->SetRenderingMode (VMRMode_Windowless);
- if (SUCCEEDED (hr)) hr = baseFilter.QueryInterface (windowlessControl);
- if (SUCCEEDED (hr)) hr = windowlessControl->SetVideoClippingWindow (hwnd);
- if (SUCCEEDED (hr)) hr = windowlessControl->SetAspectRatioMode (VMR_ARMODE_LETTER_BOX);
-
- return hr;
- }
-
- void setVideoWindow (HWND hwnd) override
- {
- windowlessControl->SetVideoClippingWindow (hwnd);
- }
-
- void setVideoPosition (HWND hwnd) override
- {
- long videoWidth = 0, videoHeight = 0;
- windowlessControl->GetNativeVideoSize (&videoWidth, &videoHeight, nullptr, nullptr);
-
- RECT src, dest;
- SetRect (&src, 0, 0, videoWidth, videoHeight);
- GetClientRect (hwnd, &dest);
-
- windowlessControl->SetVideoPosition (&src, &dest);
- }
-
- void repaintVideo (HWND hwnd, HDC hdc) override
- {
- windowlessControl->RepaintVideo (hwnd, hdc);
- }
-
- void displayModeChanged() override
- {
- windowlessControl->DisplayModeChanged();
- }
-
- HRESULT getVideoSize (long& videoWidth, long& videoHeight) override
- {
- return windowlessControl->GetNativeVideoSize (&videoWidth, &videoHeight, nullptr, nullptr);
- }
-
- ComSmartPtr<IVMRWindowlessControl> windowlessControl;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VMR7)
- };
-
-
- //==============================================================================
- struct EVR : public Base
- {
- EVR() {}
-
- HRESULT create (ComSmartPtr<IGraphBuilder>& graphBuilder,
- ComSmartPtr<IBaseFilter>& baseFilter, HWND hwnd) override
- {
- ComSmartPtr<IMFGetService> getService;
-
- HRESULT hr = baseFilter.CoCreateInstance (CLSID_EnhancedVideoRenderer);
-
- if (SUCCEEDED (hr)) hr = graphBuilder->AddFilter (baseFilter, L"EVR");
- if (SUCCEEDED (hr)) hr = baseFilter.QueryInterface (getService);
- if (SUCCEEDED (hr)) hr = getService->GetService (MR_VIDEO_RENDER_SERVICE, IID_IMFVideoDisplayControl,
- (void**) videoDisplayControl.resetAndGetPointerAddress());
- if (SUCCEEDED (hr)) hr = videoDisplayControl->SetVideoWindow (hwnd);
- if (SUCCEEDED (hr)) hr = videoDisplayControl->SetAspectRatioMode (MFVideoARMode_PreservePicture);
-
- return hr;
- }
-
- void setVideoWindow (HWND hwnd) override
- {
- videoDisplayControl->SetVideoWindow (hwnd);
- }
-
- void setVideoPosition (HWND hwnd) override
- {
- const MFVideoNormalizedRect src = { 0.0f, 0.0f, 1.0f, 1.0f };
-
- RECT dest;
- GetClientRect (hwnd, &dest);
-
- videoDisplayControl->SetVideoPosition (&src, &dest);
- }
-
- void repaintVideo (HWND, HDC) override
- {
- videoDisplayControl->RepaintVideo();
- }
-
- void displayModeChanged() override {}
-
- HRESULT getVideoSize (long& videoWidth, long& videoHeight) override
- {
- SIZE sz = { 0, 0 };
- HRESULT hr = videoDisplayControl->GetNativeVideoSize (&sz, nullptr);
- videoWidth = sz.cx;
- videoHeight = sz.cy;
- return hr;
- }
-
- ComSmartPtr<IMFVideoDisplayControl> videoDisplayControl;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (EVR)
- };
- }
-
- //==============================================================================
- struct VideoComponent::Pimpl : public Component,
- private ComponentPeer::ScaleFactorListener
- {
- Pimpl (VideoComponent& ownerToUse, bool)
- : owner (ownerToUse)
- {
- setOpaque (true);
- context.reset (new DirectShowContext (*this));
- componentWatcher.reset (new ComponentWatcher (*this));
- }
-
- ~Pimpl() override
- {
- close();
- context = nullptr;
- componentWatcher = nullptr;
-
- if (currentPeer != nullptr)
- currentPeer->removeScaleFactorListener (this);
- }
-
- Result loadFromString (const String& fileOrURLPath)
- {
- close();
- auto r = context->loadFile (fileOrURLPath);
-
- if (r.wasOk())
- {
- videoLoaded = true;
- context->updateVideoPosition();
- }
-
- return r;
- }
-
- Result load (const File& file)
- {
- auto r = loadFromString (file.getFullPathName());
-
- if (r.wasOk())
- currentFile = file;
-
- return r;
- }
-
- Result load (const URL& url)
- {
- auto r = loadFromString (URL::removeEscapeChars (url.toString (true)));
-
- if (r.wasOk())
- currentURL = url;
-
- return r;
- }
-
- void close()
- {
- stop();
- context->release();
-
- videoLoaded = false;
- currentFile = File();
- currentURL = {};
- }
-
- bool isOpen() const
- {
- return videoLoaded;
- }
-
- bool isPlaying() const
- {
- return context->state == DirectShowContext::runningState;
- }
-
- void play()
- {
- if (videoLoaded)
- context->play();
- }
-
- void stop()
- {
- if (videoLoaded)
- context->pause();
- }
-
- void setPosition (double newPosition)
- {
- if (videoLoaded)
- context->setPosition (newPosition);
- }
-
- double getPosition() const
- {
- return videoLoaded ? context->getPosition() : 0.0;
- }
-
- void setSpeed (double newSpeed)
- {
- if (videoLoaded)
- context->setSpeed (newSpeed);
- }
-
- double getSpeed() const
- {
- return videoLoaded ? context->getSpeed() : 0.0;
- }
-
- Rectangle<int> getNativeSize() const
- {
- return videoLoaded ? context->getVideoSize()
- : Rectangle<int>();
- }
-
- double getDuration() const
- {
- return videoLoaded ? context->getDuration() : 0.0;
- }
-
- void setVolume (float newVolume)
- {
- if (videoLoaded)
- context->setVolume (newVolume);
- }
-
- float getVolume() const
- {
- return videoLoaded ? context->getVolume() : 0.0f;
- }
-
- void paint (Graphics& g) override
- {
- if (videoLoaded)
- context->handleUpdateNowIfNeeded();
- else
- g.fillAll (Colours::grey);
- }
-
- void updateContextPosition()
- {
- context->updateContextPosition();
-
- if (getWidth() > 0 && getHeight() > 0)
- if (auto* peer = getTopLevelComponent()->getPeer())
- context->updateWindowPosition ((peer->getAreaCoveredBy (*this).toDouble()
- * peer->getPlatformScaleFactor()).toNearestInt());
- }
-
- void updateContextVisibility()
- {
- context->showWindow (isShowing());
- }
-
- void recreateNativeWindowAsync()
- {
- context->recreateNativeWindowAsync();
- repaint();
- }
-
- void playbackStarted()
- {
- if (owner.onPlaybackStarted != nullptr)
- owner.onPlaybackStarted();
- }
-
- void playbackStopped()
- {
- if (owner.onPlaybackStopped != nullptr)
- owner.onPlaybackStopped();
- }
-
- void errorOccurred (const String& errorMessage)
- {
- if (owner.onErrorOccurred != nullptr)
- owner.onErrorOccurred (errorMessage);
- }
-
- File currentFile;
- URL currentURL;
-
- private:
- VideoComponent& owner;
- ComponentPeer* currentPeer = nullptr;
- bool videoLoaded = false;
-
- //==============================================================================
- void nativeScaleFactorChanged (double /*newScaleFactor*/) override
- {
- if (videoLoaded)
- updateContextPosition();
- }
-
- //==============================================================================
- struct ComponentWatcher : public ComponentMovementWatcher
- {
- ComponentWatcher (Pimpl& c) : ComponentMovementWatcher (&c), owner (c)
- {
- }
-
- void componentMovedOrResized (bool, bool) override
- {
- if (owner.videoLoaded)
- owner.updateContextPosition();
- }
-
- void componentPeerChanged() override
- {
- if (owner.currentPeer != nullptr)
- owner.currentPeer->removeScaleFactorListener (&owner);
-
- if (owner.videoLoaded)
- owner.recreateNativeWindowAsync();
- }
-
- void componentVisibilityChanged() override
- {
- if (owner.videoLoaded)
- owner.updateContextVisibility();
- }
-
- Pimpl& owner;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (ComponentWatcher)
- };
-
- std::unique_ptr<ComponentWatcher> componentWatcher;
-
- //==============================================================================
- struct DirectShowContext : public AsyncUpdater
- {
- DirectShowContext (Pimpl& c) : component (c)
- {
- ignoreUnused (CoInitialize (nullptr));
- }
-
- ~DirectShowContext() override
- {
- release();
- CoUninitialize();
- }
-
- //==============================================================================
- void updateWindowPosition (const Rectangle<int>& newBounds)
- {
- nativeWindow->setWindowPosition (newBounds);
- }
-
- void showWindow (bool shouldBeVisible)
- {
- nativeWindow->showWindow (shouldBeVisible);
- }
-
- //==============================================================================
- void repaint()
- {
- if (hasVideo)
- videoRenderer->repaintVideo (nativeWindow->hwnd, nativeWindow->hdc);
- }
-
- void updateVideoPosition()
- {
- if (hasVideo)
- videoRenderer->setVideoPosition (nativeWindow->hwnd);
- }
-
- void displayResolutionChanged()
- {
- if (hasVideo)
- videoRenderer->displayModeChanged();
- }
-
- //==============================================================================
- void peerChanged()
- {
- deleteNativeWindow();
-
- mediaEvent->SetNotifyWindow (0, 0, 0);
-
- if (videoRenderer != nullptr)
- videoRenderer->setVideoWindow (nullptr);
-
- createNativeWindow();
-
- mediaEvent->CancelDefaultHandling (EC_STATE_CHANGE);
- mediaEvent->SetNotifyWindow ((OAHWND) hwnd, graphEventID, 0);
-
- if (videoRenderer != nullptr)
- videoRenderer->setVideoWindow (hwnd);
- }
-
- void handleAsyncUpdate() override
- {
- if (hwnd != nullptr)
- {
- if (needToRecreateNativeWindow)
- {
- peerChanged();
- needToRecreateNativeWindow = false;
- }
-
- if (needToUpdateViewport)
- {
- updateVideoPosition();
- needToUpdateViewport = false;
- }
-
- repaint();
- }
- else
- {
- triggerAsyncUpdate();
- }
- }
-
- void recreateNativeWindowAsync()
- {
- needToRecreateNativeWindow = true;
- triggerAsyncUpdate();
- }
-
- void updateContextPosition()
- {
- needToUpdateViewport = true;
- triggerAsyncUpdate();
- }
-
- //==============================================================================
- Result loadFile (const String& fileOrURLPath)
- {
- jassert (state == uninitializedState);
-
- if (! createNativeWindow())
- return Result::fail ("Can't create window");
-
- HRESULT hr = graphBuilder.CoCreateInstance (CLSID_FilterGraph);
-
- // basic playback interfaces
- if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (mediaControl);
- if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (mediaPosition);
- if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (mediaEvent);
- if (SUCCEEDED (hr)) hr = graphBuilder.QueryInterface (basicAudio);
-
- // video renderer interface
- if (SUCCEEDED (hr))
- {
- if (SystemStats::getOperatingSystemType() >= SystemStats::WinVista)
- {
- videoRenderer.reset (new VideoRenderers::EVR());
- hr = videoRenderer->create (graphBuilder, baseFilter, hwnd);
-
- if (FAILED (hr))
- videoRenderer = nullptr;
- }
-
- if (videoRenderer == nullptr)
- {
- videoRenderer.reset (new VideoRenderers::VMR7());
- hr = videoRenderer->create (graphBuilder, baseFilter, hwnd);
- }
- }
-
- // build filter graph
- if (SUCCEEDED (hr))
- {
- hr = graphBuilder->RenderFile (fileOrURLPath.toWideCharPointer(), nullptr);
-
- if (FAILED (hr))
- {
- #if JUCE_MODAL_LOOPS_PERMITTED
- // Annoyingly, if we don't run the msg loop between failing and deleting the window, the
- // whole OS message-dispatch system gets itself into a state, and refuses to deliver any
- // more messages for the whole app. (That's what happens in Win7, anyway)
- MessageManager::getInstance()->runDispatchLoopUntil (200);
- #endif
- }
- }
-
- // remove video renderer if not connected (no video)
- if (SUCCEEDED (hr))
- {
- if (isRendererConnected())
- {
- hasVideo = true;
- }
- else
- {
- hasVideo = false;
- graphBuilder->RemoveFilter (baseFilter);
- videoRenderer = nullptr;
- baseFilter = nullptr;
- }
- }
-
- // set window to receive events
- if (SUCCEEDED (hr))
- {
- mediaEvent->CancelDefaultHandling (EC_STATE_CHANGE);
- hr = mediaEvent->SetNotifyWindow ((OAHWND) hwnd, graphEventID, 0);
- }
-
- if (SUCCEEDED (hr))
- {
- state = stoppedState;
- pause();
- return Result::ok();
- }
-
- // Note that if you're trying to open a file and this method fails, you may
- // just need to install a suitable codec. It seems that by default DirectShow
- // doesn't support a very good range of formats.
- release();
- return getErrorMessageFromResult (hr);
- }
-
- static Result getErrorMessageFromResult (HRESULT hr)
- {
- switch (hr)
- {
- case VFW_E_INVALID_FILE_FORMAT: return Result::fail ("Invalid file format");
- case VFW_E_NOT_FOUND: return Result::fail ("File not found");
- case VFW_E_UNKNOWN_FILE_TYPE: return Result::fail ("Unknown file type");
- case VFW_E_UNSUPPORTED_STREAM: return Result::fail ("Unsupported stream");
- case VFW_E_CANNOT_CONNECT: return Result::fail ("Cannot connect");
- case VFW_E_CANNOT_LOAD_SOURCE_FILTER: return Result::fail ("Cannot load source filter");
- }
-
- TCHAR messageBuffer[512] = { 0 };
-
- FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- nullptr, hr, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
- messageBuffer, (DWORD) numElementsInArray (messageBuffer) - 1, nullptr);
-
- return Result::fail (String (messageBuffer));
- }
-
- void release()
- {
- if (mediaControl != nullptr)
- mediaControl->Stop();
-
- if (mediaEvent != nullptr)
- mediaEvent->SetNotifyWindow (0, 0, 0);
-
- if (videoRenderer != nullptr)
- videoRenderer->setVideoWindow (nullptr);
-
- hasVideo = false;
- videoRenderer = nullptr;
- baseFilter = nullptr;
- basicAudio = nullptr;
- mediaEvent = nullptr;
- mediaPosition = nullptr;
- mediaControl = nullptr;
- graphBuilder = nullptr;
-
- state = uninitializedState;
-
- if (nativeWindow != nullptr)
- deleteNativeWindow();
- }
-
- void graphEventProc()
- {
- LONG ec = 0;
- LONG_PTR p1 = {}, p2 = {};
-
- jassert (mediaEvent != nullptr);
-
- while (SUCCEEDED (mediaEvent->GetEvent (&ec, &p1, &p2, 0)))
- {
- mediaEvent->FreeEventParams (ec, p1, p2);
-
- switch (ec)
- {
- case EC_REPAINT:
- component.repaint();
- break;
-
- case EC_COMPLETE:
- component.stop();
- component.setPosition (0.0);
- break;
-
- case EC_ERRORABORT:
- case EC_ERRORABORTEX:
- component.errorOccurred (getErrorMessageFromResult ((HRESULT) p1).getErrorMessage());
- // intentional fallthrough
- case EC_USERABORT:
- component.close();
- break;
-
- case EC_STATE_CHANGE:
- switch (p1)
- {
- case State_Paused: component.playbackStopped(); break;
- case State_Running: component.playbackStarted(); break;
- default: break;
- }
-
- default:
- break;
- }
- }
- }
-
- //==============================================================================
- void play()
- {
- mediaControl->Run();
- state = runningState;
- }
-
- void stop()
- {
- mediaControl->Stop();
- state = stoppedState;
- }
-
- void pause()
- {
- mediaControl->Pause();
- state = pausedState;
- }
-
- //==============================================================================
- Rectangle<int> getVideoSize() const noexcept
- {
- long width = 0, height = 0;
-
- if (hasVideo)
- videoRenderer->getVideoSize (width, height);
-
- return { (int) width, (int) height };
- }
-
- //==============================================================================
- double getDuration() const
- {
- REFTIME duration;
- mediaPosition->get_Duration (&duration);
- return duration;
- }
-
- double getSpeed() const
- {
- double speed;
- mediaPosition->get_Rate (&speed);
- return speed;
- }
-
- double getPosition() const
- {
- REFTIME seconds;
- mediaPosition->get_CurrentPosition (&seconds);
- return seconds;
- }
-
- void setSpeed (double newSpeed) { mediaPosition->put_Rate (newSpeed); }
- void setPosition (double seconds) { mediaPosition->put_CurrentPosition (seconds); }
- void setVolume (float newVolume) { basicAudio->put_Volume (convertToDShowVolume (newVolume)); }
-
- // in DirectShow, full volume is 0, silence is -10000
- static long convertToDShowVolume (float vol) noexcept
- {
- if (vol >= 1.0f) return 0;
- if (vol <= 0.0f) return -10000;
-
- return roundToInt ((vol * 10000.0f) - 10000.0f);
- }
-
- float getVolume() const
- {
- long volume;
- basicAudio->get_Volume (&volume);
- return (float) (volume + 10000) / 10000.0f;
- }
-
- enum State { uninitializedState, runningState, pausedState, stoppedState };
- State state = uninitializedState;
-
- private:
- //==============================================================================
- enum { graphEventID = WM_APP + 0x43f0 };
-
- Pimpl& component;
- HWND hwnd = {};
- HDC hdc = {};
-
- ComSmartPtr<IGraphBuilder> graphBuilder;
- ComSmartPtr<IMediaControl> mediaControl;
- ComSmartPtr<IMediaPosition> mediaPosition;
- ComSmartPtr<IMediaEventEx> mediaEvent;
- ComSmartPtr<IBasicAudio> basicAudio;
- ComSmartPtr<IBaseFilter> baseFilter;
-
- std::unique_ptr<VideoRenderers::Base> videoRenderer;
-
- bool hasVideo = false, needToUpdateViewport = true, needToRecreateNativeWindow = false;
-
- //==============================================================================
- bool createNativeWindow()
- {
- jassert (nativeWindow == nullptr);
-
- if (auto* topLevelPeer = component.getTopLevelComponent()->getPeer())
- {
- nativeWindow.reset (new NativeWindow ((HWND) topLevelPeer->getNativeHandle(), this));
-
- hwnd = nativeWindow->hwnd;
- component.currentPeer = topLevelPeer;
- component.currentPeer->addScaleFactorListener (&component);
-
- if (hwnd != nullptr)
- {
- hdc = GetDC (hwnd);
- component.updateContextPosition();
- component.updateContextVisibility();
- return true;
- }
-
- nativeWindow = nullptr;
- }
- else
- {
- jassertfalse;
- }
-
- return false;
- }
-
- void deleteNativeWindow()
- {
- jassert (nativeWindow != nullptr);
- ReleaseDC (hwnd, hdc);
- hwnd = {};
- hdc = {};
- nativeWindow = nullptr;
- }
-
- bool isRendererConnected()
- {
- ComSmartPtr<IEnumPins> enumPins;
-
- HRESULT hr = baseFilter->EnumPins (enumPins.resetAndGetPointerAddress());
-
- if (SUCCEEDED (hr))
- hr = enumPins->Reset();
-
- ComSmartPtr<IPin> pin;
-
- while (SUCCEEDED (hr)
- && enumPins->Next (1, pin.resetAndGetPointerAddress(), nullptr) == S_OK)
- {
- ComSmartPtr<IPin> otherPin;
-
- hr = pin->ConnectedTo (otherPin.resetAndGetPointerAddress());
-
- if (SUCCEEDED (hr))
- {
- PIN_DIRECTION direction;
- hr = pin->QueryDirection (&direction);
-
- if (SUCCEEDED (hr) && direction == PINDIR_INPUT)
- return true;
- }
- else if (hr == VFW_E_NOT_CONNECTED)
- {
- hr = S_OK;
- }
- }
-
- return false;
- }
-
- //==============================================================================
- struct NativeWindowClass : private DeletedAtShutdown
- {
- bool isRegistered() const noexcept { return atom != 0; }
- LPCTSTR getWindowClassName() const noexcept { return (LPCTSTR) (pointer_sized_uint) MAKELONG (atom, 0); }
-
- JUCE_DECLARE_SINGLETON_SINGLETHREADED_MINIMAL (NativeWindowClass)
-
- private:
- NativeWindowClass()
- {
- String windowClassName ("JUCE_DIRECTSHOW_");
- windowClassName << (int) (Time::currentTimeMillis() & 0x7fffffff);
-
- HINSTANCE moduleHandle = (HINSTANCE) Process::getCurrentModuleInstanceHandle();
-
- TCHAR moduleFile [1024] = {};
- GetModuleFileName (moduleHandle, moduleFile, 1024);
-
- WNDCLASSEX wcex = {};
- wcex.cbSize = sizeof (wcex);
- wcex.style = CS_OWNDC;
- wcex.lpfnWndProc = (WNDPROC) wndProc;
- wcex.lpszClassName = windowClassName.toWideCharPointer();
- wcex.hInstance = moduleHandle;
-
- atom = RegisterClassEx (&wcex);
- jassert (atom != 0);
- }
-
- ~NativeWindowClass()
- {
- if (atom != 0)
- UnregisterClass (getWindowClassName(), (HINSTANCE) Process::getCurrentModuleInstanceHandle());
-
- clearSingletonInstance();
- }
-
- static LRESULT CALLBACK wndProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
- {
- if (auto* c = (DirectShowContext*) GetWindowLongPtr (hwnd, GWLP_USERDATA))
- {
- switch (msg)
- {
- case WM_NCHITTEST: return HTTRANSPARENT;
- case WM_ERASEBKGND: return 1;
- case WM_DISPLAYCHANGE: c->displayResolutionChanged(); break;
- case graphEventID: c->graphEventProc(); return 0;
- default: break;
- }
- }
-
- return DefWindowProc (hwnd, msg, wParam, lParam);
- }
-
- ATOM atom = {};
-
- JUCE_DECLARE_NON_COPYABLE (NativeWindowClass)
- };
-
- //==============================================================================
- struct NativeWindow
- {
- NativeWindow (HWND parentToAddTo, void* userData)
- {
- auto* wc = NativeWindowClass::getInstance();
-
- if (wc->isRegistered())
- {
- DWORD exstyle = 0;
- DWORD type = WS_CHILD;
-
- hwnd = CreateWindowEx (exstyle, wc->getWindowClassName(),
- L"", type, 0, 0, 0, 0, parentToAddTo, nullptr,
- (HINSTANCE) Process::getCurrentModuleInstanceHandle(), nullptr);
-
- if (hwnd != nullptr)
- {
- hdc = GetDC (hwnd);
- SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) userData);
- }
- }
-
- jassert (hwnd != nullptr);
- }
-
- ~NativeWindow()
- {
- if (hwnd != nullptr)
- {
- SetWindowLongPtr (hwnd, GWLP_USERDATA, (LONG_PTR) 0);
- DestroyWindow (hwnd);
- }
- }
-
- void setWindowPosition (Rectangle<int> newBounds)
- {
- SetWindowPos (hwnd, nullptr, newBounds.getX(), newBounds.getY(),
- newBounds.getWidth(), newBounds.getHeight(),
- SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER);
- }
-
- void showWindow (bool shouldBeVisible)
- {
- ShowWindow (hwnd, shouldBeVisible ? SW_SHOWNA : SW_HIDE);
- }
-
- HWND hwnd = {};
- HDC hdc = {};
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (NativeWindow)
- };
-
- std::unique_ptr<NativeWindow> nativeWindow;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (DirectShowContext)
- };
-
- std::unique_ptr<DirectShowContext> context;
-
- JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
- };
-
- JUCE_IMPLEMENT_SINGLETON (VideoComponent::Pimpl::DirectShowContext::NativeWindowClass)
|