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>
32 #include <cppuhelper/supportsservice.hxx>
34 #include <sal/log.hxx>
35 #include <rtl/string.hxx>
36 #include <salhelper/thread.hxx>
37 #include <vcl/svapp.hxx>
38 #include <vcl/syschild.hxx>
39 #include <vcl/sysdata.hxx>
41 #include "gstplayer.hxx"
42 #include "gstframegrabber.hxx"
43 #include "gstwindow.hxx"
45 #ifdef AVMEDIA_GST_0_10
46 # define AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_GStreamer_0_10"
47 # define AVMEDIA_GST_PLAYER_SERVICENAME "com.sun.star.media.Player_GStreamer_0_10"
49 # include <gst/video/videooverlay.h>
50 # define AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_GStreamer"
51 # define AVMEDIA_GST_PLAYER_SERVICENAME "com.sun.star.media.Player_GStreamer"
54 #include <gst/pbutils/missing-plugins.h>
55 #include <gst/pbutils/pbutils.h>
57 #ifdef AVMEDIA_GST_0_10
58 # define AVVERSION "gst 0.10: "
60 # define AVVERSION "gst 1.0: "
63 using namespace ::com::sun::star
;
65 namespace avmedia
{ namespace gstreamer
{
71 explicit FlagGuard(bool & flag
): flag_(flag
) { flag_
= true; }
73 ~FlagGuard() { flag_
= false; }
79 class MissingPluginInstallerThread
: public salhelper::Thread
{
81 MissingPluginInstallerThread(): Thread("MissingPluginInstaller") {}
84 void execute() override
;
88 class MissingPluginInstaller
{
89 friend class MissingPluginInstallerThread
;
92 MissingPluginInstaller(): launchNewThread_(true), inCleanUp_(false) {}
94 ~MissingPluginInstaller();
96 void report(rtl::Reference
<Player
> const & source
, GstMessage
* message
);
98 // Player::~Player calls Player::disposing calls
99 // MissingPluginInstaller::detach, so do not take Player by rtl::Reference
100 // here (which would bump its refcount back from 0 to 1):
101 void detach(Player
const * source
);
106 DECL_STATIC_LINK(MissingPluginInstaller
, launchUi
, void*, void);
109 std::set
<OString
> reported_
;
110 std::map
<OString
, std::set
<rtl::Reference
<Player
>>> queued_
;
111 rtl::Reference
<MissingPluginInstallerThread
> currentThread_
;
112 std::vector
<OString
> currentDetails_
;
113 std::set
<rtl::Reference
<Player
>> currentSources_
;
114 bool launchNewThread_
;
119 MissingPluginInstaller::~MissingPluginInstaller() {
120 osl::MutexGuard
g(mutex_
);
121 SAL_WARN_IF(currentThread_
.is(), "avmedia.gstreamer", "unjoined thread");
126 void MissingPluginInstaller::report(
127 rtl::Reference
<Player
> const & source
, GstMessage
* message
)
129 // assert(gst_is_missing_plugin_message(message));
130 gchar
* det
= gst_missing_plugin_message_get_installer_detail(message
);
131 if (det
== nullptr) {
134 "gst_missing_plugin_message_get_installer_detail failed");
137 std::size_t len
= std::strlen(det
);
138 if (len
> sal_uInt32(SAL_MAX_INT32
)) {
139 SAL_WARN("avmedia.gstreamer", "detail string too long");
143 OString
detStr(det
, len
);
145 rtl::Reference
<MissingPluginInstallerThread
> join
;
146 rtl::Reference
<MissingPluginInstallerThread
> launch
;
148 osl::MutexGuard
g(mutex_
);
149 if (reported_
.find(detStr
) != reported_
.end()) {
152 auto & i
= queued_
[detStr
];
153 bool fresh
= i
.empty();
155 if (!(fresh
&& launchNewThread_
)) {
158 join
= currentThread_
;
159 currentThread_
= new MissingPluginInstallerThread
;
161 FlagGuard
f(inCleanUp_
);
162 currentSources_
.clear();
165 launchNewThread_
= false;
166 launch
= currentThread_
;
172 Application::PostUserEvent(
173 LINK(this, MissingPluginInstaller
, launchUi
), launch
.get());
177 void eraseSource(std::set
<rtl::Reference
<Player
>> & set
, Player
const * source
)
179 auto i
= std::find_if(
180 set
.begin(), set
.end(),
181 [source
](rtl::Reference
<Player
> const & el
) {
182 return el
.get() == source
;
184 if (i
!= set
.end()) {
190 void MissingPluginInstaller::detach(Player
const * source
) {
191 rtl::Reference
<MissingPluginInstallerThread
> join
;
193 osl::MutexGuard
g(mutex_
);
195 // Guard against ~MissingPluginInstaller with erroneously un-joined
196 // currentThread_ (thus non-empty currentSources_) calling
197 // destructor of currentSources_, calling ~Player, calling here,
198 // which would use currentSources_ while it is already being
202 for (auto i
= queued_
.begin(); i
!= queued_
.end();) {
203 eraseSource(i
->second
, source
);
204 if (i
->second
.empty()) {
205 i
= queued_
.erase(i
);
210 if (currentThread_
.is()) {
211 assert(!currentSources_
.empty());
212 eraseSource(currentSources_
, source
);
213 if (currentSources_
.empty()) {
214 join
= currentThread_
;
215 currentThread_
.clear();
216 launchNewThread_
= true;
221 // missing cancellability of gst_install_plugins_sync
227 void MissingPluginInstaller::processQueue() {
228 assert(!queued_
.empty());
229 assert(currentDetails_
.empty());
230 for (const auto& rEntry
: queued_
) {
231 reported_
.insert(rEntry
.first
);
232 currentDetails_
.push_back(rEntry
.first
);
233 currentSources_
.insert(rEntry
.second
.begin(), rEntry
.second
.end());
239 IMPL_STATIC_LINK(MissingPluginInstaller
, launchUi
, void *, p
, void)
241 MissingPluginInstallerThread
* thread
= static_cast<MissingPluginInstallerThread
*>(p
);
242 rtl::Reference
<MissingPluginInstallerThread
> ref(thread
, SAL_NO_ACQUIRE
);
244 // not thread safe; hopefully fine to consistently call from our event
245 // loop (which is the only reason to have this
246 // Application::PostUserEvent diversion, in case
247 // MissingPluginInstaller::report might be called from outside our event
248 // loop), and hopefully fine to call gst_is_missing_plugin_message and
249 // gst_missing_plugin_message_get_installer_detail before calling
255 struct TheMissingPluginInstaller
:
256 public rtl::Static
<MissingPluginInstaller
, TheMissingPluginInstaller
>
260 void MissingPluginInstallerThread::execute() {
261 MissingPluginInstaller
& inst
= TheMissingPluginInstaller::get();
263 std::vector
<OString
> details
;
265 osl::MutexGuard
g(inst
.mutex_
);
266 assert(!inst
.currentDetails_
.empty());
267 details
.swap(inst
.currentDetails_
);
269 std::vector
<char *> args
;
270 args
.reserve(details
.size());
271 for (auto const& i
: details
)
273 args
.push_back(const_cast<char *>(i
.getStr()));
275 args
.push_back(nullptr);
276 gst_install_plugins_sync(args
.data(), nullptr);
278 osl::MutexGuard
g(inst
.mutex_
);
279 if (inst
.queued_
.empty() || inst
.launchNewThread_
) {
280 inst
.launchNewThread_
= true;
288 } // end anonymous namespace
292 GstPlayer_BASE( m_aMutex
),
293 mpPlaybin( nullptr ),
294 mpVolumeControl( nullptr ),
295 #if defined(ENABLE_GTKSINK)
296 mpGtkWidget( nullptr ),
298 mbUseGtkSink( false ),
299 mbFakeVideo (false ),
300 mnUnmutedVolume( 0 ),
301 mbPlayPending ( false ),
304 mbInitialized( false ),
305 mpDisplay( nullptr ),
307 mpXOverlay( nullptr ),
314 // Initialize GStreamer library
316 char name
[] = "libreoffice";
317 char *arguments
[] = { name
};
318 char** argv
= arguments
;
319 GError
* pError
= nullptr;
321 mbInitialized
= gst_init_check( &argc
, &argv
, &pError
);
323 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::Player" );
325 if (pError
!= nullptr)
327 // TODO: throw an exception?
328 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::Player error '" << pError
->message
<< "'" );
329 g_error_free (pError
);
336 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::~Player" );
342 void SAL_CALL
Player::disposing()
344 TheMissingPluginInstaller::get().detach(this);
346 ::osl::MutexGuard
aGuard(m_aMutex
);
350 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::disposing" );
352 // Release the elements and pipeline
355 #if defined(ENABLE_GTKSINK)
358 gtk_widget_destroy(mpGtkWidget
);
359 mpGtkWidget
= nullptr;
365 gst_element_set_state( mpPlaybin
, GST_STATE_NULL
);
366 g_object_unref( G_OBJECT( mpPlaybin
) );
369 mpVolumeControl
= nullptr;
373 g_object_unref( G_OBJECT ( mpXOverlay
) );
374 mpXOverlay
= nullptr;
380 g_source_remove(mnWatchID
);
386 static gboolean
pipeline_bus_callback( GstBus
*, GstMessage
*message
, gpointer data
)
388 Player
* pPlayer
= static_cast<Player
*>(data
);
390 pPlayer
->processMessage( message
);
396 static GstBusSyncReply
pipeline_bus_sync_handler( GstBus
*, GstMessage
* message
, gpointer data
)
398 Player
* pPlayer
= static_cast<Player
*>(data
);
400 return pPlayer
->processSyncMessage( message
);
404 void Player::processMessage( GstMessage
*message
)
406 switch( GST_MESSAGE_TYPE( message
) ) {
407 case GST_MESSAGE_EOS
:
408 gst_element_set_state( mpPlaybin
, GST_STATE_READY
);
409 mbPlayPending
= false;
413 case GST_MESSAGE_STATE_CHANGED
:
414 if (message
->src
== GST_OBJECT(mpPlaybin
))
416 GstState newstate
, pendingstate
;
418 gst_message_parse_state_changed (message
, nullptr, &newstate
, &pendingstate
);
420 if (!mbUseGtkSink
&& newstate
== GST_STATE_PAUSED
&&
421 pendingstate
== GST_STATE_VOID_PENDING
&& mpXOverlay
)
423 gst_video_overlay_expose(mpXOverlay
);
427 mbPlayPending
= ((newstate
== GST_STATE_READY
) || (newstate
== GST_STATE_PAUSED
));
436 static gboolean
wrap_element_query_position (GstElement
*element
, GstFormat format
, gint64
*cur
)
438 #ifdef AVMEDIA_GST_0_10
439 GstFormat my_format
= format
;
440 return gst_element_query_position( element
, &my_format
, cur
) && my_format
== format
&& *cur
> 0;
442 return gst_element_query_position( element
, format
, cur
);
447 static gboolean
wrap_element_query_duration (GstElement
*element
, GstFormat format
, gint64
*duration
)
449 #ifdef AVMEDIA_GST_0_10
450 GstFormat my_format
= format
;
451 return gst_element_query_duration( element
, &my_format
, duration
) && my_format
== format
&& *duration
> 0;
453 return gst_element_query_duration( element
, format
, duration
);
457 #ifndef AVMEDIA_GST_0_10
459 #define LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE "GstWaylandDisplayHandleContextType"
461 static gboolean
lcl_is_wayland_display_handle_need_context_message(GstMessage
* msg
)
463 g_return_val_if_fail(GST_IS_MESSAGE(msg
), false);
465 if (GST_MESSAGE_TYPE(msg
) != GST_MESSAGE_NEED_CONTEXT
)
467 const gchar
*type
= nullptr;
468 if (!gst_message_parse_context_type(msg
, &type
))
470 return !g_strcmp0(type
, LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE
);
473 static GstContext
* lcl_wayland_display_handle_context_new(void* display
)
475 GstContext
*context
= gst_context_new(LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE
, TRUE
);
476 gst_structure_set (gst_context_writable_structure (context
),
477 "handle", G_TYPE_POINTER
, display
, nullptr);
483 GstBusSyncReply
Player::processSyncMessage( GstMessage
*message
)
485 #if OSL_DEBUG_LEVEL > 0
486 if ( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ERROR
)
491 gst_message_parse_error( message
, &error
, &error_debug
);
494 "error: '" << error
->message
<< "' debug: '"
495 << error_debug
<< "'");
501 #ifdef AVMEDIA_GST_0_10
502 if (message
->structure
&&
503 !strcmp( gst_structure_get_name( message
->structure
), "prepare-xwindow-id" ) )
505 if (gst_is_video_overlay_prepare_window_handle_message (message
) )
508 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " processSyncMessage prepare window id: " <<
509 GST_MESSAGE_TYPE_NAME( message
) << " " << static_cast<int>(mnWindowID
) );
511 g_object_unref( G_OBJECT ( mpXOverlay
) );
512 g_object_set( GST_MESSAGE_SRC( message
), "force-aspect-ratio", FALSE
, nullptr );
513 mpXOverlay
= GST_VIDEO_OVERLAY( GST_MESSAGE_SRC( message
) );
514 g_object_ref( G_OBJECT ( mpXOverlay
) );
515 if ( mnWindowID
!= 0 )
517 gst_video_overlay_set_window_handle( mpXOverlay
, mnWindowID
);
518 #ifndef AVMEDIA_GST_0_10
519 gst_video_overlay_handle_events(mpXOverlay
, 0); // Let the parent window handle events.
520 if (maArea
.Width
> 0 && maArea
.Height
> 0)
521 gst_video_overlay_set_render_rectangle(mpXOverlay
, maArea
.X
, maArea
.Y
, maArea
.Width
, maArea
.Height
);
527 #ifndef AVMEDIA_GST_0_10
528 else if (lcl_is_wayland_display_handle_need_context_message(message
))
530 GstContext
*context
= lcl_wayland_display_handle_context_new(mpDisplay
);
531 gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message
)), context
);
538 #ifdef AVMEDIA_GST_0_10
539 if( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_STATE_CHANGED
) {
540 if( message
->src
== GST_OBJECT( mpPlaybin
) ) {
541 GstState newstate
, pendingstate
;
543 gst_message_parse_state_changed (message
, nullptr, &newstate
, &pendingstate
);
545 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " state change received, new state " << static_cast<int>(newstate
) << " pending " << static_cast<int>(pendingstate
) );
546 if( newstate
== GST_STATE_PAUSED
&&
547 pendingstate
== GST_STATE_VOID_PENDING
) {
549 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " change to paused received" );
551 if( mnDuration
== 0) {
552 gint64 gst_duration
= 0;
553 if( wrap_element_query_duration( mpPlaybin
, GST_FORMAT_TIME
, &gst_duration
) )
554 mnDuration
= gst_duration
;
558 GList
*pStreamInfo
= nullptr;
560 g_object_get( G_OBJECT( mpPlaybin
), "stream-info", &pStreamInfo
, nullptr );
562 for ( ; pStreamInfo
!= nullptr; pStreamInfo
= pStreamInfo
->next
) {
563 GObject
*pInfo
= G_OBJECT( pStreamInfo
->data
);
569 g_object_get( pInfo
, "type", &nType
, nullptr );
570 GEnumValue
*pValue
= g_enum_get_value( G_PARAM_SPEC_ENUM( g_object_class_find_property( G_OBJECT_GET_CLASS( pInfo
), "type" ) )->enum_class
,
573 if( !g_ascii_strcasecmp( pValue
->value_nick
, "video" ) ) {
574 GstStructure
*pStructure
;
577 g_object_get( pInfo
, "object", &pPad
, nullptr );
578 pStructure
= gst_caps_get_structure( GST_PAD_CAPS( pPad
), 0 );
580 gst_structure_get_int( pStructure
, "width", &mnWidth
);
581 gst_structure_get_int( pStructure
, "height", &mnHeight
);
582 SAL_INFO( "avmedia.gstreamer", AVVERSION
"queried size: " << mnWidth
<< "x" << mnHeight
);
584 g_object_unref (pPad
);
588 maSizeCondition
.set();
593 // We get to use the exciting new playbin2 ! (now known as playbin)
594 if( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ASYNC_DONE
) {
595 if( mnDuration
== 0) {
596 gint64 gst_duration
= 0;
597 if( wrap_element_query_duration( mpPlaybin
, GST_FORMAT_TIME
, &gst_duration
) )
598 mnDuration
= gst_duration
;
601 GstPad
*pad
= nullptr;
603 g_signal_emit_by_name( mpPlaybin
, "get-video-pad", 0, &pad
);
608 GstCaps
*caps
= gst_pad_get_current_caps( pad
);
610 if( gst_structure_get( gst_caps_get_structure( caps
, 0 ),
611 "width", G_TYPE_INT
, &w
,
612 "height", G_TYPE_INT
, &h
,
617 SAL_INFO( "avmedia.gstreamer", AVVERSION
"queried size: " << mnWidth
<< "x" << mnHeight
);
620 gst_caps_unref( caps
);
621 g_object_unref( pad
);
624 maSizeCondition
.set();
626 #endif // AVMEDIA_GST_0_10
627 } else if (gst_is_missing_plugin_message(message
)) {
628 TheMissingPluginInstaller::get().report(this, message
);
630 // an error occurred, set condition so that OOo thread doesn't wait for us
631 maSizeCondition
.set();
633 } else if( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ERROR
) {
635 // an error occurred, set condition so that OOo thread doesn't wait for us
636 maSizeCondition
.set();
643 void Player::preparePlaybin( const OUString
& rURL
, GstElement
*pSink
)
645 #if defined(ENABLE_GTKSINK)
648 gtk_widget_destroy(mpGtkWidget
);
649 mpGtkWidget
= nullptr;
653 if (mpPlaybin
!= nullptr)
655 gst_element_set_state( mpPlaybin
, GST_STATE_NULL
);
656 mbPlayPending
= false;
657 g_object_unref( mpPlaybin
);
660 mpPlaybin
= gst_element_factory_make( "playbin", nullptr );
662 //tdf#96989 on systems with flat-volumes setting the volume directly on the
663 //playbin to 100% results in setting the global volume to 100% of the
664 //maximum. We expect to set as % of the current volume.
665 mpVolumeControl
= gst_element_factory_make( "volume", nullptr );
666 GstElement
*pAudioSink
= gst_element_factory_make( "autoaudiosink", nullptr );
667 GstElement
* pAudioOutput
= gst_bin_new("audio-output-bin");
668 gst_bin_add_many(GST_BIN(pAudioOutput
), mpVolumeControl
, pAudioSink
, nullptr);
669 gst_element_link(mpVolumeControl
, pAudioSink
);
670 GstPad
*pPad
= gst_element_get_static_pad(mpVolumeControl
, "sink");
671 gst_element_add_pad(GST_ELEMENT(pAudioOutput
), gst_ghost_pad_new("sink", pPad
));
672 gst_object_unref(GST_OBJECT(pPad
));
673 g_object_set(G_OBJECT(mpPlaybin
), "audio-sink", pAudioOutput
, nullptr);
675 if( pSink
!= nullptr ) // used for getting preferred size etc.
677 g_object_set( G_OBJECT( mpPlaybin
), "video-sink", pSink
, nullptr );
683 OString ascURL
= OUStringToOString( rURL
, RTL_TEXTENCODING_UTF8
);
684 g_object_set( G_OBJECT( mpPlaybin
), "uri", ascURL
.getStr() , nullptr );
686 GstBus
*pBus
= gst_element_get_bus( mpPlaybin
);
689 g_source_remove(mnWatchID
);
692 mnWatchID
= gst_bus_add_watch( pBus
, pipeline_bus_callback
, this );
694 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " set sync handler" );
695 #ifdef AVMEDIA_GST_0_10
696 gst_bus_set_sync_handler( pBus
, pipeline_bus_sync_handler
, this );
698 gst_bus_set_sync_handler( pBus
, pipeline_bus_sync_handler
, this, nullptr );
700 g_object_unref( pBus
);
704 bool Player::create( const OUString
& rURL
)
708 // create all the elements and link them
710 SAL_INFO( "avmedia.gstreamer", "create player, URL: '" << rURL
<< "'" );
712 if( mbInitialized
&& !rURL
.isEmpty() )
714 // fakesink for pre-roll & sizing ...
715 preparePlaybin( rURL
, gst_element_factory_make( "fakesink", nullptr ) );
717 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
718 mbPlayPending
= false;
732 void SAL_CALL
Player::start()
734 ::osl::MutexGuard
aGuard(m_aMutex
);
736 // set the pipeline state to READY and run the loop
737 if( mbInitialized
&& mpPlaybin
!= nullptr )
739 gst_element_set_state( mpPlaybin
, GST_STATE_PLAYING
);
740 mbPlayPending
= true;
745 void SAL_CALL
Player::stop()
747 ::osl::MutexGuard
aGuard(m_aMutex
);
749 // set the pipeline in PAUSED STATE
751 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
753 mbPlayPending
= false;
754 SAL_INFO( "avmedia.gstreamer", AVVERSION
"stop " << mpPlaybin
);
758 sal_Bool SAL_CALL
Player::isPlaying()
760 ::osl::MutexGuard
aGuard(m_aMutex
);
762 bool bRet
= mbPlayPending
;
764 // return whether the pipeline is in PLAYING STATE or not
765 if( !mbPlayPending
&& mbInitialized
&& mpPlaybin
)
767 bRet
= GST_STATE( mpPlaybin
) == GST_STATE_PLAYING
;
770 SAL_INFO( "avmedia.gstreamer", AVVERSION
"isPlaying " << bRet
);
776 double SAL_CALL
Player::getDuration()
778 ::osl::MutexGuard
aGuard(m_aMutex
);
780 // slideshow checks for non-zero duration, so cheat here
781 double duration
= 0.3;
783 if( mpPlaybin
&& mnDuration
> 0 ) {
784 duration
= mnDuration
/ GST_SECOND
;
791 void SAL_CALL
Player::setMediaTime( double fTime
)
793 ::osl::MutexGuard
aGuard(m_aMutex
);
796 gint64 gst_position
= llround (fTime
* GST_SECOND
);
798 gst_element_seek( mpPlaybin
, 1.0,
801 GST_SEEK_TYPE_SET
, gst_position
,
802 GST_SEEK_TYPE_NONE
, 0 );
804 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
806 SAL_INFO( "avmedia.gstreamer", AVVERSION
"seek to: " << gst_position
<< " ns original: " << fTime
<< " s" );
811 double SAL_CALL
Player::getMediaTime()
813 ::osl::MutexGuard
aGuard(m_aMutex
);
815 double position
= 0.0;
818 // get current position in the stream
820 if( wrap_element_query_position( mpPlaybin
, GST_FORMAT_TIME
, &gst_position
) )
821 position
= gst_position
/ GST_SECOND
;
828 void SAL_CALL
Player::setPlaybackLoop( sal_Bool bSet
)
830 ::osl::MutexGuard
aGuard(m_aMutex
);
831 // TODO check how to do with GST
836 sal_Bool SAL_CALL
Player::isPlaybackLoop()
838 ::osl::MutexGuard
aGuard(m_aMutex
);
839 // TODO check how to do with GST
844 void SAL_CALL
Player::setMute( sal_Bool bSet
)
846 ::osl::MutexGuard
aGuard(m_aMutex
);
848 SAL_INFO( "avmedia.gstreamer", AVVERSION
"set mute: " << bSet
<< " muted: " << mbMuted
<< " unmuted volume: " << mnUnmutedVolume
);
850 // change the volume to 0 or the unmuted volume
851 if( mpPlaybin
&& mbMuted
!= bool(bSet
) )
853 double nVolume
= mnUnmutedVolume
;
859 g_object_set( G_OBJECT( mpVolumeControl
), "volume", nVolume
, nullptr );
866 sal_Bool SAL_CALL
Player::isMute()
868 ::osl::MutexGuard
aGuard(m_aMutex
);
874 void SAL_CALL
Player::setVolumeDB( sal_Int16 nVolumeDB
)
876 ::osl::MutexGuard
aGuard(m_aMutex
);
878 mnUnmutedVolume
= pow( 10.0, nVolumeDB
/ 20.0 );
880 SAL_INFO( "avmedia.gstreamer", AVVERSION
"set volume: " << nVolumeDB
<< " gst volume: " << mnUnmutedVolume
);
883 if( !mbMuted
&& mpPlaybin
)
885 g_object_set( G_OBJECT( mpVolumeControl
), "volume", mnUnmutedVolume
, nullptr );
890 sal_Int16 SAL_CALL
Player::getVolumeDB()
892 ::osl::MutexGuard
aGuard(m_aMutex
);
894 sal_Int16
nVolumeDB(0);
897 double nGstVolume
= 0.0;
899 g_object_get( G_OBJECT( mpVolumeControl
), "volume", &nGstVolume
, nullptr );
901 nVolumeDB
= static_cast<sal_Int16
>( 20.0*log10 ( nGstVolume
) );
908 awt::Size SAL_CALL
Player::getPreferredPlayerWindowSize()
910 ::osl::MutexGuard
aGuard(m_aMutex
);
912 awt::Size
aSize( 0, 0 );
914 if( maURL
.isEmpty() )
916 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::getPreferredPlayerWindowSize - empty URL => 0x0" );
920 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " pre-Player::getPreferredPlayerWindowSize, member " << mnWidth
<< "x" << mnHeight
);
922 osl::Condition::Result aResult
= maSizeCondition
.wait( std::chrono::seconds(10) );
924 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::getPreferredPlayerWindowSize after waitCondition " << aResult
<< ", member " << mnWidth
<< "x" << mnHeight
);
926 if( mnWidth
!= 0 && mnHeight
!= 0 ) {
927 aSize
.Width
= mnWidth
;
928 aSize
.Height
= mnHeight
;
934 uno::Reference
< ::media::XPlayerWindow
> SAL_CALL
Player::createPlayerWindow( const uno::Sequence
< uno::Any
>& rArguments
)
936 ::osl::MutexGuard
aGuard(m_aMutex
);
938 uno::Reference
< ::media::XPlayerWindow
> xRet
;
941 if (rArguments
.getLength() > 1 && (rArguments
[1] >>= maArea
))
942 aSize
= awt::Size(maArea
.Width
, maArea
.Height
);
944 aSize
= getPreferredPlayerWindowSize();
947 preparePlaybin( maURL
, nullptr );
949 SAL_INFO( "avmedia.gstreamer", AVVERSION
"Player::createPlayerWindow " << aSize
.Width
<< "x" << aSize
.Height
<< " length: " << rArguments
.getLength() );
951 if( aSize
.Width
> 0 && aSize
.Height
> 0 )
953 ::avmedia::gstreamer::Window
* pWindow
= new ::avmedia::gstreamer::Window
;
957 if( rArguments
.getLength() > 2 )
959 sal_IntPtr pIntPtr
= 0;
960 rArguments
[ 2 ] >>= pIntPtr
;
961 SystemChildWindow
*pParentWindow
= reinterpret_cast< SystemChildWindow
* >( pIntPtr
);
963 const SystemEnvData
* pEnvData
= pParentWindow
? pParentWindow
->GetSystemData() : nullptr;
964 OSL_ASSERT(pEnvData
);
967 OUString aToolkit
= OUString::createFromAscii(pEnvData
->pToolkit
);
968 OUString aPlatform
= OUString::createFromAscii(pEnvData
->pPlatformName
);
970 // tdf#124027: the position of embedded window is identical w/ the position
971 // of media object in all other vclplugs (gtk, kde5, gen), in gtk3 w/o gtksink it
972 // needs to be translated
973 if (aToolkit
== "gtk3")
977 Point aPoint
= pParentWindow
->GetPosPixel();
978 maArea
.X
= aPoint
.getX();
979 maArea
.Y
= aPoint
.getY();
983 GstElement
*pVideosink
= nullptr;
984 #if defined(ENABLE_GTKSINK)
985 pVideosink
= (aToolkit
== "gtk3") ?
986 gst_element_factory_make("gtksink", "gtksink") : nullptr;
990 g_object_get(pVideosink
, "widget", &mpGtkWidget
, nullptr);
991 gtk_widget_set_vexpand(mpGtkWidget
, true);
992 gtk_widget_set_hexpand(mpGtkWidget
, true);
993 GtkWidget
*pParent
= static_cast<GtkWidget
*>(pEnvData
->pWidget
);
994 gtk_container_add (GTK_CONTAINER(pParent
), mpGtkWidget
);
996 g_object_set( G_OBJECT( mpPlaybin
), "video-sink", pVideosink
, nullptr);
997 g_object_set( G_OBJECT( mpPlaybin
), "force-aspect-ratio", FALSE
, nullptr);
999 gtk_widget_show_all (pParent
);
1004 if (aPlatform
== "wayland")
1005 pVideosink
= gst_element_factory_make("waylandsink", "video-output");
1007 pVideosink
= gst_element_factory_make("autovideosink", "video-output");
1013 g_object_set(G_OBJECT(mpPlaybin
), "video-sink", pVideosink
, nullptr);
1014 mbUseGtkSink
= false;
1015 mnWindowID
= pEnvData
->aWindow
;
1016 mpDisplay
= pEnvData
->pDisplay
;
1017 SAL_INFO( "avmedia.gstreamer", AVVERSION
"set window id to " << static_cast<int>(mnWindowID
) << " XOverlay " << mpXOverlay
);
1018 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
1019 if ( mpXOverlay
!= nullptr )
1020 gst_video_overlay_set_window_handle( mpXOverlay
, mnWindowID
);
1029 uno::Reference
< media::XFrameGrabber
> SAL_CALL
Player::createFrameGrabber()
1031 ::osl::MutexGuard
aGuard(m_aMutex
);
1032 FrameGrabber
* pFrameGrabber
= nullptr;
1033 const awt::Size
aPrefSize( getPreferredPlayerWindowSize() );
1035 if( ( aPrefSize
.Width
> 0 ) && ( aPrefSize
.Height
> 0 ) )
1036 pFrameGrabber
= FrameGrabber::create( maURL
);
1037 SAL_INFO( "avmedia.gstreamer", AVVERSION
"created FrameGrabber " << pFrameGrabber
);
1039 return pFrameGrabber
;
1043 OUString SAL_CALL
Player::getImplementationName()
1045 return OUString( AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME
);
1049 sal_Bool SAL_CALL
Player::supportsService( const OUString
& ServiceName
)
1051 return cppu::supportsService(this, ServiceName
);
1055 uno::Sequence
< OUString
> SAL_CALL
Player::getSupportedServiceNames()
1057 return { AVMEDIA_GST_PLAYER_SERVICENAME
};
1060 } // namespace gstreamer
1061 } // namespace avmedia
1063 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */