1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
33 #include <com/sun/star/text/GraphicCrop.hpp>
35 #include <cppuhelper/supportsservice.hxx>
36 #include <sal/log.hxx>
37 #include <rtl/string.hxx>
38 #include <salhelper/thread.hxx>
39 #include <vcl/svapp.hxx>
40 #include <vcl/syschild.hxx>
41 #include <vcl/sysdata.hxx>
42 #include <vcl/graph.hxx>
43 #include <avmedia/mediaitem.hxx>
45 #include "gstplayer.hxx"
46 #include "gstframegrabber.hxx"
47 #include "gstwindow.hxx"
49 #include <gst/video/videooverlay.h>
50 #include <gst/pbutils/missing-plugins.h>
51 #include <gst/pbutils/pbutils.h>
53 #define AVVERSION "gst 1.0: "
55 using namespace ::com::sun::star
;
57 namespace avmedia::gstreamer
{
63 explicit FlagGuard(bool & flag
): flag_(flag
) { flag_
= true; }
65 ~FlagGuard() { flag_
= false; }
71 class MissingPluginInstallerThread
: public salhelper::Thread
{
73 MissingPluginInstallerThread(): Thread("MissingPluginInstaller") {}
76 void execute() override
;
80 class MissingPluginInstaller
{
81 friend class MissingPluginInstallerThread
;
84 MissingPluginInstaller(): launchNewThread_(true), inCleanUp_(false) {}
86 ~MissingPluginInstaller();
88 void report(rtl::Reference
<Player
> const & source
, GstMessage
* message
);
90 // Player::~Player calls Player::disposing calls
91 // MissingPluginInstaller::detach, so do not take Player by rtl::Reference
92 // here (which would bump its refcount back from 0 to 1):
93 void detach(Player
const * source
);
98 DECL_STATIC_LINK(MissingPluginInstaller
, launchUi
, void*, void);
100 std::recursive_mutex mutex_
;
101 std::set
<OString
> reported_
;
102 std::map
<OString
, std::set
<rtl::Reference
<Player
>>> queued_
;
103 rtl::Reference
<MissingPluginInstallerThread
> currentThread_
;
104 std::vector
<OString
> currentDetails_
;
105 std::set
<rtl::Reference
<Player
>> currentSources_
;
106 bool launchNewThread_
;
111 MissingPluginInstaller::~MissingPluginInstaller() {
112 std::unique_lock
g(mutex_
);
113 SAL_WARN_IF(currentThread_
.is(), "avmedia.gstreamer", "unjoined thread");
118 void MissingPluginInstaller::report(
119 rtl::Reference
<Player
> const & source
, GstMessage
* message
)
121 // assert(gst_is_missing_plugin_message(message));
122 gchar
* det
= gst_missing_plugin_message_get_installer_detail(message
);
123 if (det
== nullptr) {
126 "gst_missing_plugin_message_get_installer_detail failed");
129 std::size_t len
= std::strlen(det
);
130 if (len
> SAL_MAX_INT32
) {
131 SAL_WARN("avmedia.gstreamer", "detail string too long");
135 OString
detStr(det
, len
);
137 rtl::Reference
<MissingPluginInstallerThread
> join
;
138 rtl::Reference
<MissingPluginInstallerThread
> launch
;
140 std::unique_lock
g(mutex_
);
141 if (reported_
.find(detStr
) != reported_
.end()) {
144 auto & i
= queued_
[detStr
];
145 bool fresh
= i
.empty();
147 if (!(fresh
&& launchNewThread_
)) {
150 join
= currentThread_
;
151 currentThread_
= new MissingPluginInstallerThread
;
153 FlagGuard
f(inCleanUp_
);
154 currentSources_
.clear();
157 launchNewThread_
= false;
158 launch
= currentThread_
;
164 Application::PostUserEvent(
165 LINK(this, MissingPluginInstaller
, launchUi
), launch
.get());
169 void eraseSource(std::set
<rtl::Reference
<Player
>> & set
, Player
const * source
)
171 auto i
= std::find_if(
172 set
.begin(), set
.end(),
173 [source
](rtl::Reference
<Player
> const & el
) {
174 return el
.get() == source
;
176 if (i
!= set
.end()) {
182 void MissingPluginInstaller::detach(Player
const * source
) {
183 rtl::Reference
<MissingPluginInstallerThread
> join
;
185 std::unique_lock
g(mutex_
);
187 // Guard against ~MissingPluginInstaller with erroneously un-joined
188 // currentThread_ (thus non-empty currentSources_) calling
189 // destructor of currentSources_, calling ~Player, calling here,
190 // which would use currentSources_ while it is already being
194 for (auto i
= queued_
.begin(); i
!= queued_
.end();) {
195 eraseSource(i
->second
, source
);
196 if (i
->second
.empty()) {
197 i
= queued_
.erase(i
);
202 if (currentThread_
.is()) {
203 assert(!currentSources_
.empty());
204 eraseSource(currentSources_
, source
);
205 if (currentSources_
.empty()) {
206 join
= currentThread_
;
207 currentThread_
.clear();
208 launchNewThread_
= true;
213 // missing cancellability of gst_install_plugins_sync
219 void MissingPluginInstaller::processQueue() {
220 assert(!queued_
.empty());
221 assert(currentDetails_
.empty());
222 for (const auto& rEntry
: queued_
) {
223 reported_
.insert(rEntry
.first
);
224 currentDetails_
.push_back(rEntry
.first
);
225 currentSources_
.insert(rEntry
.second
.begin(), rEntry
.second
.end());
231 IMPL_STATIC_LINK(MissingPluginInstaller
, launchUi
, void *, p
, void)
233 MissingPluginInstallerThread
* thread
= static_cast<MissingPluginInstallerThread
*>(p
);
234 rtl::Reference
<MissingPluginInstallerThread
> ref(thread
, SAL_NO_ACQUIRE
);
236 // not thread safe; hopefully fine to consistently call from our event
237 // loop (which is the only reason to have this
238 // Application::PostUserEvent diversion, in case
239 // MissingPluginInstaller::report might be called from outside our event
240 // loop), and hopefully fine to call gst_is_missing_plugin_message and
241 // gst_missing_plugin_message_get_installer_detail before calling
247 MissingPluginInstaller
& TheMissingPluginInstaller()
249 static MissingPluginInstaller theInstaller
;
254 void MissingPluginInstallerThread::execute() {
255 MissingPluginInstaller
& inst
= TheMissingPluginInstaller();
257 std::vector
<OString
> details
;
259 std::unique_lock
g(inst
.mutex_
);
260 assert(!inst
.currentDetails_
.empty());
261 details
.swap(inst
.currentDetails_
);
263 std::vector
<char *> args
;
264 args
.reserve(details
.size());
265 for (auto const& i
: details
)
267 args
.push_back(const_cast<char *>(i
.getStr()));
269 args
.push_back(nullptr);
270 gst_install_plugins_sync(args
.data(), nullptr);
272 std::unique_lock
g(inst
.mutex_
);
273 if (inst
.queued_
.empty() || inst
.launchNewThread_
) {
274 inst
.launchNewThread_
= true;
282 } // end anonymous namespace
286 GstPlayer_BASE( m_aMutex
),
287 mpPlaybin( nullptr ),
288 mpVolumeControl( nullptr ),
289 mbUseGtkSink( false ),
290 mbFakeVideo (false ),
291 mnUnmutedVolume( 0 ),
294 mbInitialized( false ),
295 mpDisplay( nullptr ),
297 mpXOverlay( nullptr ),
304 // Initialize GStreamer library
306 char name
[] = "libreoffice";
307 char *arguments
[] = { name
};
308 char** argv
= arguments
;
309 GError
* pError
= nullptr;
311 mbInitialized
= gst_init_check( &argc
, &argv
, &pError
);
313 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::Player" );
315 if (pError
!= nullptr)
317 // TODO: throw an exception?
318 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::Player error '" << pError
->message
<< "'" );
319 g_error_free (pError
);
326 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::~Player" );
332 void SAL_CALL
Player::disposing()
334 TheMissingPluginInstaller().detach(this);
336 ::osl::MutexGuard
aGuard(m_aMutex
);
340 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::disposing" );
342 // Release the elements and pipeline
347 gst_element_set_state( mpPlaybin
, GST_STATE_NULL
);
348 g_object_unref( G_OBJECT( mpPlaybin
) );
351 mpVolumeControl
= nullptr;
355 g_object_unref( G_OBJECT ( mpXOverlay
) );
356 mpXOverlay
= nullptr;
362 g_source_remove(mnWatchID
);
368 static gboolean
pipeline_bus_callback( GstBus
*, GstMessage
*message
, gpointer data
)
370 Player
* pPlayer
= static_cast<Player
*>(data
);
372 pPlayer
->processMessage( message
);
378 static GstBusSyncReply
pipeline_bus_sync_handler( GstBus
*, GstMessage
* message
, gpointer data
)
380 Player
* pPlayer
= static_cast<Player
*>(data
);
382 return pPlayer
->processSyncMessage( message
);
386 void Player::processMessage( GstMessage
*message
)
388 switch( GST_MESSAGE_TYPE( message
) ) {
389 case GST_MESSAGE_EOS
:
390 gst_element_set_state( mpPlaybin
, GST_STATE_READY
);
394 case GST_MESSAGE_STATE_CHANGED
:
395 if (message
->src
== GST_OBJECT(mpPlaybin
))
397 GstState newstate
, pendingstate
;
399 gst_message_parse_state_changed (message
, nullptr, &newstate
, &pendingstate
);
401 if (!mbUseGtkSink
&& newstate
== GST_STATE_PAUSED
&&
402 pendingstate
== GST_STATE_VOID_PENDING
&& mpXOverlay
)
404 gst_video_overlay_expose(mpXOverlay
);
413 #define LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE "GstWaylandDisplayHandleContextType"
415 static bool lcl_is_wayland_display_handle_need_context_message(GstMessage
* msg
)
417 g_return_val_if_fail(GST_IS_MESSAGE(msg
), false);
419 if (GST_MESSAGE_TYPE(msg
) != GST_MESSAGE_NEED_CONTEXT
)
421 const gchar
*type
= nullptr;
422 if (!gst_message_parse_context_type(msg
, &type
))
424 return !g_strcmp0(type
, LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE
);
427 static GstContext
* lcl_wayland_display_handle_context_new(void* display
)
429 GstContext
*context
= gst_context_new(LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE
, true);
430 gst_structure_set (gst_context_writable_structure (context
),
431 "handle", G_TYPE_POINTER
, display
, nullptr);
435 GstBusSyncReply
Player::processSyncMessage( GstMessage
*message
)
437 #if OSL_DEBUG_LEVEL > 0
438 if ( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ERROR
)
443 gst_message_parse_error( message
, &error
, &error_debug
);
446 "error: '" << error
->message
<< "' debug: '"
447 << error_debug
<< "'");
449 else if ( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_WARNING
)
454 gst_message_parse_warning( message
, &error
, &error_debug
);
457 "warning: '" << error
->message
<< "' debug: '"
458 << error_debug
<< "'");
460 else if ( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_INFO
)
465 gst_message_parse_info( message
, &error
, &error_debug
);
468 "info: '" << error
->message
<< "' debug: '"
469 << error_debug
<< "'");
475 if (gst_is_video_overlay_prepare_window_handle_message (message
) )
477 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " processSyncMessage prepare window id: " <<
478 GST_MESSAGE_TYPE_NAME( message
) << " " << static_cast<int>(mnWindowID
) );
480 g_object_unref( G_OBJECT ( mpXOverlay
) );
481 g_object_set( GST_MESSAGE_SRC( message
), "force-aspect-ratio", FALSE
, nullptr );
482 mpXOverlay
= GST_VIDEO_OVERLAY( GST_MESSAGE_SRC( message
) );
483 g_object_ref( G_OBJECT ( mpXOverlay
) );
484 if ( mnWindowID
!= 0 )
486 gst_video_overlay_set_window_handle( mpXOverlay
, mnWindowID
);
487 gst_video_overlay_handle_events(mpXOverlay
, 0); // Let the parent window handle events.
488 if (maArea
.Width
> 0 && maArea
.Height
> 0)
489 gst_video_overlay_set_render_rectangle(mpXOverlay
, maArea
.X
, maArea
.Y
, maArea
.Width
, maArea
.Height
);
494 else if (lcl_is_wayland_display_handle_need_context_message(message
))
496 GstContext
*context
= lcl_wayland_display_handle_context_new(mpDisplay
);
497 gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message
)), context
);
503 if( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ASYNC_DONE
) {
504 if( mnDuration
== 0) {
505 gint64 gst_duration
= 0;
506 if( gst_element_query_duration( mpPlaybin
, GST_FORMAT_TIME
, &gst_duration
) )
507 mnDuration
= gst_duration
;
510 GstPad
*pad
= nullptr;
512 g_signal_emit_by_name( mpPlaybin
, "get-video-pad", 0, &pad
);
517 GstCaps
*caps
= gst_pad_get_current_caps( pad
);
519 if( gst_structure_get( gst_caps_get_structure( caps
, 0 ),
520 "width", G_TYPE_INT
, &w
,
521 "height", G_TYPE_INT
, &h
,
526 SAL_INFO( "avmedia.gstreamer", AVVERSION
"queried size: " << mnWidth
<< "x" << mnHeight
);
529 gst_caps_unref( caps
);
530 g_object_unref( pad
);
533 maSizeCondition
.set();
535 } else if (gst_is_missing_plugin_message(message
)) {
536 TheMissingPluginInstaller().report(this, message
);
538 // an error occurred, set condition so that OOo thread doesn't wait for us
539 maSizeCondition
.set();
541 } else if( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ERROR
) {
543 // an error occurred, set condition so that OOo thread doesn't wait for us
544 maSizeCondition
.set();
551 void Player::preparePlaybin( std::u16string_view rURL
, GstElement
*pSink
)
553 if (mpPlaybin
!= nullptr)
555 gst_element_set_state( mpPlaybin
, GST_STATE_NULL
);
556 g_object_unref( mpPlaybin
);
559 mpPlaybin
= gst_element_factory_make( "playbin", nullptr );
561 //tdf#96989 on systems with flat-volumes setting the volume directly on the
562 //playbin to 100% results in setting the global volume to 100% of the
563 //maximum. We expect to set as % of the current volume.
564 mpVolumeControl
= gst_element_factory_make( "volume", nullptr );
565 GstElement
*pAudioSink
= gst_element_factory_make( "autoaudiosink", nullptr );
566 GstElement
* pAudioOutput
= gst_bin_new("audio-output-bin");
567 assert(pAudioOutput
);
569 gst_bin_add(GST_BIN(pAudioOutput
), pAudioSink
);
572 gst_bin_add(GST_BIN(pAudioOutput
), mpVolumeControl
);
574 gst_element_link(mpVolumeControl
, pAudioSink
);
575 GstPad
*pPad
= gst_element_get_static_pad(mpVolumeControl
, "sink");
576 gst_element_add_pad(GST_ELEMENT(pAudioOutput
), gst_ghost_pad_new("sink", pPad
));
577 gst_object_unref(GST_OBJECT(pPad
));
579 g_object_set(G_OBJECT(mpPlaybin
), "audio-sink", pAudioOutput
, nullptr);
581 if( pSink
!= nullptr ) // used for getting preferred size etc.
583 g_object_set( G_OBJECT( mpPlaybin
), "video-sink", pSink
, nullptr );
589 OString ascURL
= OUStringToOString( rURL
, RTL_TEXTENCODING_UTF8
);
590 g_object_set( G_OBJECT( mpPlaybin
), "uri", ascURL
.getStr() , nullptr );
592 GstBus
*pBus
= gst_element_get_bus( mpPlaybin
);
595 g_source_remove(mnWatchID
);
598 mnWatchID
= gst_bus_add_watch( pBus
, pipeline_bus_callback
, this );
600 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " set sync handler" );
601 gst_bus_set_sync_handler( pBus
, pipeline_bus_sync_handler
, this, nullptr );
602 g_object_unref( pBus
);
606 bool Player::create( const OUString
& rURL
)
610 // create all the elements and link them
612 SAL_INFO( "avmedia.gstreamer", "create player, URL: '" << rURL
<< "'" );
614 if( mbInitialized
&& !rURL
.isEmpty() )
616 // fakesink for pre-roll & sizing ...
617 preparePlaybin( rURL
, gst_element_factory_make( "fakesink", nullptr ) );
619 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
632 void SAL_CALL
Player::start()
634 ::osl::MutexGuard
aGuard(m_aMutex
);
636 // set the pipeline state to READY and run the loop
637 if( mbInitialized
&& mpPlaybin
!= nullptr )
639 gst_element_set_state( mpPlaybin
, GST_STATE_PLAYING
);
642 SAL_INFO( "avmedia.gstreamer", AVVERSION
"start " << mpPlaybin
);
645 void SAL_CALL
Player::stop()
647 ::osl::MutexGuard
aGuard(m_aMutex
);
649 // set the pipeline in PAUSED STATE
651 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
653 SAL_INFO( "avmedia.gstreamer", AVVERSION
"stop " << mpPlaybin
);
656 sal_Bool SAL_CALL
Player::isPlaying()
658 ::osl::MutexGuard
aGuard(m_aMutex
);
662 // return whether the pipeline target is PLAYING STATE or not
663 if (mbInitialized
&& mpPlaybin
)
665 bRet
= GST_STATE_TARGET(mpPlaybin
) == GST_STATE_PLAYING
;
671 double SAL_CALL
Player::getDuration()
673 ::osl::MutexGuard
aGuard(m_aMutex
);
675 // slideshow checks for non-zero duration, so cheat here
676 double duration
= 0.3;
678 if( mpPlaybin
&& mnDuration
> 0 ) {
679 duration
= mnDuration
/ GST_SECOND
;
686 void SAL_CALL
Player::setMediaTime( double fTime
)
688 ::osl::MutexGuard
aGuard(m_aMutex
);
693 gint64 gst_position
= llround (fTime
* GST_SECOND
);
695 gst_element_seek( mpPlaybin
, 1.0,
698 GST_SEEK_TYPE_SET
, gst_position
,
699 GST_SEEK_TYPE_NONE
, 0 );
701 SAL_INFO( "avmedia.gstreamer", AVVERSION
"seek to: " << gst_position
<< " ns original: " << fTime
<< " s" );
705 double SAL_CALL
Player::getMediaTime()
707 ::osl::MutexGuard
aGuard(m_aMutex
);
709 double position
= 0.0;
712 // get current position in the stream
714 if( gst_element_query_position( mpPlaybin
, GST_FORMAT_TIME
, &gst_position
) )
715 position
= gst_position
/ GST_SECOND
;
722 void SAL_CALL
Player::setPlaybackLoop( sal_Bool bSet
)
724 ::osl::MutexGuard
aGuard(m_aMutex
);
725 // TODO check how to do with GST
730 sal_Bool SAL_CALL
Player::isPlaybackLoop()
732 ::osl::MutexGuard
aGuard(m_aMutex
);
733 // TODO check how to do with GST
738 void SAL_CALL
Player::setMute( sal_Bool bSet
)
740 ::osl::MutexGuard
aGuard(m_aMutex
);
742 SAL_INFO( "avmedia.gstreamer", AVVERSION
"set mute: " << bSet
<< " muted: " << mbMuted
<< " unmuted volume: " << mnUnmutedVolume
);
744 // change the volume to 0 or the unmuted volume
745 if (mpVolumeControl
&& mbMuted
!= bool(bSet
))
747 double nVolume
= mnUnmutedVolume
;
753 g_object_set( G_OBJECT( mpVolumeControl
), "volume", nVolume
, nullptr );
760 sal_Bool SAL_CALL
Player::isMute()
762 ::osl::MutexGuard
aGuard(m_aMutex
);
768 void SAL_CALL
Player::setVolumeDB( sal_Int16 nVolumeDB
)
770 ::osl::MutexGuard
aGuard(m_aMutex
);
772 mnUnmutedVolume
= pow( 10.0, nVolumeDB
/ 20.0 );
774 SAL_INFO( "avmedia.gstreamer", AVVERSION
"set volume: " << nVolumeDB
<< " gst volume: " << mnUnmutedVolume
);
777 if (mpVolumeControl
&& !mbMuted
)
779 g_object_set( G_OBJECT( mpVolumeControl
), "volume", mnUnmutedVolume
, nullptr );
784 sal_Int16 SAL_CALL
Player::getVolumeDB()
786 ::osl::MutexGuard
aGuard(m_aMutex
);
788 sal_Int16
nVolumeDB(0);
792 double nGstVolume
= 0.0;
794 g_object_get( G_OBJECT( mpVolumeControl
), "volume", &nGstVolume
, nullptr );
796 nVolumeDB
= static_cast<sal_Int16
>( 20.0*log10 ( nGstVolume
) );
803 awt::Size SAL_CALL
Player::getPreferredPlayerWindowSize()
805 ::osl::MutexGuard
aGuard(m_aMutex
);
807 awt::Size
aSize( 0, 0 );
809 if( maURL
.isEmpty() )
811 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::getPreferredPlayerWindowSize - empty URL => 0x0" );
815 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " pre-Player::getPreferredPlayerWindowSize, member " << mnWidth
<< "x" << mnHeight
);
817 osl::Condition::Result aResult
= maSizeCondition
.wait( std::chrono::seconds(10) );
819 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::getPreferredPlayerWindowSize after waitCondition " << aResult
<< ", member " << mnWidth
<< "x" << mnHeight
);
821 if( mnWidth
!= 0 && mnHeight
!= 0 ) {
822 aSize
.Width
= mnWidth
;
823 aSize
.Height
= mnHeight
;
829 uno::Reference
< ::media::XPlayerWindow
> SAL_CALL
Player::createPlayerWindow( const uno::Sequence
< uno::Any
>& rArguments
)
831 ::osl::MutexGuard
aGuard(m_aMutex
);
833 uno::Reference
< ::media::XPlayerWindow
> xRet
;
835 if (rArguments
.getLength() > 1)
836 rArguments
[1] >>= maArea
;
838 awt::Size aSize
= getPreferredPlayerWindowSize();
841 preparePlaybin( maURL
, nullptr );
843 SAL_INFO( "avmedia.gstreamer", AVVERSION
"Player::createPlayerWindow " << aSize
.Width
<< "x" << aSize
.Height
<< " length: " << rArguments
.getLength() );
845 if( aSize
.Width
> 0 && aSize
.Height
> 0 )
847 if (rArguments
.getLength() <= 2)
849 xRet
= new ::avmedia::gstreamer::Window
;
853 sal_IntPtr pIntPtr
= 0;
854 rArguments
[ 2 ] >>= pIntPtr
;
855 SystemChildWindow
*pParentWindow
= reinterpret_cast< SystemChildWindow
* >( pIntPtr
);
859 const SystemEnvData
* pEnvData
= pParentWindow
->GetSystemData();
863 // tdf#124027: the position of embedded window is identical w/ the position
864 // of media object in all other vclplugs (kf5, gen), in gtk3 w/o gtksink it
865 // needs to be translated
866 if (pEnvData
->toolkit
== SystemEnvData::Toolkit::Gtk
)
868 Point aPoint
= pParentWindow
->GetPosPixel();
869 maArea
.X
= aPoint
.getX();
870 maArea
.Y
= aPoint
.getY();
873 mbUseGtkSink
= false;
875 GstElement
*pVideosink
= static_cast<GstElement
*>(pParentWindow
->CreateGStreamerSink());
878 if (pEnvData
->toolkit
== SystemEnvData::Toolkit::Gtk
)
883 if (pEnvData
->platform
== SystemEnvData::Platform::Wayland
)
884 pVideosink
= gst_element_factory_make("waylandsink", "video-output");
886 pVideosink
= gst_element_factory_make("autovideosink", "video-output");
891 xRet
= new ::avmedia::gstreamer::Window
;
893 g_object_set(G_OBJECT(mpPlaybin
), "video-sink", pVideosink
, nullptr);
894 g_object_set(G_OBJECT(mpPlaybin
), "force-aspect-ratio", FALSE
, nullptr);
896 if ((rArguments
.getLength() >= 4) && (rArguments
[3] >>= pIntPtr
) && pIntPtr
)
898 auto pItem
= reinterpret_cast<const avmedia::MediaItem
*>(pIntPtr
);
899 Graphic aGraphic
= pItem
->getGraphic();
900 const text::GraphicCrop
& rCrop
= pItem
->getCrop();
901 if (!aGraphic
.IsNone() && (rCrop
.Bottom
> 0 || rCrop
.Left
> 0 || rCrop
.Right
> 0 || rCrop
.Top
> 0))
903 // The media item has a non-empty cropping set. Try to crop the video accordingly.
904 Size aPref
= aGraphic
.GetPrefSize();
905 Size aPixel
= aGraphic
.GetSizePixel();
906 tools::Long nLeft
= aPixel
.getWidth() * rCrop
.Left
/ aPref
.getWidth();
907 tools::Long nTop
= aPixel
.getHeight() * rCrop
.Top
/ aPref
.getHeight();
908 tools::Long nRight
= aPixel
.getWidth() * rCrop
.Right
/ aPref
.getWidth();
909 tools::Long nBottom
= aPixel
.getHeight() * rCrop
.Bottom
/ aPref
.getHeight();
910 GstElement
* pVideoFilter
= gst_element_factory_make("videocrop", nullptr);
913 g_object_set(G_OBJECT(pVideoFilter
), "left", nLeft
, nullptr);
914 g_object_set(G_OBJECT(pVideoFilter
), "top", nTop
, nullptr);
915 g_object_set(G_OBJECT(pVideoFilter
), "right", nRight
, nullptr);
916 g_object_set(G_OBJECT(pVideoFilter
), "bottom", nBottom
, nullptr);
917 g_object_set(G_OBJECT(mpPlaybin
), "video-filter", pVideoFilter
, nullptr);
924 mnWindowID
= pEnvData
->GetWindowHandle(pParentWindow
->ImplGetFrame());
925 mpDisplay
= pEnvData
->pDisplay
;
926 SAL_INFO( "avmedia.gstreamer", AVVERSION
"set window id to " << static_cast<int>(mnWindowID
) << " XOverlay " << mpXOverlay
);
928 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
929 if (!mbUseGtkSink
&& mpXOverlay
)
930 gst_video_overlay_set_window_handle( mpXOverlay
, mnWindowID
);
936 uno::Reference
< media::XFrameGrabber
> SAL_CALL
Player::createFrameGrabber()
938 ::osl::MutexGuard
aGuard(m_aMutex
);
939 rtl::Reference
<FrameGrabber
> pFrameGrabber
;
940 const awt::Size
aPrefSize( getPreferredPlayerWindowSize() );
942 if( ( aPrefSize
.Width
> 0 ) && ( aPrefSize
.Height
> 0 ) )
943 pFrameGrabber
= FrameGrabber::create( maURL
);
944 SAL_INFO( "avmedia.gstreamer", AVVERSION
"created FrameGrabber " << pFrameGrabber
.get() );
946 return pFrameGrabber
;
950 OUString SAL_CALL
Player::getImplementationName()
952 return u
"com.sun.star.comp.avmedia.Player_GStreamer"_ustr
;
956 sal_Bool SAL_CALL
Player::supportsService( const OUString
& ServiceName
)
958 return cppu::supportsService(this, ServiceName
);
962 uno::Sequence
< OUString
> SAL_CALL
Player::getSupportedServiceNames()
964 return { u
"com.sun.star.media.Player_GStreamer"_ustr
};
969 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */