diff options
Diffstat (limited to 'avmedia/source/gstreamer')
-rw-r--r-- | avmedia/source/gstreamer/avmediagstreamer.component | 16 | ||||
-rw-r--r-- | avmedia/source/gstreamer/gstcommon.hxx | 43 | ||||
-rw-r--r-- | avmedia/source/gstreamer/gstframegrabber.cxx | 175 | ||||
-rw-r--r-- | avmedia/source/gstreamer/gstframegrabber.hxx | 65 | ||||
-rw-r--r-- | avmedia/source/gstreamer/gstmanager.cxx | 75 | ||||
-rw-r--r-- | avmedia/source/gstreamer/gstmanager.hxx | 47 | ||||
-rw-r--r-- | avmedia/source/gstreamer/gstplayer.cxx | 946 | ||||
-rw-r--r-- | avmedia/source/gstreamer/gstplayer.hxx | 110 | ||||
-rw-r--r-- | avmedia/source/gstreamer/gstwindow.cxx | 192 | ||||
-rw-r--r-- | avmedia/source/gstreamer/gstwindow.hxx | 82 |
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: */ |