diff options
author | Caolán McNamara <caolanm@redhat.com> | 2022-02-21 10:01:42 +0000 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2022-03-01 18:38:27 +0100 |
commit | 05db887bc226b85befe2c2b9e84b796020a6ca05 (patch) | |
tree | cdb86937b266212f6fae5f7d6e596f30350a49ad /avmedia | |
parent | ae071f7d680545e284e5947d26cbea30e59bdfb5 (diff) |
gtk4: media dimensions are only reliably available async
this is also the case for the direct gstreamer use in gtk3 but
more egregious when abstracted away from it in gtk4
Change-Id: If90069308d67929585722c079c482cd4ad196e1f
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/130468
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
Diffstat (limited to 'avmedia')
-rw-r--r-- | avmedia/source/gtk/gtkplayer.cxx | 130 | ||||
-rw-r--r-- | avmedia/source/gtk/gtkplayer.hxx | 20 | ||||
-rw-r--r-- | avmedia/source/viewer/mediawindow.cxx | 127 |
3 files changed, 224 insertions, 53 deletions
diff --git a/avmedia/source/gtk/gtkplayer.cxx b/avmedia/source/gtk/gtkplayer.cxx index 7ce488f231ec..b988e7f19684 100644 --- a/avmedia/source/gtk/gtkplayer.cxx +++ b/avmedia/source/gtk/gtkplayer.cxx @@ -37,8 +37,12 @@ namespace avmedia::gtk { GtkPlayer::GtkPlayer() : GtkPlayer_BASE(m_aMutex) + , m_lListener(m_aMutex) , m_pStream(nullptr) , m_pVideo(nullptr) + , m_nNotifySignalId(0) + , m_nInvalidateSizeSignalId(0) + , m_nTimeoutId(0) , m_nUnmutedVolume(0) { } @@ -61,6 +65,8 @@ void GtkPlayer::cleanup() if (m_pStream) { + uninstallNotify(); + // shouldn't have to attempt this unref on idle, but with gtk4-4.4.1 I get // intermittent "instance of invalid non-instantiatable type '(null)'" // on some mysterious gst dbus callback @@ -81,6 +87,51 @@ void SAL_CALL GtkPlayer::disposing() cleanup(); } +static void do_notify(GtkPlayer* pThis) +{ + rtl::Reference<GtkPlayer> xThis(pThis); + xThis->notifyListeners(); + xThis->uninstallNotify(); +} + +static void invalidate_size_cb(GdkPaintable* /*pPaintable*/, GtkPlayer* pThis) { do_notify(pThis); } + +static void notify_cb(GtkMediaStream* /*pStream*/, GParamSpec* pspec, GtkPlayer* pThis) +{ + if (g_str_equal(pspec->name, "prepared") || g_str_equal(pspec->name, "error")) + do_notify(pThis); +} + +static bool timeout_cb(GtkPlayer* pThis) +{ + do_notify(pThis); + return false; +} + +void GtkPlayer::installNotify() +{ + if (m_nNotifySignalId) + return; + m_nNotifySignalId = g_signal_connect(m_pStream, "notify", G_CALLBACK(notify_cb), this); + // notify should be enough, but there is an upstream bug so also try "invalidate-size" and add a timeout for + // audio-only case where that won't happen, see: https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/4513 + m_nInvalidateSizeSignalId + = g_signal_connect(m_pStream, "invalidate-size", G_CALLBACK(invalidate_size_cb), this); + m_nTimeoutId = g_timeout_add_seconds(10, G_SOURCE_FUNC(timeout_cb), this); +} + +void GtkPlayer::uninstallNotify() +{ + if (!m_nNotifySignalId) + return; + g_signal_handler_disconnect(m_pStream, m_nNotifySignalId); + m_nNotifySignalId = 0; + g_signal_handler_disconnect(m_pStream, m_nInvalidateSizeSignalId); + m_nInvalidateSizeSignalId = 0; + g_source_remove(m_nTimeoutId); + m_nTimeoutId = 0; +} + bool GtkPlayer::create(const OUString& rURL) { bool bRet = false; @@ -104,6 +155,25 @@ bool GtkPlayer::create(const OUString& rURL) return bRet; } +void GtkPlayer::notifyListeners() +{ + comphelper::OInterfaceContainerHelper2* pContainer + = m_lListener.getContainer(cppu::UnoType<css::media::XPlayerListener>::get()); + if (!pContainer) + return; + + css::lang::EventObject aEvent; + aEvent.Source = static_cast<cppu::OWeakObject*>(this); + + comphelper::OInterfaceIteratorHelper2 pIterator(*pContainer); + while (pIterator.hasMoreElements()) + { + css::uno::Reference<css::media::XPlayerListener> xListener( + static_cast<css::media::XPlayerListener*>(pIterator.next())); + xListener->preferredPlayerWindowSizeAvailable(aEvent); + } +} + void SAL_CALL GtkPlayer::start() { osl::MutexGuard aGuard(m_aMutex); @@ -231,41 +301,6 @@ sal_Int16 SAL_CALL GtkPlayer::getVolumeDB() return m_nUnmutedVolume; } -namespace -{ -void invalidate_size(GdkPaintable* /*paintable*/, Timer* pTimer) { pTimer->Stop(); } - -Size GetPreferredPlayerWindowSize(GdkPaintable* pStream) -{ - Size aSize(gdk_paintable_get_intrinsic_width(pStream), - gdk_paintable_get_intrinsic_height(pStream)); - - // This is pretty nasty, maybe for the XFrameGrabber case it could be - // possible to implement an XGraphic which can be updated when the - // information becomes available rather than explicitly wait for it here, - // but the getPreferredPlayerWindowSize problem would remain. - if (aSize.Width() == 0 && aSize.Height() == 0) - { - Timer aTimer("gtkplayer waiting to find out size"); - aTimer.SetTimeout(3000); - - gulong nSignalId - = g_signal_connect(pStream, "invalidate-size", G_CALLBACK(invalidate_size), &aTimer); - - aTimer.Start(); - while (aTimer.IsActive()) - Application::Yield(); - - g_signal_handler_disconnect(pStream, nSignalId); - - aSize = Size(gdk_paintable_get_intrinsic_width(pStream), - gdk_paintable_get_intrinsic_height(pStream)); - } - - return aSize; -} -} - awt::Size SAL_CALL GtkPlayer::getPreferredPlayerWindowSize() { osl::MutexGuard aGuard(m_aMutex); @@ -274,9 +309,8 @@ awt::Size SAL_CALL GtkPlayer::getPreferredPlayerWindowSize() if (m_pStream) { - Size aPrefSize = GetPreferredPlayerWindowSize(GDK_PAINTABLE(m_pStream)); - aSize.Width = aPrefSize.Width(); - aSize.Height = aPrefSize.Height(); + aSize.Width = gdk_paintable_get_intrinsic_width(GDK_PAINTABLE(m_pStream)); + aSize.Height = gdk_paintable_get_intrinsic_height(GDK_PAINTABLE(m_pStream)); } return aSize; @@ -325,6 +359,26 @@ uno::Reference<::media::XPlayerWindow> return xRet; } +void SAL_CALL +GtkPlayer::addPlayerListener(const css::uno::Reference<css::media::XPlayerListener>& rListener) +{ + m_lListener.addInterface(cppu::UnoType<css::media::XPlayerListener>::get(), rListener); + if (gtk_media_stream_is_prepared(m_pStream)) + { + css::lang::EventObject aEvent; + aEvent.Source = static_cast<cppu::OWeakObject*>(this); + rListener->preferredPlayerWindowSizeAvailable(aEvent); + } + else + installNotify(); +} + +void SAL_CALL +GtkPlayer::removePlayerListener(const css::uno::Reference<css::media::XPlayerListener>& rListener) +{ + m_lListener.removeInterface(cppu::UnoType<css::media::XPlayerListener>::get(), rListener); +} + namespace { class GtkFrameGrabber : public ::cppu::WeakImplHelper<css::media::XFrameGrabber> diff --git a/avmedia/source/gtk/gtkplayer.hxx b/avmedia/source/gtk/gtkplayer.hxx index 89f9355c94ae..46e416e79e25 100644 --- a/avmedia/source/gtk/gtkplayer.hxx +++ b/avmedia/source/gtk/gtkplayer.hxx @@ -15,6 +15,8 @@ #include <com/sun/star/lang/XServiceInfo.hpp> #include <com/sun/star/media/XPlayer.hpp> +#include <com/sun/star/media/XPlayerNotifier.hpp> +#include <comphelper/multicontainer2.hxx> #include <cppuhelper/compbase.hxx> #include <cppuhelper/basemutex.hxx> @@ -23,7 +25,9 @@ typedef struct _GtkWidget GtkWidget; namespace avmedia::gtk { -typedef cppu::WeakComponentImplHelper<css::media::XPlayer, css::lang::XServiceInfo> GtkPlayer_BASE; +typedef cppu::WeakComponentImplHelper<css::media::XPlayer, css::media::XPlayerNotifier, + css::lang::XServiceInfo> + GtkPlayer_BASE; class GtkPlayer final : public cppu::BaseMutex, public GtkPlayer_BASE { @@ -54,15 +58,29 @@ public: virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + virtual void SAL_CALL + addPlayerListener(const css::uno::Reference<css::media::XPlayerListener>& rListener) override; + virtual void SAL_CALL removePlayerListener( + const css::uno::Reference<css::media::XPlayerListener>& rListener) override; + virtual void SAL_CALL disposing() final override; + void notifyListeners(); + void installNotify(); + void uninstallNotify(); + private: void cleanup(); + comphelper::OMultiTypeInterfaceContainerHelper2 m_lListener; + OUString m_aURL; css::awt::Rectangle m_aArea; // Area of the player window. GtkMediaStream* m_pStream; GtkWidget* m_pVideo; + unsigned long m_nNotifySignalId; + unsigned long m_nInvalidateSizeSignalId; + unsigned long m_nTimeoutId; sal_Int16 m_nUnmutedVolume; }; diff --git a/avmedia/source/viewer/mediawindow.cxx b/avmedia/source/viewer/mediawindow.cxx index c251f020225e..f3c034146f1d 100644 --- a/avmedia/source/viewer/mediawindow.cxx +++ b/avmedia/source/viewer/mediawindow.cxx @@ -28,11 +28,16 @@ #include <vcl/weld.hxx> #include <sfx2/filedlghelper.hxx> #include <com/sun/star/awt/Size.hpp> +#include <com/sun/star/frame/XDispatchHelper.hpp> #include <com/sun/star/media/XPlayer.hpp> +#include <com/sun/star/media/XPlayerNotifier.hpp> #include <com/sun/star/ui/dialogs/ExtendedFilePickerElementIds.hpp> #include <com/sun/star/ui/dialogs/TemplateDescription.hpp> #include <com/sun/star/ui/dialogs/XFilePicker3.hpp> #include <com/sun/star/ui/dialogs/XFilePickerControlAccess.hpp> +#include <com/sun/star/util/URLTransformer.hpp> +#include <comphelper/processfactory.hxx> +#include <comphelper/propertysequence.hxx> #include <memory> #include <sal/log.hxx> @@ -296,14 +301,14 @@ void MediaWindow::executeFormatErrorBox(weld::Window* pParent) xBox->run(); } -bool MediaWindow::isMediaURL( const OUString& rURL, const OUString& rReferer, bool bDeep, Size* pPreferredSizePixel ) +bool MediaWindow::isMediaURL(const OUString& rURL, const OUString& rReferer, bool bDeep, rtl::Reference<PlayerListener> xPreferredPixelSizeListener) { const INetURLObject aURL( rURL ); if( aURL.GetProtocol() == INetProtocol::NotValid ) return false; - if( bDeep || pPreferredSizePixel ) + if (bDeep || xPreferredPixelSizeListener) { try { @@ -313,14 +318,20 @@ bool MediaWindow::isMediaURL( const OUString& rURL, const OUString& rReferer, bo if( xPlayer.is() ) { - if( pPreferredSizePixel ) + if (xPreferredPixelSizeListener) { - const awt::Size aAwtSize( xPlayer->getPreferredPlayerWindowSize() ); - - pPreferredSizePixel->setWidth( aAwtSize.Width ); - pPreferredSizePixel->setHeight( aAwtSize.Height ); + uno::Reference<media::XPlayerNotifier> xPlayerNotifier(xPlayer, css::uno::UNO_QUERY); + if (xPlayerNotifier) + { + // wait until its possible to query this to get a sensible answer + xPreferredPixelSizeListener->startListening(xPlayerNotifier); + } + else + { + // assume the size is possible to query immediately + xPreferredPixelSizeListener->callPlayerWindowSizeAvailable(xPlayer); + } } - return true; } } @@ -346,18 +357,13 @@ bool MediaWindow::isMediaURL( const OUString& rURL, const OUString& rReferer, bo return false; } - uno::Reference< media::XPlayer > MediaWindow::createPlayer( const OUString& rURL, const OUString& rReferer, const OUString* pMimeType ) { return priv::MediaWindowImpl::createPlayer( rURL, rReferer, pMimeType ); } - -uno::Reference< graphic::XGraphic > MediaWindow::grabFrame( const OUString& rURL, - const OUString& rReferer, - const OUString& sMimeType ) +uno::Reference< graphic::XGraphic > MediaWindow::grabFrame(const css::uno::Reference<css::media::XPlayer>& xPlayer) { - uno::Reference< media::XPlayer > xPlayer( createPlayer( rURL, rReferer, &sMimeType ) ); uno::Reference< graphic::XGraphic > xRet; std::unique_ptr< Graphic > xGraphic; @@ -399,6 +405,99 @@ uno::Reference< graphic::XGraphic > MediaWindow::grabFrame( const OUString& rURL return xRet; } +uno::Reference< graphic::XGraphic > MediaWindow::grabFrame(const OUString& rURL, + const OUString& rReferer, + const OUString& sMimeType, + rtl::Reference<PlayerListener> xPreferredPixelSizeListener) +{ + uno::Reference<media::XPlayer> xPlayer(createPlayer(rURL, rReferer, &sMimeType)); + + if (xPreferredPixelSizeListener) + { + uno::Reference<media::XPlayerNotifier> xPlayerNotifier(xPlayer, css::uno::UNO_QUERY); + if (xPlayerNotifier) + { + // set a callback to call when a more sensible result is available, which + // might be called immediately if already available + xPreferredPixelSizeListener->startListening(xPlayerNotifier); + } + else + { + // assume the size is possible to query immediately + xPreferredPixelSizeListener->callPlayerWindowSizeAvailable(xPlayer); + } + + return nullptr; + } + + return grabFrame(xPlayer); +} + +void MediaWindow::dispatchInsertAVMedia(const css::uno::Reference<css::frame::XDispatchProvider>& rDispatchProvider, + const css::awt::Size& rSize, const OUString& rURL, bool bLink) +{ + util::URL aDispatchURL; + aDispatchURL.Complete = ".uno:InsertAVMedia"; + + css::uno::Reference<css::util::XURLTransformer> xTrans(css::util::URLTransformer::create(::comphelper::getProcessComponentContext())); + xTrans->parseStrict(aDispatchURL); + + css::uno::Reference<css::frame::XDispatch> xDispatch = rDispatchProvider->queryDispatch(aDispatchURL, "", 0); + css::uno::Sequence<css::beans::PropertyValue> aArgs(comphelper::InitPropertySequence({ + { "URL", css::uno::makeAny(rURL) }, + { "Size.Width", uno::makeAny(rSize.Width)}, + { "Size.Height", uno::makeAny(rSize.Height)}, + { "IsLink", css::uno::makeAny(bLink) }, + })); + xDispatch->dispatch(aDispatchURL, aArgs); +} + +PlayerListener::PlayerListener(const std::function<void(const css::uno::Reference<css::media::XPlayer>&)> &rFn) + : PlayerListener_BASE(m_aMutex) + , m_aFn(rFn) +{ +} + +void PlayerListener::dispose() +{ + stopListening(); + PlayerListener_BASE::dispose(); +} + +void PlayerListener::startListening(const css::uno::Reference<media::XPlayerNotifier>& rNotifier) +{ + osl::MutexGuard aGuard(m_aMutex); + + m_xNotifier = rNotifier; + m_xNotifier->addPlayerListener(this); +} + +void PlayerListener::stopListening() +{ + osl::MutexGuard aGuard(m_aMutex); + if (!m_xNotifier) + return; + m_xNotifier->removePlayerListener(this); + m_xNotifier.clear(); +} + +void SAL_CALL PlayerListener::preferredPlayerWindowSizeAvailable(const css::lang::EventObject&) +{ + osl::MutexGuard aGuard(m_aMutex); + + css::uno::Reference<media::XPlayer> xPlayer(m_xNotifier, css::uno::UNO_QUERY_THROW); + callPlayerWindowSizeAvailable(xPlayer); + + stopListening(); +} + +void SAL_CALL PlayerListener::disposing(const css::lang::EventObject&) +{ +} + +PlayerListener::~PlayerListener() +{ +} } // namespace avmedia |