summaryrefslogtreecommitdiff
path: root/avmedia
diff options
context:
space:
mode:
authorMichael Weghorn <m.weghorn@posteo.de>2024-05-30 13:24:02 +0200
committerMichael Weghorn <m.weghorn@posteo.de>2024-05-31 09:24:01 +0200
commitf04e711ea34ad3ad5ee642047be5d56a18fa0b53 (patch)
treeda72793b4016a307acb2355a77877a4f01af1049 /avmedia
parent2269b418f7af598ff8194acb9929c8bd6c4baeb1 (diff)
tdf#145735 qt avmedia: Implement frame grabber
Add a new `QtFrameGrabber` class that implements the `css::media::XFrameGrabber` interface and return an instance of it in `QtPlayer::createFrameGrabber`. As there seems to be no direct way to retrieve the image/frame of a video at a certain point in time, create a separate `QMediaPlayer` instance for the `QtFrameGrabber`, set a video sink, connect to its `&QVideoSink::videoFrameChanged` signal and start playing the video (and stop it again once the first frame has been received) in order to retrieve a corresponding frame. From that `QVideoFrame`, a `QImage` can be retrieved that can then be converted to an `XGraphic`. With this in place, a frame from the actual video is now displaced in Impress in non-presentation mode instead of just a generic "video icon" placeholder when opening a presentation containing a video, (e.g. attachment 145517 from tdf#120452) when the qt6 VCL plugin is in use. Change-Id: I3bba3c0fb62a219ac632ceed03ec17f9078f18d0 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/168255 Tested-by: Jenkins Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
Diffstat (limited to 'avmedia')
-rw-r--r--avmedia/CustomTarget_avmediaqt6_moc.mk1
-rw-r--r--avmedia/Library_avmediaqt6.mk1
-rw-r--r--avmedia/source/qt6/QtFrameGrabber.cxx106
-rw-r--r--avmedia/source/qt6/QtFrameGrabber.hxx50
-rw-r--r--avmedia/source/qt6/QtPlayer.cxx9
5 files changed, 166 insertions, 1 deletions
diff --git a/avmedia/CustomTarget_avmediaqt6_moc.mk b/avmedia/CustomTarget_avmediaqt6_moc.mk
index 0f9ca1af23b6..6cce5d258323 100644
--- a/avmedia/CustomTarget_avmediaqt6_moc.mk
+++ b/avmedia/CustomTarget_avmediaqt6_moc.mk
@@ -10,6 +10,7 @@
$(eval $(call gb_CustomTarget_CustomTarget,avmedia/source/qt6))
$(call gb_CustomTarget_get_target,avmedia/source/qt6) : \
+ $(gb_CustomTarget_workdir)/avmedia/source/qt6/QtFrameGrabber.moc \
$(gb_CustomTarget_workdir)/avmedia/source/qt6/QtPlayer.moc
diff --git a/avmedia/Library_avmediaqt6.mk b/avmedia/Library_avmediaqt6.mk
index a1acb7568d02..86b4553e1add 100644
--- a/avmedia/Library_avmediaqt6.mk
+++ b/avmedia/Library_avmediaqt6.mk
@@ -37,6 +37,7 @@ $(eval $(call gb_Library_use_libraries,avmediaqt6,\
$(eval $(call gb_Library_add_exception_objects,avmediaqt6,\
avmedia/source/qt6/gstwindow \
+ avmedia/source/qt6/QtFrameGrabber \
avmedia/source/qt6/QtManager \
avmedia/source/qt6/QtPlayer \
))
diff --git a/avmedia/source/qt6/QtFrameGrabber.cxx b/avmedia/source/qt6/QtFrameGrabber.cxx
new file mode 100644
index 000000000000..58726d649af0
--- /dev/null
+++ b/avmedia/source/qt6/QtFrameGrabber.cxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <QtCore/QBuffer>
+#include <QtCore/QByteArray>
+
+#include <sal/log.hxx>
+#include <vcl/filter/PngImageReader.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/image.hxx>
+#include <vcl/scheduler.hxx>
+
+#include "QtFrameGrabber.hxx"
+#include <QtFrameGrabber.moc>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+inline OUString toOUString(const QString& s)
+{
+ return OUString(reinterpret_cast<const sal_Unicode*>(s.data()), s.length());
+}
+
+uno::Reference<css::graphic::XGraphic> toXGraphic(const QImage& rImage)
+{
+ QByteArray aData;
+ QBuffer aBuffer(&aData);
+ rImage.save(&aBuffer, "PNG");
+
+ SvMemoryStream aStream(aData.data(), aData.size(), StreamMode::READ);
+ vcl::PngImageReader aReader(aStream);
+ Graphic aGraphic;
+ aReader.read(aGraphic);
+
+ return aGraphic.GetXGraphic();
+}
+}
+
+namespace avmedia::qt
+{
+QtFrameGrabber::QtFrameGrabber(const QUrl& rSourceUrl)
+ : m_bWaitingForFrame(false)
+{
+ m_xMediaPlayer = std::make_unique<QMediaPlayer>();
+ m_xMediaPlayer->setSource(rSourceUrl);
+
+ m_xVideoSink = std::make_unique<QVideoSink>();
+ m_xMediaPlayer->setVideoSink(m_xVideoSink.get());
+
+ connect(m_xMediaPlayer.get(), &QMediaPlayer::errorOccurred, this,
+ &QtFrameGrabber::onErrorOccured, Qt::BlockingQueuedConnection);
+}
+
+void QtFrameGrabber::onErrorOccured(QMediaPlayer::Error eError, const QString& rErrorString)
+{
+ std::lock_guard aLock(m_aMutex);
+
+ SAL_WARN("avmedia", "Media playback error occured when trying to grab frame: "
+ << toOUString(rErrorString) << ", code: " << eError);
+
+ m_bWaitingForFrame = false;
+}
+
+void QtFrameGrabber::onVideoFrameChanged(const QVideoFrame& rFrame)
+{
+ std::lock_guard aLock(m_aMutex);
+
+ disconnect(m_xVideoSink.get(), &QVideoSink::videoFrameChanged, this,
+ &QtFrameGrabber::onVideoFrameChanged);
+
+ const QImage aImage = rFrame.toImage();
+ m_xGraphic = toXGraphic(aImage);
+ m_bWaitingForFrame = false;
+}
+
+css::uno::Reference<css::graphic::XGraphic> SAL_CALL QtFrameGrabber::grabFrame(double fMediaTime)
+{
+ std::lock_guard aLock(m_aMutex);
+
+ m_xMediaPlayer->setPosition(fMediaTime * 1000);
+
+ // in order to get a video frame, connect to videoFrameChanged signal and start playing
+ // until the first frame has been received
+ m_bWaitingForFrame = true;
+ connect(m_xVideoSink.get(), &QVideoSink::videoFrameChanged, this,
+ &QtFrameGrabber::onVideoFrameChanged, Qt::BlockingQueuedConnection);
+ m_xMediaPlayer->play();
+ while (m_bWaitingForFrame)
+ Scheduler::ProcessEventsToIdle();
+ m_xMediaPlayer->stop();
+
+ uno::Reference<css::graphic::XGraphic> xGraphic = m_xGraphic;
+ m_xGraphic.clear();
+ return xGraphic;
+}
+
+}; // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/avmedia/source/qt6/QtFrameGrabber.hxx b/avmedia/source/qt6/QtFrameGrabber.hxx
new file mode 100644
index 000000000000..125ec74fc381
--- /dev/null
+++ b/avmedia/source/qt6/QtFrameGrabber.hxx
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#pragma once
+
+#include <mutex>
+
+#include <QtCore/QObject>
+#include <QtMultimedia/QMediaPlayer>
+#include <QtMultimedia/QVideoFrame>
+#include <QtMultimedia/QVideoSink>
+
+#include <com/sun/star/graphic/XGraphic.hpp>
+#include <com/sun/star/media/XFrameGrabber.hpp>
+#include <comphelper/compbase.hxx>
+
+namespace avmedia::qt
+{
+class QtFrameGrabber : public QObject, public ::cppu::WeakImplHelper<css::media::XFrameGrabber>
+{
+ Q_OBJECT
+
+private:
+ std::unique_ptr<QVideoSink> m_xVideoSink;
+ std::unique_ptr<QMediaPlayer> m_xMediaPlayer;
+
+ std::recursive_mutex m_aMutex;
+ bool m_bWaitingForFrame;
+ css::uno::Reference<css::graphic::XGraphic> m_xGraphic;
+
+public:
+ QtFrameGrabber(const QUrl& rSourceUrl);
+
+ virtual css::uno::Reference<css::graphic::XGraphic>
+ SAL_CALL grabFrame(double fMediaTime) override;
+
+private slots:
+ void onErrorOccured(QMediaPlayer::Error eError, const QString& rErrorString);
+ void onVideoFrameChanged(const QVideoFrame& rFrame);
+};
+
+} // namespace avmedia::qt
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/avmedia/source/qt6/QtPlayer.cxx b/avmedia/source/qt6/QtPlayer.cxx
index 5f75394ff1a3..d5291d5b0d5a 100644
--- a/avmedia/source/qt6/QtPlayer.cxx
+++ b/avmedia/source/qt6/QtPlayer.cxx
@@ -27,6 +27,7 @@
#include <vcl/timer.hxx>
#include <gstwindow.hxx>
+#include "QtFrameGrabber.hxx"
#include "QtPlayer.hxx"
#include <QtPlayer.moc>
@@ -229,7 +230,13 @@ uno::Reference<::media::XPlayerWindow>
return xRet;
}
-uno::Reference<media::XFrameGrabber> SAL_CALL QtPlayer::createFrameGrabber() { return nullptr; }
+uno::Reference<media::XFrameGrabber> SAL_CALL QtPlayer::createFrameGrabber()
+{
+ osl::MutexGuard aGuard(m_aMutex);
+
+ rtl::Reference<QtFrameGrabber> xFrameGrabber = new QtFrameGrabber(m_xMediaPlayer->source());
+ return xFrameGrabber;
+}
void SAL_CALL
QtPlayer::addPlayerListener(const css::uno::Reference<css::media::XPlayerListener>& rListener)