summaryrefslogtreecommitdiffstats
path: root/avmedia/source/gstreamer
diff options
context:
space:
mode:
Diffstat (limited to 'avmedia/source/gstreamer')
-rw-r--r--avmedia/source/gstreamer/avmediagstreamer.component16
-rw-r--r--avmedia/source/gstreamer/gstcommon.hxx43
-rw-r--r--avmedia/source/gstreamer/gstframegrabber.cxx175
-rw-r--r--avmedia/source/gstreamer/gstframegrabber.hxx65
-rw-r--r--avmedia/source/gstreamer/gstmanager.cxx75
-rw-r--r--avmedia/source/gstreamer/gstmanager.hxx47
-rw-r--r--avmedia/source/gstreamer/gstplayer.cxx946
-rw-r--r--avmedia/source/gstreamer/gstplayer.hxx110
-rw-r--r--avmedia/source/gstreamer/gstwindow.cxx192
-rw-r--r--avmedia/source/gstreamer/gstwindow.hxx82
10 files changed, 1751 insertions, 0 deletions
diff --git a/avmedia/source/gstreamer/avmediagstreamer.component b/avmedia/source/gstreamer/avmediagstreamer.component
new file mode 100644
index 000000000..cdc308d8b
--- /dev/null
+++ b/avmedia/source/gstreamer/avmediagstreamer.component
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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/.
+ *
+-->
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.comp.media.Manager_GStreamer"
+ constructor="com_sun_star_comp_media_Manager_GStreamer_get_implementation">
+ <service name="com.sun.star.comp.avmedia.Manager_GStreamer"/>
+ </implementation>
+</component>
diff --git a/avmedia/source/gstreamer/gstcommon.hxx b/avmedia/source/gstreamer/gstcommon.hxx
new file mode 100644
index 000000000..0e27907fb
--- /dev/null
+++ b/avmedia/source/gstreamer/gstcommon.hxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <gst/gst.h>
+
+#include <osl/mutex.hxx>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+#include <cppuhelper/weak.hxx>
+#include <cppuhelper/factory.hxx>
+
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/uno/RuntimeException.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/registry/XRegistryKey.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/awt/KeyModifier.hpp>
+#include <com/sun/star/awt/MouseButton.hpp>
+#include <com/sun/star/media/XManager.hpp>
+
+#define WM_GRAPHNOTIFY (WM_USER + 567)
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstframegrabber.cxx b/avmedia/source/gstreamer/gstframegrabber.cxx
new file mode 100644
index 000000000..ece799d87
--- /dev/null
+++ b/avmedia/source/gstreamer/gstframegrabber.cxx
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "gstframegrabber.hxx"
+#include "gstplayer.hxx"
+
+#include <cppuhelper/supportsservice.hxx>
+
+#include <gst/gstbuffer.h>
+#include <gst/video/video.h>
+#include <gst/video/gstvideosink.h>
+#include <o3tl/safeint.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/BitmapTools.hxx>
+
+#include <string>
+
+constexpr OUStringLiteral AVMEDIA_GST_FRAMEGRABBER_IMPLEMENTATIONNAME = u"com.sun.star.comp.avmedia.FrameGrabber_GStreamer";
+constexpr OUStringLiteral AVMEDIA_GST_FRAMEGRABBER_SERVICENAME = u"com.sun.star.media.FrameGrabber_GStreamer";
+
+using namespace ::com::sun::star;
+
+namespace avmedia::gstreamer {
+
+void FrameGrabber::disposePipeline()
+{
+ if( mpPipeline != nullptr )
+ {
+ gst_element_set_state( mpPipeline, GST_STATE_NULL );
+ g_object_unref( G_OBJECT( mpPipeline ) );
+ mpPipeline = nullptr;
+ }
+}
+
+FrameGrabber::FrameGrabber( std::u16string_view rURL )
+{
+ gchar *pPipelineStr;
+ pPipelineStr = g_strdup_printf(
+ "uridecodebin uri=%s ! videoconvert ! videoscale ! appsink "
+ "name=sink caps=\"video/x-raw,format=RGB,pixel-aspect-ratio=1/1\"",
+ OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 ).getStr() );
+
+ GError *pError = nullptr;
+ mpPipeline = gst_parse_launch( pPipelineStr, &pError );
+ if( pError != nullptr) {
+ g_warning( "Failed to construct frame-grabber pipeline '%s'\n", pError->message );
+ g_error_free( pError );
+ disposePipeline();
+ }
+
+ if( mpPipeline ) {
+ // pre-roll
+ switch( gst_element_set_state( mpPipeline, GST_STATE_PAUSED ) ) {
+ case GST_STATE_CHANGE_FAILURE:
+ case GST_STATE_CHANGE_NO_PREROLL:
+ g_warning( "failure pre-rolling media" );
+ disposePipeline();
+ break;
+ default:
+ break;
+ }
+ }
+ if( mpPipeline &&
+ gst_element_get_state( mpPipeline, nullptr, nullptr, 5 * GST_SECOND ) == GST_STATE_CHANGE_FAILURE )
+ disposePipeline();
+}
+
+FrameGrabber::~FrameGrabber()
+{
+ disposePipeline();
+}
+
+rtl::Reference<FrameGrabber> FrameGrabber::create( std::u16string_view rURL )
+{
+ return new FrameGrabber( rURL );
+}
+
+uno::Reference< graphic::XGraphic > SAL_CALL FrameGrabber::grabFrame( double fMediaTime )
+{
+ uno::Reference< graphic::XGraphic > xRet;
+
+ if( !mpPipeline )
+ return xRet;
+
+ gint64 gst_position = llround( fMediaTime * GST_SECOND );
+ gst_element_seek_simple(
+ mpPipeline, GST_FORMAT_TIME,
+ GstSeekFlags(GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_FLUSH),
+ gst_position );
+
+ GstElement *pSink = gst_bin_get_by_name( GST_BIN( mpPipeline ), "sink" );
+ if( !pSink )
+ return xRet;
+
+ GstBuffer *pBuf = nullptr;
+ GstCaps *pCaps = nullptr;
+
+ // synchronously fetch the frame
+ GstSample *pSample = nullptr;
+ g_signal_emit_by_name( pSink, "pull-preroll", &pSample, nullptr );
+
+ if( pSample )
+ {
+ pBuf = gst_sample_get_buffer( pSample );
+ pCaps = gst_sample_get_caps( pSample );
+ }
+
+ // get geometry
+ int nWidth = 0, nHeight = 0;
+ if( !pCaps )
+ g_warning( "could not get snapshot format\n" );
+ else
+ {
+ GstStructure *pStruct = gst_caps_get_structure( pCaps, 0 );
+
+ /* we need to get the final caps on the buffer to get the size */
+ if( !gst_structure_get_int( pStruct, "width", &nWidth ) ||
+ !gst_structure_get_int( pStruct, "height", &nHeight ) )
+ nWidth = nHeight = 0;
+ }
+
+ if( pBuf && nWidth > 0 && nHeight > 0 &&
+ // sanity check the size
+ gst_buffer_get_size( pBuf ) >= o3tl::make_unsigned( nWidth * nHeight * 3 )
+ )
+ {
+ sal_uInt8 *pData = nullptr;
+ GstMapInfo aMapInfo;
+ gst_buffer_map( pBuf, &aMapInfo, GST_MAP_READ );
+ pData = aMapInfo.data;
+
+ int nStride = GST_ROUND_UP_4( nWidth * 3 );
+ BitmapEx aBmp = vcl::bitmap::CreateFromData(pData, nWidth, nHeight, nStride, vcl::PixelFormat::N24_BPP);
+
+ gst_buffer_unmap( pBuf, &aMapInfo );
+ xRet = Graphic( aBmp ).GetXGraphic();
+ }
+
+ return xRet;
+}
+
+OUString SAL_CALL FrameGrabber::getImplementationName( )
+{
+ return AVMEDIA_GST_FRAMEGRABBER_IMPLEMENTATIONNAME;
+}
+
+sal_Bool SAL_CALL FrameGrabber::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL FrameGrabber::getSupportedServiceNames()
+{
+ return { AVMEDIA_GST_FRAMEGRABBER_SERVICENAME };
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstframegrabber.hxx b/avmedia/source/gstreamer/gstframegrabber.hxx
new file mode 100644
index 000000000..c706192ef
--- /dev/null
+++ b/avmedia/source/gstreamer/gstframegrabber.hxx
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "gstplayer.hxx"
+#include <com/sun/star/media/XFrameGrabber.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+
+namespace avmedia::gstreamer {
+
+typedef ::cppu::WeakImplHelper< css::media::XFrameGrabber,
+ css::lang::XServiceInfo > FrameGrabber_BASE;
+
+
+class FrameGrabber : public FrameGrabber_BASE
+{
+ GstElement *mpPipeline;
+ void disposePipeline();
+public:
+ // noncopyable
+ FrameGrabber(const FrameGrabber&) = delete;
+ const FrameGrabber& operator=(const FrameGrabber&) =delete;
+
+ // static create method instead of public Ctor
+ static rtl::Reference<FrameGrabber> create( std::u16string_view rURL );
+
+ virtual ~FrameGrabber() override;
+
+ // XFrameGrabber
+ virtual css::uno::Reference< css::graphic::XGraphic > SAL_CALL grabFrame( double fMediaTime ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+private:
+ explicit FrameGrabber( std::u16string_view aURL );
+};
+
+} // avmedia::gst
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstmanager.cxx b/avmedia/source/gstreamer/gstmanager.cxx
new file mode 100644
index 000000000..1803b5880
--- /dev/null
+++ b/avmedia/source/gstreamer/gstmanager.cxx
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cppuhelper/supportsservice.hxx>
+
+#include "gstmanager.hxx"
+#include "gstplayer.hxx"
+
+#include <tools/urlobj.hxx>
+#include <rtl/ref.hxx>
+
+using namespace ::com::sun::star;
+
+namespace avmedia::gstreamer {
+
+Manager::Manager()
+{
+}
+
+Manager::~Manager()
+{
+}
+
+uno::Reference< media::XPlayer > SAL_CALL Manager::createPlayer( const OUString& rURL )
+{
+ rtl::Reference<Player> pPlayer( new Player );
+ const INetURLObject aURL( rURL );
+
+ if( !pPlayer->create( aURL.GetMainURL( INetURLObject::DecodeMechanism::Unambiguous ) ) )
+ pPlayer.clear();
+
+ return pPlayer;
+}
+
+OUString SAL_CALL Manager::getImplementationName( )
+{
+ return "com.sun.star.comp.avmedia.Manager_GStreamer";
+}
+
+sal_Bool SAL_CALL Manager::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL Manager::getSupportedServiceNames( )
+{
+ return { "com.sun.star.media.Manager" };
+}
+
+} // namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+com_sun_star_comp_media_Manager_GStreamer_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new avmedia::gstreamer::Manager());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstmanager.hxx b/avmedia/source/gstreamer/gstmanager.hxx
new file mode 100644
index 000000000..21a5245dd
--- /dev/null
+++ b/avmedia/source/gstreamer/gstmanager.hxx
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include "gstcommon.hxx"
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/media/XManager.hpp>
+
+namespace avmedia::gstreamer {
+
+class Manager : public ::cppu::WeakImplHelper< css::media::XManager,
+ css::lang::XServiceInfo >
+{
+public:
+
+ explicit Manager();
+ virtual ~Manager() override;
+
+ // XManager
+ virtual css::uno::Reference< css::media::XPlayer > SAL_CALL createPlayer( const OUString& aURL ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+} // namespace avmedia::gstreamer
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstplayer.cxx b/avmedia/source/gstreamer/gstplayer.cxx
new file mode 100644
index 000000000..6884654b6
--- /dev/null
+++ b/avmedia/source/gstreamer/gstplayer.cxx
@@ -0,0 +1,946 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+#include <cassert>
+#include <chrono>
+#include <cstddef>
+#include <cstring>
+#include <map>
+#include <set>
+#include <vector>
+#include <math.h>
+
+#include <com/sun/star/text/GraphicCrop.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+#include <sal/log.hxx>
+#include <rtl/string.hxx>
+#include <salhelper/thread.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/syschild.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/graph.hxx>
+#include <avmedia/mediaitem.hxx>
+
+#include "gstplayer.hxx"
+#include "gstframegrabber.hxx"
+#include "gstwindow.hxx"
+
+#include <gst/video/videooverlay.h>
+#include <gst/pbutils/missing-plugins.h>
+#include <gst/pbutils/pbutils.h>
+
+constexpr OUStringLiteral AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME = u"com.sun.star.comp.avmedia.Player_GStreamer";
+constexpr OUStringLiteral AVMEDIA_GST_PLAYER_SERVICENAME = u"com.sun.star.media.Player_GStreamer";
+#define AVVERSION "gst 1.0: "
+
+using namespace ::com::sun::star;
+
+namespace avmedia::gstreamer {
+
+namespace {
+
+class FlagGuard {
+public:
+ explicit FlagGuard(bool & flag): flag_(flag) { flag_ = true; }
+
+ ~FlagGuard() { flag_ = false; }
+
+private:
+ bool & flag_;
+};
+
+class MissingPluginInstallerThread: public salhelper::Thread {
+public:
+ MissingPluginInstallerThread(): Thread("MissingPluginInstaller") {}
+
+private:
+ void execute() override;
+};
+
+
+class MissingPluginInstaller {
+ friend class MissingPluginInstallerThread;
+
+public:
+ MissingPluginInstaller(): launchNewThread_(true), inCleanUp_(false) {}
+
+ ~MissingPluginInstaller();
+
+ void report(rtl::Reference<Player> const & source, GstMessage * message);
+
+ // Player::~Player calls Player::disposing calls
+ // MissingPluginInstaller::detach, so do not take Player by rtl::Reference
+ // here (which would bump its refcount back from 0 to 1):
+ void detach(Player const * source);
+
+private:
+ void processQueue();
+
+ DECL_STATIC_LINK(MissingPluginInstaller, launchUi, void*, void);
+
+ osl::Mutex mutex_;
+ std::set<OString> reported_;
+ std::map<OString, std::set<rtl::Reference<Player>>> queued_;
+ rtl::Reference<MissingPluginInstallerThread> currentThread_;
+ std::vector<OString> currentDetails_;
+ std::set<rtl::Reference<Player>> currentSources_;
+ bool launchNewThread_;
+ bool inCleanUp_;
+};
+
+
+MissingPluginInstaller::~MissingPluginInstaller() {
+ osl::MutexGuard g(mutex_);
+ SAL_WARN_IF(currentThread_.is(), "avmedia.gstreamer", "unjoined thread");
+ inCleanUp_ = true;
+}
+
+
+void MissingPluginInstaller::report(
+ rtl::Reference<Player> const & source, GstMessage * message)
+{
+ // assert(gst_is_missing_plugin_message(message));
+ gchar * det = gst_missing_plugin_message_get_installer_detail(message);
+ if (det == nullptr) {
+ SAL_WARN(
+ "avmedia.gstreamer",
+ "gst_missing_plugin_message_get_installer_detail failed");
+ return;
+ }
+ std::size_t len = std::strlen(det);
+ if (len > SAL_MAX_INT32) {
+ SAL_WARN("avmedia.gstreamer", "detail string too long");
+ g_free(det);
+ return;
+ }
+ OString detStr(det, len);
+ g_free(det);
+ rtl::Reference<MissingPluginInstallerThread> join;
+ rtl::Reference<MissingPluginInstallerThread> launch;
+ {
+ osl::MutexGuard g(mutex_);
+ if (reported_.find(detStr) != reported_.end()) {
+ return;
+ }
+ auto & i = queued_[detStr];
+ bool fresh = i.empty();
+ i.insert(source);
+ if (!(fresh && launchNewThread_)) {
+ return;
+ }
+ join = currentThread_;
+ currentThread_ = new MissingPluginInstallerThread;
+ {
+ FlagGuard f(inCleanUp_);
+ currentSources_.clear();
+ }
+ processQueue();
+ launchNewThread_ = false;
+ launch = currentThread_;
+ }
+ if (join.is()) {
+ join->join();
+ }
+ launch->acquire();
+ Application::PostUserEvent(
+ LINK(this, MissingPluginInstaller, launchUi), launch.get());
+}
+
+
+void eraseSource(std::set<rtl::Reference<Player>> & set, Player const * source)
+{
+ auto i = std::find_if(
+ set.begin(), set.end(),
+ [source](rtl::Reference<Player> const & el) {
+ return el.get() == source;
+ });
+ if (i != set.end()) {
+ set.erase(i);
+ }
+}
+
+
+void MissingPluginInstaller::detach(Player const * source) {
+ rtl::Reference<MissingPluginInstallerThread> join;
+ {
+ osl::MutexGuard g(mutex_);
+ if (inCleanUp_) {
+ // Guard against ~MissingPluginInstaller with erroneously un-joined
+ // currentThread_ (thus non-empty currentSources_) calling
+ // destructor of currentSources_, calling ~Player, calling here,
+ // which would use currentSources_ while it is already being
+ // destroyed:
+ return;
+ }
+ for (auto i = queued_.begin(); i != queued_.end();) {
+ eraseSource(i->second, source);
+ if (i->second.empty()) {
+ i = queued_.erase(i);
+ } else {
+ ++i;
+ }
+ }
+ if (currentThread_.is()) {
+ assert(!currentSources_.empty());
+ eraseSource(currentSources_, source);
+ if (currentSources_.empty()) {
+ join = currentThread_;
+ currentThread_.clear();
+ launchNewThread_ = true;
+ }
+ }
+ }
+ if (join.is()) {
+ // missing cancellability of gst_install_plugins_sync
+ join->join();
+ }
+}
+
+
+void MissingPluginInstaller::processQueue() {
+ assert(!queued_.empty());
+ assert(currentDetails_.empty());
+ for (const auto& rEntry : queued_) {
+ reported_.insert(rEntry.first);
+ currentDetails_.push_back(rEntry.first);
+ currentSources_.insert(rEntry.second.begin(), rEntry.second.end());
+ }
+ queued_.clear();
+}
+
+
+IMPL_STATIC_LINK(MissingPluginInstaller, launchUi, void *, p, void)
+{
+ MissingPluginInstallerThread* thread = static_cast<MissingPluginInstallerThread*>(p);
+ rtl::Reference<MissingPluginInstallerThread> ref(thread, SAL_NO_ACQUIRE);
+ gst_pb_utils_init();
+ // not thread safe; hopefully fine to consistently call from our event
+ // loop (which is the only reason to have this
+ // Application::PostUserEvent diversion, in case
+ // MissingPluginInstaller::report might be called from outside our event
+ // loop), and hopefully fine to call gst_is_missing_plugin_message and
+ // gst_missing_plugin_message_get_installer_detail before calling
+ // gst_pb_utils_init
+ ref->launch();
+}
+
+
+struct TheMissingPluginInstaller:
+ public rtl::Static<MissingPluginInstaller, TheMissingPluginInstaller>
+{};
+
+
+void MissingPluginInstallerThread::execute() {
+ MissingPluginInstaller & inst = TheMissingPluginInstaller::get();
+ for (;;) {
+ std::vector<OString> details;
+ {
+ osl::MutexGuard g(inst.mutex_);
+ assert(!inst.currentDetails_.empty());
+ details.swap(inst.currentDetails_);
+ }
+ std::vector<char *> args;
+ args.reserve(details.size());
+ for (auto const& i : details)
+ {
+ args.push_back(const_cast<char *>(i.getStr()));
+ }
+ args.push_back(nullptr);
+ gst_install_plugins_sync(args.data(), nullptr);
+ {
+ osl::MutexGuard g(inst.mutex_);
+ if (inst.queued_.empty() || inst.launchNewThread_) {
+ inst.launchNewThread_ = true;
+ break;
+ }
+ inst.processQueue();
+ }
+ }
+}
+
+} // end anonymous namespace
+
+
+Player::Player() :
+ GstPlayer_BASE( m_aMutex ),
+ mpPlaybin( nullptr ),
+ mpVolumeControl( nullptr ),
+ mbUseGtkSink( false ),
+ mbFakeVideo (false ),
+ mnUnmutedVolume( 0 ),
+ mbMuted( false ),
+ mbLooping( false ),
+ mbInitialized( false ),
+ mpDisplay( nullptr ),
+ mnWindowID( 0 ),
+ mpXOverlay( nullptr ),
+ mnDuration( 0 ),
+ mnWidth( 0 ),
+ mnHeight( 0 ),
+ mnWatchID( 0 ),
+ mbWatchID( false )
+{
+ // Initialize GStreamer library
+ int argc = 1;
+ char name[] = "libreoffice";
+ char *arguments[] = { name };
+ char** argv = arguments;
+ GError* pError = nullptr;
+
+ mbInitialized = gst_init_check( &argc, &argv, &pError );
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::Player" );
+
+ if (pError != nullptr)
+ {
+ // TODO: throw an exception?
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::Player error '" << pError->message << "'" );
+ g_error_free (pError);
+ }
+}
+
+
+Player::~Player()
+{
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::~Player" );
+ if( mbInitialized )
+ disposing();
+}
+
+
+void SAL_CALL Player::disposing()
+{
+ TheMissingPluginInstaller::get().detach(this);
+
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ stop();
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::disposing" );
+
+ // Release the elements and pipeline
+ if( mbInitialized )
+ {
+ if( mpPlaybin )
+ {
+ gst_element_set_state( mpPlaybin, GST_STATE_NULL );
+ g_object_unref( G_OBJECT( mpPlaybin ) );
+
+ mpPlaybin = nullptr;
+ mpVolumeControl = nullptr;
+ }
+
+ if( mpXOverlay ) {
+ g_object_unref( G_OBJECT ( mpXOverlay ) );
+ mpXOverlay = nullptr;
+ }
+
+ }
+ if (mbWatchID)
+ {
+ g_source_remove(mnWatchID);
+ mbWatchID = false;
+ }
+}
+
+
+static gboolean pipeline_bus_callback( GstBus *, GstMessage *message, gpointer data )
+{
+ Player* pPlayer = static_cast<Player*>(data);
+
+ pPlayer->processMessage( message );
+
+ return true;
+}
+
+
+static GstBusSyncReply pipeline_bus_sync_handler( GstBus *, GstMessage * message, gpointer data )
+{
+ Player* pPlayer = static_cast<Player*>(data);
+
+ return pPlayer->processSyncMessage( message );
+}
+
+
+void Player::processMessage( GstMessage *message )
+{
+ switch( GST_MESSAGE_TYPE( message ) ) {
+ case GST_MESSAGE_EOS:
+ gst_element_set_state( mpPlaybin, GST_STATE_READY );
+ if (mbLooping)
+ start();
+ break;
+ case GST_MESSAGE_STATE_CHANGED:
+ if (message->src == GST_OBJECT(mpPlaybin))
+ {
+ GstState newstate, pendingstate;
+
+ gst_message_parse_state_changed (message, nullptr, &newstate, &pendingstate);
+
+ if (!mbUseGtkSink && newstate == GST_STATE_PAUSED &&
+ pendingstate == GST_STATE_VOID_PENDING && mpXOverlay)
+ {
+ gst_video_overlay_expose(mpXOverlay);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+#define LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE "GstWaylandDisplayHandleContextType"
+
+static bool lcl_is_wayland_display_handle_need_context_message(GstMessage* msg)
+{
+ g_return_val_if_fail(GST_IS_MESSAGE(msg), false);
+
+ if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_NEED_CONTEXT)
+ return false;
+ const gchar *type = nullptr;
+ if (!gst_message_parse_context_type(msg, &type))
+ return false;
+ return !g_strcmp0(type, LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
+}
+
+static GstContext* lcl_wayland_display_handle_context_new(void* display)
+{
+ GstContext *context = gst_context_new(LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE, true);
+ gst_structure_set (gst_context_writable_structure (context),
+ "handle", G_TYPE_POINTER, display, nullptr);
+ return context;
+}
+
+GstBusSyncReply Player::processSyncMessage( GstMessage *message )
+{
+#if OSL_DEBUG_LEVEL > 0
+ if ( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR )
+ {
+ GError* error;
+ gchar* error_debug;
+
+ gst_message_parse_error( message, &error, &error_debug );
+ SAL_WARN(
+ "avmedia.gstreamer",
+ "error: '" << error->message << "' debug: '"
+ << error_debug << "'");
+ }
+#endif
+
+ if (!mbUseGtkSink)
+ {
+ if (gst_is_video_overlay_prepare_window_handle_message (message) )
+ {
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " processSyncMessage prepare window id: " <<
+ GST_MESSAGE_TYPE_NAME( message ) << " " << static_cast<int>(mnWindowID) );
+ if( mpXOverlay )
+ g_object_unref( G_OBJECT ( mpXOverlay ) );
+ g_object_set( GST_MESSAGE_SRC( message ), "force-aspect-ratio", FALSE, nullptr );
+ mpXOverlay = GST_VIDEO_OVERLAY( GST_MESSAGE_SRC( message ) );
+ g_object_ref( G_OBJECT ( mpXOverlay ) );
+ if ( mnWindowID != 0 )
+ {
+ gst_video_overlay_set_window_handle( mpXOverlay, mnWindowID );
+ gst_video_overlay_handle_events(mpXOverlay, 0); // Let the parent window handle events.
+ if (maArea.Width > 0 && maArea.Height > 0)
+ gst_video_overlay_set_render_rectangle(mpXOverlay, maArea.X, maArea.Y, maArea.Width, maArea.Height);
+ }
+
+ return GST_BUS_DROP;
+ }
+ else if (lcl_is_wayland_display_handle_need_context_message(message))
+ {
+ GstContext *context = lcl_wayland_display_handle_context_new(mpDisplay);
+ gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message)), context);
+
+ return GST_BUS_DROP;
+ }
+ }
+
+ if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ASYNC_DONE ) {
+ if( mnDuration == 0) {
+ gint64 gst_duration = 0;
+ if( gst_element_query_duration( mpPlaybin, GST_FORMAT_TIME, &gst_duration) )
+ mnDuration = gst_duration;
+ }
+ if( mnWidth == 0 ) {
+ GstPad *pad = nullptr;
+
+ g_signal_emit_by_name( mpPlaybin, "get-video-pad", 0, &pad );
+
+ if( pad ) {
+ int w = 0, h = 0;
+
+ GstCaps *caps = gst_pad_get_current_caps( pad );
+
+ if( gst_structure_get( gst_caps_get_structure( caps, 0 ),
+ "width", G_TYPE_INT, &w,
+ "height", G_TYPE_INT, &h,
+ nullptr ) ) {
+ mnWidth = w;
+ mnHeight = h;
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "queried size: " << mnWidth << "x" << mnHeight );
+
+ }
+ gst_caps_unref( caps );
+ g_object_unref( pad );
+ }
+
+ maSizeCondition.set();
+ }
+ } else if (gst_is_missing_plugin_message(message)) {
+ TheMissingPluginInstaller::get().report(this, message);
+ if( mnWidth == 0 ) {
+ // an error occurred, set condition so that OOo thread doesn't wait for us
+ maSizeCondition.set();
+ }
+ } else if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR ) {
+ if( mnWidth == 0 ) {
+ // an error occurred, set condition so that OOo thread doesn't wait for us
+ maSizeCondition.set();
+ }
+ }
+
+ return GST_BUS_PASS;
+}
+
+void Player::preparePlaybin( std::u16string_view rURL, GstElement *pSink )
+{
+ if (mpPlaybin != nullptr)
+ {
+ gst_element_set_state( mpPlaybin, GST_STATE_NULL );
+ g_object_unref( mpPlaybin );
+ }
+
+ mpPlaybin = gst_element_factory_make( "playbin", nullptr );
+
+ //tdf#96989 on systems with flat-volumes setting the volume directly on the
+ //playbin to 100% results in setting the global volume to 100% of the
+ //maximum. We expect to set as % of the current volume.
+ mpVolumeControl = gst_element_factory_make( "volume", nullptr );
+ GstElement *pAudioSink = gst_element_factory_make( "autoaudiosink", nullptr );
+ GstElement* pAudioOutput = gst_bin_new("audio-output-bin");
+ assert(pAudioOutput);
+ if (pAudioSink)
+ gst_bin_add(GST_BIN(pAudioOutput), pAudioSink);
+ if (mpVolumeControl)
+ {
+ gst_bin_add(GST_BIN(pAudioOutput), mpVolumeControl);
+ if (pAudioSink)
+ gst_element_link(mpVolumeControl, pAudioSink);
+ GstPad *pPad = gst_element_get_static_pad(mpVolumeControl, "sink");
+ gst_element_add_pad(GST_ELEMENT(pAudioOutput), gst_ghost_pad_new("sink", pPad));
+ gst_object_unref(GST_OBJECT(pPad));
+ }
+ g_object_set(G_OBJECT(mpPlaybin), "audio-sink", pAudioOutput, nullptr);
+
+ if( pSink != nullptr ) // used for getting preferred size etc.
+ {
+ g_object_set( G_OBJECT( mpPlaybin ), "video-sink", pSink, nullptr );
+ mbFakeVideo = true;
+ }
+ else
+ mbFakeVideo = false;
+
+ OString ascURL = OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 );
+ g_object_set( G_OBJECT( mpPlaybin ), "uri", ascURL.getStr() , nullptr );
+
+ GstBus *pBus = gst_element_get_bus( mpPlaybin );
+ if (mbWatchID)
+ {
+ g_source_remove(mnWatchID);
+ mbWatchID = false;
+ }
+ mnWatchID = gst_bus_add_watch( pBus, pipeline_bus_callback, this );
+ mbWatchID = true;
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " set sync handler" );
+ gst_bus_set_sync_handler( pBus, pipeline_bus_sync_handler, this, nullptr );
+ g_object_unref( pBus );
+}
+
+
+bool Player::create( const OUString& rURL )
+{
+ bool bRet = false;
+
+ // create all the elements and link them
+
+ SAL_INFO( "avmedia.gstreamer", "create player, URL: '" << rURL << "'" );
+
+ if( mbInitialized && !rURL.isEmpty() )
+ {
+ // fakesink for pre-roll & sizing ...
+ preparePlaybin( rURL, gst_element_factory_make( "fakesink", nullptr ) );
+
+ gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
+
+ bRet = true;
+ }
+
+ if( bRet )
+ maURL = rURL;
+ else
+ maURL.clear();
+
+ return bRet;
+}
+
+void SAL_CALL Player::start()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // set the pipeline state to READY and run the loop
+ if( mbInitialized && mpPlaybin != nullptr )
+ {
+ gst_element_set_state( mpPlaybin, GST_STATE_PLAYING );
+ }
+}
+
+void SAL_CALL Player::stop()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // set the pipeline in PAUSED STATE
+ if( mpPlaybin )
+ gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "stop " << mpPlaybin );
+}
+
+sal_Bool SAL_CALL Player::isPlaying()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ bool bRet = false;
+
+ // return whether the pipeline target is PLAYING STATE or not
+ if (mbInitialized && mpPlaybin)
+ {
+ bRet = GST_STATE_TARGET(mpPlaybin) == GST_STATE_PLAYING;
+ }
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "isPlaying " << bRet );
+
+ return bRet;
+}
+
+double SAL_CALL Player::getDuration()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ // slideshow checks for non-zero duration, so cheat here
+ double duration = 0.3;
+
+ if( mpPlaybin && mnDuration > 0 ) {
+ duration = mnDuration / GST_SECOND;
+ }
+
+ return duration;
+}
+
+
+void SAL_CALL Player::setMediaTime( double fTime )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ if( !mpPlaybin )
+ return;
+
+ gint64 gst_position = llround (fTime * GST_SECOND);
+
+ gst_element_seek( mpPlaybin, 1.0,
+ GST_FORMAT_TIME,
+ GST_SEEK_FLAG_FLUSH,
+ GST_SEEK_TYPE_SET, gst_position,
+ GST_SEEK_TYPE_NONE, 0 );
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "seek to: " << gst_position << " ns original: " << fTime << " s" );
+}
+
+
+double SAL_CALL Player::getMediaTime()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ double position = 0.0;
+
+ if( mpPlaybin ) {
+ // get current position in the stream
+ gint64 gst_position;
+ if( gst_element_query_position( mpPlaybin, GST_FORMAT_TIME, &gst_position ) )
+ position = gst_position / GST_SECOND;
+ }
+
+ return position;
+}
+
+
+void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ // TODO check how to do with GST
+ mbLooping = bSet;
+}
+
+
+sal_Bool SAL_CALL Player::isPlaybackLoop()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ // TODO check how to do with GST
+ return mbLooping;
+}
+
+
+void SAL_CALL Player::setMute( sal_Bool bSet )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "set mute: " << bSet << " muted: " << mbMuted << " unmuted volume: " << mnUnmutedVolume );
+
+ // change the volume to 0 or the unmuted volume
+ if (mpVolumeControl && mbMuted != bool(bSet))
+ {
+ double nVolume = mnUnmutedVolume;
+ if( bSet )
+ {
+ nVolume = 0.0;
+ }
+
+ g_object_set( G_OBJECT( mpVolumeControl ), "volume", nVolume, nullptr );
+
+ mbMuted = bSet;
+ }
+}
+
+
+sal_Bool SAL_CALL Player::isMute()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ return mbMuted;
+}
+
+
+void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ mnUnmutedVolume = pow( 10.0, nVolumeDB / 20.0 );
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "set volume: " << nVolumeDB << " gst volume: " << mnUnmutedVolume );
+
+ // change volume
+ if (mpVolumeControl && !mbMuted)
+ {
+ g_object_set( G_OBJECT( mpVolumeControl ), "volume", mnUnmutedVolume, nullptr );
+ }
+}
+
+
+sal_Int16 SAL_CALL Player::getVolumeDB()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ sal_Int16 nVolumeDB(0);
+
+ if (mpVolumeControl)
+ {
+ double nGstVolume = 0.0;
+
+ g_object_get( G_OBJECT( mpVolumeControl ), "volume", &nGstVolume, nullptr );
+
+ nVolumeDB = static_cast<sal_Int16>( 20.0*log10 ( nGstVolume ) );
+ }
+
+ return nVolumeDB;
+}
+
+
+awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ awt::Size aSize( 0, 0 );
+
+ if( maURL.isEmpty() )
+ {
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::getPreferredPlayerWindowSize - empty URL => 0x0" );
+ return aSize;
+ }
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " pre-Player::getPreferredPlayerWindowSize, member " << mnWidth << "x" << mnHeight );
+
+ osl::Condition::Result aResult = maSizeCondition.wait( std::chrono::seconds(10) );
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::getPreferredPlayerWindowSize after waitCondition " << aResult << ", member " << mnWidth << "x" << mnHeight );
+
+ if( mnWidth != 0 && mnHeight != 0 ) {
+ aSize.Width = mnWidth;
+ aSize.Height = mnHeight;
+ }
+
+ return aSize;
+}
+
+uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& rArguments )
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+
+ uno::Reference< ::media::XPlayerWindow > xRet;
+
+ if (rArguments.getLength() > 1)
+ rArguments[1] >>= maArea;
+
+ awt::Size aSize = getPreferredPlayerWindowSize();
+
+ if( mbFakeVideo )
+ preparePlaybin( maURL, nullptr );
+
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "Player::createPlayerWindow " << aSize.Width << "x" << aSize.Height << " length: " << rArguments.getLength() );
+
+ if( aSize.Width > 0 && aSize.Height > 0 )
+ {
+ if (rArguments.getLength() <= 2)
+ {
+ xRet = new ::avmedia::gstreamer::Window;
+ return xRet;
+ }
+
+ sal_IntPtr pIntPtr = 0;
+ rArguments[ 2 ] >>= pIntPtr;
+ SystemChildWindow *pParentWindow = reinterpret_cast< SystemChildWindow* >( pIntPtr );
+ if (!pParentWindow)
+ return nullptr;
+
+ const SystemEnvData* pEnvData = pParentWindow->GetSystemData();
+ if (!pEnvData)
+ return nullptr;
+
+ // tdf#124027: the position of embedded window is identical w/ the position
+ // of media object in all other vclplugs (kf5, gen), in gtk3 w/o gtksink it
+ // needs to be translated
+ if (pEnvData->toolkit == SystemEnvData::Toolkit::Gtk)
+ {
+ Point aPoint = pParentWindow->GetPosPixel();
+ maArea.X = aPoint.getX();
+ maArea.Y = aPoint.getY();
+ }
+
+ mbUseGtkSink = false;
+
+ GstElement *pVideosink = static_cast<GstElement*>(pParentWindow->CreateGStreamerSink());
+ if (pVideosink)
+ {
+ if (pEnvData->toolkit == SystemEnvData::Toolkit::Gtk)
+ mbUseGtkSink = true;
+ }
+ else
+ {
+ if (pEnvData->platform == SystemEnvData::Platform::Wayland)
+ pVideosink = gst_element_factory_make("waylandsink", "video-output");
+ else
+ pVideosink = gst_element_factory_make("autovideosink", "video-output");
+ if (!pVideosink)
+ return nullptr;
+ }
+
+ xRet = new ::avmedia::gstreamer::Window;
+
+ g_object_set(G_OBJECT(mpPlaybin), "video-sink", pVideosink, nullptr);
+ g_object_set(G_OBJECT(mpPlaybin), "force-aspect-ratio", FALSE, nullptr);
+
+ if ((rArguments.getLength() >= 4) && (rArguments[3] >>= pIntPtr) && pIntPtr)
+ {
+ auto pItem = reinterpret_cast<const avmedia::MediaItem*>(pIntPtr);
+ Graphic aGraphic = pItem->getGraphic();
+ const text::GraphicCrop& rCrop = pItem->getCrop();
+ if (!aGraphic.IsNone() && (rCrop.Bottom > 0 || rCrop.Left > 0 || rCrop.Right > 0 || rCrop.Top > 0))
+ {
+ // The media item has a non-empty cropping set. Try to crop the video accordingly.
+ Size aPref = aGraphic.GetPrefSize();
+ Size aPixel = aGraphic.GetSizePixel();
+ tools::Long nLeft = aPixel.getWidth() * rCrop.Left / aPref.getWidth();
+ tools::Long nTop = aPixel.getHeight() * rCrop.Top / aPref.getHeight();
+ tools::Long nRight = aPixel.getWidth() * rCrop.Right / aPref.getWidth();
+ tools::Long nBottom = aPixel.getHeight() * rCrop.Bottom / aPref.getHeight();
+ GstElement* pVideoFilter = gst_element_factory_make("videocrop", nullptr);
+ if (pVideoFilter)
+ {
+ g_object_set(G_OBJECT(pVideoFilter), "left", nLeft, nullptr);
+ g_object_set(G_OBJECT(pVideoFilter), "top", nTop, nullptr);
+ g_object_set(G_OBJECT(pVideoFilter), "right", nRight, nullptr);
+ g_object_set(G_OBJECT(pVideoFilter), "bottom", nBottom, nullptr);
+ g_object_set(G_OBJECT(mpPlaybin), "video-filter", pVideoFilter, nullptr);
+ }
+ }
+ }
+
+ if (!mbUseGtkSink)
+ {
+ mnWindowID = pEnvData->GetWindowHandle(pParentWindow->ImplGetFrame());
+ mpDisplay = pEnvData->pDisplay;
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "set window id to " << static_cast<int>(mnWindowID) << " XOverlay " << mpXOverlay);
+ }
+ gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
+ if (!mbUseGtkSink && mpXOverlay)
+ gst_video_overlay_set_window_handle( mpXOverlay, mnWindowID );
+ }
+
+ return xRet;
+}
+
+uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
+{
+ ::osl::MutexGuard aGuard(m_aMutex);
+ rtl::Reference<FrameGrabber> pFrameGrabber;
+ const awt::Size aPrefSize( getPreferredPlayerWindowSize() );
+
+ if( ( aPrefSize.Width > 0 ) && ( aPrefSize.Height > 0 ) )
+ pFrameGrabber = FrameGrabber::create( maURL );
+ SAL_INFO( "avmedia.gstreamer", AVVERSION "created FrameGrabber " << pFrameGrabber.get() );
+
+ return pFrameGrabber;
+}
+
+
+OUString SAL_CALL Player::getImplementationName()
+{
+ return AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME;
+}
+
+
+sal_Bool SAL_CALL Player::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+
+uno::Sequence< OUString > SAL_CALL Player::getSupportedServiceNames()
+{
+ return { AVMEDIA_GST_PLAYER_SERVICENAME };
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstplayer.hxx b/avmedia/source/gstreamer/gstplayer.hxx
new file mode 100644
index 000000000..2694ac00c
--- /dev/null
+++ b/avmedia/source/gstreamer/gstplayer.hxx
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <osl/conditn.hxx>
+#include "gstcommon.hxx"
+
+#include <com/sun/star/media/XPlayer.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <cppuhelper/basemutex.hxx>
+
+typedef struct _GstVideoOverlay GstVideoOverlay;
+
+namespace avmedia::gstreamer {
+
+
+typedef ::cppu::WeakComponentImplHelper< css::media::XPlayer,
+ css::lang::XServiceInfo > GstPlayer_BASE;
+
+class Player final : public ::cppu::BaseMutex,
+ public GstPlayer_BASE
+{
+public:
+
+ explicit Player();
+ virtual ~Player() override;
+
+ void preparePlaybin( std::u16string_view rURL, GstElement *pSink );
+ bool create( const OUString& rURL );
+ void processMessage( GstMessage *message );
+ GstBusSyncReply processSyncMessage( GstMessage *message );
+
+ // XPlayer
+ virtual void SAL_CALL start( ) override;
+ virtual void SAL_CALL stop( ) override;
+ virtual sal_Bool SAL_CALL isPlaying( ) override;
+ virtual double SAL_CALL getDuration( ) override;
+ virtual void SAL_CALL setMediaTime( double fTime ) override;
+ virtual double SAL_CALL getMediaTime( ) override;
+ virtual void SAL_CALL setPlaybackLoop( sal_Bool bSet ) override;
+ virtual sal_Bool SAL_CALL isPlaybackLoop( ) override;
+ virtual void SAL_CALL setMute( sal_Bool bSet ) override;
+ virtual sal_Bool SAL_CALL isMute( ) override;
+ virtual void SAL_CALL setVolumeDB( sal_Int16 nVolumeDB ) override;
+ virtual sal_Int16 SAL_CALL getVolumeDB( ) override;
+ virtual css::awt::Size SAL_CALL getPreferredPlayerWindowSize( ) override;
+ virtual css::uno::Reference< css::media::XPlayerWindow > SAL_CALL createPlayerWindow( const css::uno::Sequence< css::uno::Any >& aArguments ) override;
+ virtual css::uno::Reference< css::media::XFrameGrabber > SAL_CALL createFrameGrabber( ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+ // ::cppu::OComponentHelper
+ virtual void SAL_CALL disposing() final override;
+
+private:
+ OUString maURL;
+
+ // Add elements and pipeline here
+ GstElement* mpPlaybin; // the playbin is also a pipeline
+ GstElement* mpVolumeControl; // the playbin is also a pipeline
+ bool mbUseGtkSink;
+ bool mbFakeVideo;
+
+ gdouble mnUnmutedVolume;
+ bool mbMuted;
+ bool mbLooping;
+ bool mbInitialized;
+
+ void* mpDisplay;
+ tools::Long mnWindowID;
+ GstVideoOverlay* mpXOverlay;
+ gint64 mnDuration;
+ int mnWidth;
+ int mnHeight;
+
+ css::awt::Rectangle maArea; // Area of the player window.
+
+ guint mnWatchID;
+ bool mbWatchID;
+
+ osl::Condition maSizeCondition;
+};
+
+} // namespace avmedia::gstreamer
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstwindow.cxx b/avmedia/source/gstreamer/gstwindow.cxx
new file mode 100644
index 000000000..5862443da
--- /dev/null
+++ b/avmedia/source/gstreamer/gstwindow.cxx
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/awt/SystemPointer.hpp>
+
+#include <cppuhelper/supportsservice.hxx>
+
+#include "gstwindow.hxx"
+
+constexpr OUStringLiteral AVMEDIA_GST_WINDOW_IMPLEMENTATIONNAME = u"com.sun.star.comp.avmedia.Window_GStreamer";
+constexpr OUStringLiteral AVMEDIA_GST_WINDOW_SERVICENAME = u"com.sun.star.media.Window_GStreamer";
+
+using namespace ::com::sun::star;
+
+namespace avmedia::gstreamer {
+
+// Window
+
+
+Window::Window() :
+ meZoomLevel( media::ZoomLevel_NOT_AVAILABLE )
+{
+}
+
+Window::~Window()
+{
+}
+
+// XPlayerWindow
+
+
+void SAL_CALL Window::update( )
+{
+}
+
+sal_Bool SAL_CALL Window::setZoomLevel( media::ZoomLevel eZoomLevel )
+{
+ bool bRet = false;
+
+ if( meZoomLevel != media::ZoomLevel_NOT_AVAILABLE &&
+ eZoomLevel != media::ZoomLevel_NOT_AVAILABLE )
+ {
+ if( eZoomLevel != meZoomLevel )
+ {
+ meZoomLevel = eZoomLevel;
+ }
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+media::ZoomLevel SAL_CALL Window::getZoomLevel( )
+{
+ return meZoomLevel;
+}
+
+void SAL_CALL Window::setPointerType( sal_Int32 /*nPointerType*/ )
+{
+}
+
+// XWindow
+
+
+void SAL_CALL Window::setPosSize( sal_Int32 /*X*/, sal_Int32 /*Y*/, sal_Int32 /*Width*/, sal_Int32 /*Height*/, sal_Int16 /*Flags*/ )
+{
+}
+
+awt::Rectangle SAL_CALL Window::getPosSize()
+{
+ awt::Rectangle aRet;
+
+ aRet.X = aRet.Y = 0;
+ aRet.Width = 320;
+ aRet.Height = 240;
+
+ return aRet;
+}
+
+void SAL_CALL Window::setVisible( sal_Bool /*bVisible*/ )
+{
+}
+
+void SAL_CALL Window::setEnable( sal_Bool /*bEnable*/ )
+{
+}
+
+void SAL_CALL Window::setFocus( )
+{
+}
+
+void SAL_CALL Window::addWindowListener( const uno::Reference< awt::XWindowListener >& )
+{
+}
+
+void SAL_CALL Window::removeWindowListener( const uno::Reference< awt::XWindowListener >& )
+{
+}
+
+void SAL_CALL Window::addFocusListener( const uno::Reference< awt::XFocusListener >& )
+{
+}
+
+void SAL_CALL Window::removeFocusListener( const uno::Reference< awt::XFocusListener >& )
+{
+}
+
+void SAL_CALL Window::addKeyListener( const uno::Reference< awt::XKeyListener >& )
+{
+}
+
+void SAL_CALL Window::removeKeyListener( const uno::Reference< awt::XKeyListener >& )
+{
+}
+
+void SAL_CALL Window::addMouseListener( const uno::Reference< awt::XMouseListener >& )
+{
+}
+
+void SAL_CALL Window::removeMouseListener( const uno::Reference< awt::XMouseListener >& )
+{
+}
+
+void SAL_CALL Window::addMouseMotionListener( const uno::Reference< awt::XMouseMotionListener >& )
+{
+}
+
+void SAL_CALL Window::removeMouseMotionListener( const uno::Reference< awt::XMouseMotionListener >& )
+{
+}
+
+void SAL_CALL Window::addPaintListener( const uno::Reference< awt::XPaintListener >& )
+{
+}
+
+void SAL_CALL Window::removePaintListener( const uno::Reference< awt::XPaintListener >& )
+{
+}
+
+// XComponent
+
+
+void SAL_CALL Window::dispose( )
+{
+}
+
+void SAL_CALL Window::addEventListener( const uno::Reference< lang::XEventListener >& )
+{
+}
+
+void SAL_CALL Window::removeEventListener( const uno::Reference< lang::XEventListener >& )
+{
+}
+
+// XServiceInfo
+
+
+OUString SAL_CALL Window::getImplementationName( )
+{
+ return AVMEDIA_GST_WINDOW_IMPLEMENTATIONNAME;
+}
+
+sal_Bool SAL_CALL Window::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+uno::Sequence< OUString > SAL_CALL Window::getSupportedServiceNames( )
+{
+ return { AVMEDIA_GST_WINDOW_SERVICENAME };
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/avmedia/source/gstreamer/gstwindow.hxx b/avmedia/source/gstreamer/gstwindow.hxx
new file mode 100644
index 000000000..ff8a7cc91
--- /dev/null
+++ b/avmedia/source/gstreamer/gstwindow.hxx
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/interfacecontainer.h>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/media/XPlayerWindow.hpp>
+
+namespace avmedia::gstreamer {
+
+class Player;
+
+class Window : public ::cppu::WeakImplHelper< css::media::XPlayerWindow,
+ css::lang::XServiceInfo >
+{
+public:
+
+ explicit Window();
+ virtual ~Window() override;
+
+ // XPlayerWindow
+ virtual void SAL_CALL update( ) override;
+ virtual sal_Bool SAL_CALL setZoomLevel( css::media::ZoomLevel ZoomLevel ) override;
+ virtual css::media::ZoomLevel SAL_CALL getZoomLevel( ) override;
+ virtual void SAL_CALL setPointerType( sal_Int32 nPointerType ) override;
+
+ // XWindow
+ virtual void SAL_CALL setPosSize( sal_Int32 X, sal_Int32 Y, sal_Int32 Width, sal_Int32 Height, sal_Int16 Flags ) override;
+ virtual css::awt::Rectangle SAL_CALL getPosSize( ) override;
+ virtual void SAL_CALL setVisible( sal_Bool Visible ) override;
+ virtual void SAL_CALL setEnable( sal_Bool Enable ) override;
+ virtual void SAL_CALL setFocus( ) override;
+ virtual void SAL_CALL addWindowListener( const css::uno::Reference< css::awt::XWindowListener >& xListener ) override;
+ virtual void SAL_CALL removeWindowListener( const css::uno::Reference< css::awt::XWindowListener >& xListener ) override;
+ virtual void SAL_CALL addFocusListener( const css::uno::Reference< css::awt::XFocusListener >& xListener ) override;
+ virtual void SAL_CALL removeFocusListener( const css::uno::Reference< css::awt::XFocusListener >& xListener ) override;
+ virtual void SAL_CALL addKeyListener( const css::uno::Reference< css::awt::XKeyListener >& xListener ) override;
+ virtual void SAL_CALL removeKeyListener( const css::uno::Reference< css::awt::XKeyListener >& xListener ) override;
+ virtual void SAL_CALL addMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override;
+ virtual void SAL_CALL removeMouseListener( const css::uno::Reference< css::awt::XMouseListener >& xListener ) override;
+ virtual void SAL_CALL addMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override;
+ virtual void SAL_CALL removeMouseMotionListener( const css::uno::Reference< css::awt::XMouseMotionListener >& xListener ) override;
+ virtual void SAL_CALL addPaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override;
+ virtual void SAL_CALL removePaintListener( const css::uno::Reference< css::awt::XPaintListener >& xListener ) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose( ) override;
+ virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
+ virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+
+private:
+
+ css::media::ZoomLevel meZoomLevel;
+};
+
+} // namespace avmedia::gstreamer
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */