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 <rtl/string.hxx>
35 #include <salhelper/thread.hxx>
36 #include <vcl/svapp.hxx>
37 #include <vcl/syschild.hxx>
38 #include <vcl/sysdata.hxx>
40 #include "gstplayer.hxx"
41 #include "gstframegrabber.hxx"
42 #include "gstwindow.hxx"
44 #ifdef AVMEDIA_GST_0_10
45 # define AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_GStreamer_0_10"
46 # define AVMEDIA_GST_PLAYER_SERVICENAME "com.sun.star.media.Player_GStreamer_0_10"
48 # include <gst/video/videooverlay.h>
49 # define AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_GStreamer"
50 # define AVMEDIA_GST_PLAYER_SERVICENAME "com.sun.star.media.Player_GStreamer"
53 #include <gst/pbutils/missing-plugins.h>
54 #include <gst/pbutils/pbutils.h>
56 #ifdef AVMEDIA_GST_0_10
57 # define AVVERSION "gst 0.10: "
59 # define AVVERSION "gst 1.0: "
62 using namespace ::com::sun::star
;
64 namespace avmedia
{ namespace gstreamer
{
70 explicit FlagGuard(bool & flag
): flag_(flag
) { flag_
= true; }
72 ~FlagGuard() { flag_
= false; }
78 class MissingPluginInstallerThread
: public salhelper::Thread
{
80 MissingPluginInstallerThread(): Thread("MissingPluginInstaller") {}
83 void execute() override
;
87 class MissingPluginInstaller
{
88 friend class MissingPluginInstallerThread
;
91 MissingPluginInstaller(): launchNewThread_(true), inCleanUp_(false) {}
93 ~MissingPluginInstaller();
95 void report(rtl::Reference
<Player
> const & source
, GstMessage
* message
);
97 // Player::~Player calls Player::disposing calls
98 // MissingPluginInstaller::detach, so do not take Player by rtl::Reference
99 // here (which would bump its refcount back from 0 to 1):
100 void detach(Player
const * source
);
105 DECL_STATIC_LINK(MissingPluginInstaller
, launchUi
, void*, void);
108 std::set
<OString
> reported_
;
109 std::map
<OString
, std::set
<rtl::Reference
<Player
>>> queued_
;
110 rtl::Reference
<MissingPluginInstallerThread
> currentThread_
;
111 std::vector
<OString
> currentDetails_
;
112 std::set
<rtl::Reference
<Player
>> currentSources_
;
113 bool launchNewThread_
;
118 MissingPluginInstaller::~MissingPluginInstaller() {
119 osl::MutexGuard
g(mutex_
);
120 SAL_WARN_IF(currentThread_
.is(), "avmedia.gstreamer", "unjoined thread");
125 void MissingPluginInstaller::report(
126 rtl::Reference
<Player
> const & source
, GstMessage
* message
)
128 // assert(gst_is_missing_plugin_message(message));
129 gchar
* det
= gst_missing_plugin_message_get_installer_detail(message
);
130 if (det
== nullptr) {
133 "gst_missing_plugin_message_get_installer_detail failed");
136 std::size_t len
= std::strlen(det
);
137 if (len
> sal_uInt32(SAL_MAX_INT32
)) {
138 SAL_WARN("avmedia.gstreamer", "detail string too long");
142 OString
detStr(det
, len
);
144 rtl::Reference
<MissingPluginInstallerThread
> join
;
145 rtl::Reference
<MissingPluginInstallerThread
> launch
;
147 osl::MutexGuard
g(mutex_
);
148 if (reported_
.find(detStr
) != reported_
.end()) {
151 auto & i
= queued_
[detStr
];
152 bool fresh
= i
.empty();
154 if (!(fresh
&& launchNewThread_
)) {
157 join
= currentThread_
;
158 currentThread_
= new MissingPluginInstallerThread
;
160 FlagGuard
f(inCleanUp_
);
161 currentSources_
.clear();
164 launchNewThread_
= false;
165 launch
= currentThread_
;
171 Application::PostUserEvent(
172 LINK(this, MissingPluginInstaller
, launchUi
), launch
.get());
176 void eraseSource(std::set
<rtl::Reference
<Player
>> & set
, Player
const * source
)
178 auto i
= std::find_if(
179 set
.begin(), set
.end(),
180 [source
](rtl::Reference
<Player
> const & el
) {
181 return el
.get() == source
;
183 if (i
!= set
.end()) {
189 void MissingPluginInstaller::detach(Player
const * source
) {
190 rtl::Reference
<MissingPluginInstallerThread
> join
;
192 osl::MutexGuard
g(mutex_
);
194 // Guard against ~MissingPluginInstaller with erroneously un-joined
195 // currentThread_ (thus non-empty currentSources_) calling
196 // destructor of currentSources_, calling ~Player, calling here,
197 // which would use currentSources_ while it is already being
201 for (auto i
= queued_
.begin(); i
!= queued_
.end();) {
202 eraseSource(i
->second
, source
);
203 if (i
->second
.empty()) {
204 i
= queued_
.erase(i
);
209 if (currentThread_
.is()) {
210 assert(!currentSources_
.empty());
211 eraseSource(currentSources_
, source
);
212 if (currentSources_
.empty()) {
213 join
= currentThread_
;
214 currentThread_
.clear();
215 launchNewThread_
= true;
220 // missing cancellability of gst_install_plugins_sync
226 void MissingPluginInstaller::processQueue() {
227 assert(!queued_
.empty());
228 assert(currentDetails_
.empty());
229 for (auto i
= queued_
.begin(); i
!= queued_
.end(); ++i
) {
230 reported_
.insert(i
->first
);
231 currentDetails_
.push_back(i
->first
);
232 currentSources_
.insert(i
->second
.begin(), i
->second
.end());
238 IMPL_STATIC_LINK(MissingPluginInstaller
, launchUi
, void *, p
, void)
240 MissingPluginInstallerThread
* thread
= static_cast<MissingPluginInstallerThread
*>(p
);
241 rtl::Reference
<MissingPluginInstallerThread
> ref(thread
, SAL_NO_ACQUIRE
);
243 // not thread safe; hopefully fine to consistently call from our event
244 // loop (which is the only reason to have this
245 // Application::PostUserEvent diversion, in case
246 // MissingPluginInstaller::report might be called from outside our event
247 // loop), and hopefully fine to call gst_is_missing_plugin_message and
248 // gst_missing_plugin_message_get_installer_detail before calling
254 struct TheMissingPluginInstaller
:
255 public rtl::Static
<MissingPluginInstaller
, TheMissingPluginInstaller
>
259 void MissingPluginInstallerThread::execute() {
260 MissingPluginInstaller
& inst
= TheMissingPluginInstaller::get();
262 std::vector
<OString
> details
;
264 osl::MutexGuard
g(inst
.mutex_
);
265 assert(!inst
.currentDetails_
.empty());
266 details
.swap(inst
.currentDetails_
);
268 std::vector
<char *> args
;
269 for (auto const & i
: details
) {
270 args
.push_back(const_cast<char *>(i
.getStr()));
272 args
.push_back(nullptr);
273 gst_install_plugins_sync(args
.data(), nullptr);
275 osl::MutexGuard
g(inst
.mutex_
);
276 if (inst
.queued_
.empty() || inst
.launchNewThread_
) {
277 inst
.launchNewThread_
= true;
285 } // end anonymous namespace
289 GstPlayer_BASE( m_aMutex
),
290 mpPlaybin( nullptr ),
291 mpVolumeControl( nullptr ),
292 #if defined(ENABLE_GTKSINK)
293 mpGtkWidget( nullptr ),
295 mbUseGtkSink( false ),
296 mbFakeVideo (false ),
297 mnUnmutedVolume( 0 ),
298 mbPlayPending ( false ),
301 mbInitialized( false ),
303 mpXOverlay( nullptr ),
310 // Initialize GStreamer library
312 char name
[] = "libreoffice";
313 char *arguments
[] = { name
};
314 char** argv
= arguments
;
315 GError
* pError
= nullptr;
317 mbInitialized
= gst_init_check( &argc
, &argv
, &pError
);
319 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::Player" );
321 if (pError
!= nullptr)
323 // TODO: throw an exception?
324 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::Player error '" << pError
->message
<< "'" );
325 g_error_free (pError
);
332 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::~Player" );
338 void SAL_CALL
Player::disposing()
340 TheMissingPluginInstaller::get().detach(this);
342 ::osl::MutexGuard
aGuard(m_aMutex
);
346 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::disposing" );
348 // Release the elements and pipeline
351 #if defined(ENABLE_GTKSINK)
354 gtk_widget_destroy(mpGtkWidget
);
355 mpGtkWidget
= nullptr;
361 gst_element_set_state( mpPlaybin
, GST_STATE_NULL
);
362 g_object_unref( G_OBJECT( mpPlaybin
) );
365 mpVolumeControl
= nullptr;
369 g_object_unref( G_OBJECT ( mpXOverlay
) );
370 mpXOverlay
= nullptr;
376 g_source_remove(mnWatchID
);
382 static gboolean
pipeline_bus_callback( GstBus
*, GstMessage
*message
, gpointer data
)
384 Player
* pPlayer
= static_cast<Player
*>(data
);
386 pPlayer
->processMessage( message
);
392 static GstBusSyncReply
pipeline_bus_sync_handler( GstBus
*, GstMessage
* message
, gpointer data
)
394 Player
* pPlayer
= static_cast<Player
*>(data
);
396 return pPlayer
->processSyncMessage( message
);
400 void Player::processMessage( GstMessage
*message
)
402 switch( GST_MESSAGE_TYPE( message
) ) {
403 case GST_MESSAGE_EOS
:
404 gst_element_set_state( mpPlaybin
, GST_STATE_READY
);
405 mbPlayPending
= false;
409 case GST_MESSAGE_STATE_CHANGED
:
410 if (message
->src
== GST_OBJECT(mpPlaybin
))
412 GstState newstate
, pendingstate
;
414 gst_message_parse_state_changed (message
, nullptr, &newstate
, &pendingstate
);
416 if (!mbUseGtkSink
&& newstate
== GST_STATE_PAUSED
&&
417 pendingstate
== GST_STATE_VOID_PENDING
&& mpXOverlay
)
419 gst_video_overlay_expose(mpXOverlay
);
423 mbPlayPending
= ((newstate
== GST_STATE_READY
) || (newstate
== GST_STATE_PAUSED
));
432 static gboolean
wrap_element_query_position (GstElement
*element
, GstFormat format
, gint64
*cur
)
434 #ifdef AVMEDIA_GST_0_10
435 GstFormat my_format
= format
;
436 return gst_element_query_position( element
, &my_format
, cur
) && my_format
== format
&& *cur
> 0;
438 return gst_element_query_position( element
, format
, cur
);
443 static gboolean
wrap_element_query_duration (GstElement
*element
, GstFormat format
, gint64
*duration
)
445 #ifdef AVMEDIA_GST_0_10
446 GstFormat my_format
= format
;
447 return gst_element_query_duration( element
, &my_format
, duration
) && my_format
== format
&& *duration
> 0;
449 return gst_element_query_duration( element
, format
, duration
);
454 GstBusSyncReply
Player::processSyncMessage( GstMessage
*message
)
456 #if OSL_DEBUG_LEVEL > 0
457 if ( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ERROR
)
462 gst_message_parse_error( message
, &error
, &error_debug
);
465 "error: '" << error
->message
<< "' debug: '"
466 << error_debug
<< "'");
472 #ifdef AVMEDIA_GST_0_10
473 if (message
->structure
&&
474 !strcmp( gst_structure_get_name( message
->structure
), "prepare-xwindow-id" ) )
476 if (gst_is_video_overlay_prepare_window_handle_message (message
) )
479 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " processSyncMessage prepare window id: " <<
480 GST_MESSAGE_TYPE_NAME( message
) << " " << (int)mnWindowID
);
482 g_object_unref( G_OBJECT ( mpXOverlay
) );
483 g_object_set( GST_MESSAGE_SRC( message
), "force-aspect-ratio", FALSE
, nullptr );
484 mpXOverlay
= GST_VIDEO_OVERLAY( GST_MESSAGE_SRC( message
) );
485 g_object_ref( G_OBJECT ( mpXOverlay
) );
486 if ( mnWindowID
!= 0 )
487 gst_video_overlay_set_window_handle( mpXOverlay
, mnWindowID
);
492 #ifdef AVMEDIA_GST_0_10
493 if( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_STATE_CHANGED
) {
494 if( message
->src
== GST_OBJECT( mpPlaybin
) ) {
495 GstState newstate
, pendingstate
;
497 gst_message_parse_state_changed (message
, nullptr, &newstate
, &pendingstate
);
499 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " state change received, new state " << (int)newstate
<< " pending " << (int)pendingstate
);
500 if( newstate
== GST_STATE_PAUSED
&&
501 pendingstate
== GST_STATE_VOID_PENDING
) {
503 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " change to paused received" );
505 if( mnDuration
== 0) {
506 gint64 gst_duration
= 0;
507 if( wrap_element_query_duration( mpPlaybin
, GST_FORMAT_TIME
, &gst_duration
) )
508 mnDuration
= gst_duration
;
512 GList
*pStreamInfo
= nullptr;
514 g_object_get( G_OBJECT( mpPlaybin
), "stream-info", &pStreamInfo
, nullptr );
516 for ( ; pStreamInfo
!= nullptr; pStreamInfo
= pStreamInfo
->next
) {
517 GObject
*pInfo
= G_OBJECT( pStreamInfo
->data
);
523 g_object_get( pInfo
, "type", &nType
, nullptr );
524 GEnumValue
*pValue
= g_enum_get_value( G_PARAM_SPEC_ENUM( g_object_class_find_property( G_OBJECT_GET_CLASS( pInfo
), "type" ) )->enum_class
,
527 if( !g_ascii_strcasecmp( pValue
->value_nick
, "video" ) ) {
528 GstStructure
*pStructure
;
531 g_object_get( pInfo
, "object", &pPad
, nullptr );
532 pStructure
= gst_caps_get_structure( GST_PAD_CAPS( pPad
), 0 );
534 gst_structure_get_int( pStructure
, "width", &mnWidth
);
535 gst_structure_get_int( pStructure
, "height", &mnHeight
);
536 SAL_INFO( "avmedia.gstreamer", AVVERSION
"queried size: " << mnWidth
<< "x" << mnHeight
);
538 g_object_unref (pPad
);
542 maSizeCondition
.set();
547 // We get to use the exciting new playbin2 ! (now known as playbin)
548 if( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ASYNC_DONE
) {
549 if( mnDuration
== 0) {
550 gint64 gst_duration
= 0;
551 if( wrap_element_query_duration( mpPlaybin
, GST_FORMAT_TIME
, &gst_duration
) )
552 mnDuration
= gst_duration
;
555 GstPad
*pad
= nullptr;
557 g_signal_emit_by_name( mpPlaybin
, "get-video-pad", 0, &pad
);
562 GstCaps
*caps
= gst_pad_get_current_caps( pad
);
564 if( gst_structure_get( gst_caps_get_structure( caps
, 0 ),
565 "width", G_TYPE_INT
, &w
,
566 "height", G_TYPE_INT
, &h
,
571 SAL_INFO( "avmedia.gstreamer", AVVERSION
"queried size: " << mnWidth
<< "x" << mnHeight
);
574 gst_caps_unref( caps
);
575 g_object_unref( pad
);
578 maSizeCondition
.set();
580 #endif // AVMEDIA_GST_0_10
581 } else if (gst_is_missing_plugin_message(message
)) {
582 TheMissingPluginInstaller::get().report(this, message
);
584 // an error occurred, set condition so that OOo thread doesn't wait for us
585 maSizeCondition
.set();
587 } else if( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ERROR
) {
589 // an error occurred, set condition so that OOo thread doesn't wait for us
590 maSizeCondition
.set();
597 void Player::preparePlaybin( const OUString
& rURL
, GstElement
*pSink
)
599 #if defined(ENABLE_GTKSINK)
602 gtk_widget_destroy(mpGtkWidget
);
603 mpGtkWidget
= nullptr;
607 if (mpPlaybin
!= nullptr)
609 gst_element_set_state( mpPlaybin
, GST_STATE_NULL
);
610 mbPlayPending
= false;
611 g_object_unref( mpPlaybin
);
614 mpPlaybin
= gst_element_factory_make( "playbin", nullptr );
616 //tdf#96989 on systems with flat-volumes setting the volume directly on the
617 //playbin to 100% results in setting the global volume to 100% of the
618 //maximum. We expect to set as % of the current volume.
619 mpVolumeControl
= gst_element_factory_make( "volume", nullptr );
620 GstElement
*pAudioSink
= gst_element_factory_make( "autoaudiosink", nullptr );
621 GstElement
* pAudioOutput
= gst_bin_new("audio-output-bin");
622 gst_bin_add_many(GST_BIN(pAudioOutput
), mpVolumeControl
, pAudioSink
, nullptr);
623 gst_element_link(mpVolumeControl
, pAudioSink
);
624 GstPad
*pPad
= gst_element_get_static_pad(mpVolumeControl
, "sink");
625 gst_element_add_pad(GST_ELEMENT(pAudioOutput
), gst_ghost_pad_new("sink", pPad
));
626 gst_object_unref(GST_OBJECT(pPad
));
627 g_object_set(G_OBJECT(mpPlaybin
), "audio-sink", pAudioOutput
, nullptr);
629 if( pSink
!= nullptr ) // used for getting preferred size etc.
631 g_object_set( G_OBJECT( mpPlaybin
), "video-sink", pSink
, nullptr );
637 OString ascURL
= OUStringToOString( rURL
, RTL_TEXTENCODING_UTF8
);
638 g_object_set( G_OBJECT( mpPlaybin
), "uri", ascURL
.getStr() , nullptr );
640 GstBus
*pBus
= gst_element_get_bus( mpPlaybin
);
643 g_source_remove(mnWatchID
);
646 mnWatchID
= gst_bus_add_watch( pBus
, pipeline_bus_callback
, this );
648 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " set sync handler" );
649 #ifdef AVMEDIA_GST_0_10
650 gst_bus_set_sync_handler( pBus
, pipeline_bus_sync_handler
, this );
652 gst_bus_set_sync_handler( pBus
, pipeline_bus_sync_handler
, this, nullptr );
654 g_object_unref( pBus
);
658 bool Player::create( const OUString
& rURL
)
662 // create all the elements and link them
664 SAL_INFO( "avmedia.gstreamer", "create player, URL: '" << rURL
<< "'" );
666 if( mbInitialized
&& !rURL
.isEmpty() )
668 // fakesink for pre-roll & sizing ...
669 preparePlaybin( rURL
, gst_element_factory_make( "fakesink", nullptr ) );
671 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
672 mbPlayPending
= false;
686 void SAL_CALL
Player::start()
688 ::osl::MutexGuard
aGuard(m_aMutex
);
690 // set the pipeline state to READY and run the loop
691 if( mbInitialized
&& nullptr != mpPlaybin
)
693 gst_element_set_state( mpPlaybin
, GST_STATE_PLAYING
);
694 mbPlayPending
= true;
699 void SAL_CALL
Player::stop()
701 ::osl::MutexGuard
aGuard(m_aMutex
);
703 // set the pipeline in PAUSED STATE
705 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
707 mbPlayPending
= false;
708 SAL_INFO( "avmedia.gstreamer", AVVERSION
"stop " << mpPlaybin
);
712 sal_Bool SAL_CALL
Player::isPlaying()
714 ::osl::MutexGuard
aGuard(m_aMutex
);
716 bool bRet
= mbPlayPending
;
718 // return whether the pipeline is in PLAYING STATE or not
719 if( !mbPlayPending
&& mbInitialized
&& mpPlaybin
)
721 bRet
= GST_STATE( mpPlaybin
) == GST_STATE_PLAYING
;
724 SAL_INFO( "avmedia.gstreamer", AVVERSION
"isPlaying " << bRet
);
730 double SAL_CALL
Player::getDuration()
732 ::osl::MutexGuard
aGuard(m_aMutex
);
734 // slideshow checks for non-zero duration, so cheat here
735 double duration
= 0.01;
737 if( mpPlaybin
&& mnDuration
> 0 ) {
738 duration
= mnDuration
/ GST_SECOND
;
745 void SAL_CALL
Player::setMediaTime( double fTime
)
747 ::osl::MutexGuard
aGuard(m_aMutex
);
750 gint64 gst_position
= llround (fTime
* GST_SECOND
);
752 gst_element_seek( mpPlaybin
, 1.0,
755 GST_SEEK_TYPE_SET
, gst_position
,
756 GST_SEEK_TYPE_NONE
, 0 );
758 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
760 SAL_INFO( "avmedia.gstreamer", AVVERSION
"seek to: " << gst_position
<< " ns original: " << fTime
<< " s" );
765 double SAL_CALL
Player::getMediaTime()
767 ::osl::MutexGuard
aGuard(m_aMutex
);
769 double position
= 0.0;
772 // get current position in the stream
774 if( wrap_element_query_position( mpPlaybin
, GST_FORMAT_TIME
, &gst_position
) )
775 position
= gst_position
/ GST_SECOND
;
782 void SAL_CALL
Player::setPlaybackLoop( sal_Bool bSet
)
784 ::osl::MutexGuard
aGuard(m_aMutex
);
785 // TODO check how to do with GST
790 sal_Bool SAL_CALL
Player::isPlaybackLoop()
792 ::osl::MutexGuard
aGuard(m_aMutex
);
793 // TODO check how to do with GST
798 void SAL_CALL
Player::setMute( sal_Bool bSet
)
800 ::osl::MutexGuard
aGuard(m_aMutex
);
802 SAL_INFO( "avmedia.gstreamer", AVVERSION
"set mute: " << bSet
<< " muted: " << mbMuted
<< " unmuted volume: " << mnUnmutedVolume
);
804 // change the volume to 0 or the unmuted volume
805 if( mpPlaybin
&& mbMuted
!= bool(bSet
) )
807 double nVolume
= mnUnmutedVolume
;
813 g_object_set( G_OBJECT( mpVolumeControl
), "volume", nVolume
, nullptr );
820 sal_Bool SAL_CALL
Player::isMute()
822 ::osl::MutexGuard
aGuard(m_aMutex
);
828 void SAL_CALL
Player::setVolumeDB( sal_Int16 nVolumeDB
)
830 ::osl::MutexGuard
aGuard(m_aMutex
);
832 mnUnmutedVolume
= pow( 10.0, nVolumeDB
/ 20.0 );
834 SAL_INFO( "avmedia.gstreamer", AVVERSION
"set volume: " << nVolumeDB
<< " gst volume: " << mnUnmutedVolume
);
837 if( !mbMuted
&& mpPlaybin
)
839 g_object_set( G_OBJECT( mpVolumeControl
), "volume", mnUnmutedVolume
, nullptr );
844 sal_Int16 SAL_CALL
Player::getVolumeDB()
846 ::osl::MutexGuard
aGuard(m_aMutex
);
848 sal_Int16
nVolumeDB(0);
851 double nGstVolume
= 0.0;
853 g_object_get( G_OBJECT( mpVolumeControl
), "volume", &nGstVolume
, nullptr );
855 nVolumeDB
= (sal_Int16
) ( 20.0*log10 ( nGstVolume
) );
862 awt::Size SAL_CALL
Player::getPreferredPlayerWindowSize()
864 ::osl::MutexGuard
aGuard(m_aMutex
);
866 awt::Size
aSize( 0, 0 );
868 if( maURL
.isEmpty() )
870 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::getPreferredPlayerWindowSize - empty URL => 0x0" );
874 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " pre-Player::getPreferredPlayerWindowSize, member " << mnWidth
<< "x" << mnHeight
);
876 osl::Condition::Result aResult
= maSizeCondition
.wait( std::chrono::seconds(10) );
878 SAL_INFO( "avmedia.gstreamer", AVVERSION
<< this << " Player::getPreferredPlayerWindowSize after waitCondition " << aResult
<< ", member " << mnWidth
<< "x" << mnHeight
);
880 if( mnWidth
!= 0 && mnHeight
!= 0 ) {
881 aSize
.Width
= mnWidth
;
882 aSize
.Height
= mnHeight
;
889 uno::Reference
< ::media::XPlayerWindow
> SAL_CALL
Player::createPlayerWindow( const uno::Sequence
< uno::Any
>& rArguments
)
891 ::osl::MutexGuard
aGuard(m_aMutex
);
893 uno::Reference
< ::media::XPlayerWindow
> xRet
;
894 awt::Size
aSize( getPreferredPlayerWindowSize() );
897 preparePlaybin( maURL
, nullptr );
899 SAL_INFO( "avmedia.gstreamer", AVVERSION
"Player::createPlayerWindow " << aSize
.Width
<< "x" << aSize
.Height
<< " length: " << rArguments
.getLength() );
901 if( aSize
.Width
> 0 && aSize
.Height
> 0 )
903 ::avmedia::gstreamer::Window
* pWindow
= new ::avmedia::gstreamer::Window
;
907 if( rArguments
.getLength() > 2 )
909 sal_IntPtr pIntPtr
= 0;
910 rArguments
[ 2 ] >>= pIntPtr
;
911 SystemChildWindow
*pParentWindow
= reinterpret_cast< SystemChildWindow
* >( pIntPtr
);
912 const SystemEnvData
* pEnvData
= pParentWindow
? pParentWindow
->GetSystemData() : nullptr;
913 OSL_ASSERT(pEnvData
);
916 #if defined(ENABLE_GTKSINK)
917 GstElement
*pVideosink
= g_strcmp0(pEnvData
->pToolkit
, "gtk3") == 0 ?
918 gst_element_factory_make("gtksink", "gtksink") : nullptr;
922 g_object_get(pVideosink
, "widget", &mpGtkWidget
, nullptr);
923 gtk_widget_set_vexpand(mpGtkWidget
, true);
924 gtk_widget_set_hexpand(mpGtkWidget
, true);
925 GtkWidget
*pParent
= static_cast<GtkWidget
*>(pEnvData
->pWidget
);
926 gtk_container_add (GTK_CONTAINER(pParent
), mpGtkWidget
);
928 g_object_set( G_OBJECT( mpPlaybin
), "video-sink", pVideosink
, nullptr);
929 g_object_set( G_OBJECT( mpPlaybin
), "force-aspect-ratio", FALSE
, nullptr);
931 gtk_widget_show_all (pParent
);
936 mbUseGtkSink
= false;
937 mnWindowID
= pEnvData
->aWindow
;
938 SAL_INFO( "avmedia.gstreamer", AVVERSION
"set window id to " << (int)mnWindowID
<< " XOverlay " << mpXOverlay
);
939 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
940 if ( mpXOverlay
!= nullptr )
941 gst_video_overlay_set_window_handle( mpXOverlay
, mnWindowID
);
952 uno::Reference
< media::XFrameGrabber
> SAL_CALL
Player::createFrameGrabber()
954 ::osl::MutexGuard
aGuard(m_aMutex
);
955 FrameGrabber
* pFrameGrabber
= nullptr;
956 const awt::Size
aPrefSize( getPreferredPlayerWindowSize() );
958 if( ( aPrefSize
.Width
> 0 ) && ( aPrefSize
.Height
> 0 ) )
959 pFrameGrabber
= FrameGrabber::create( maURL
);
960 SAL_INFO( "avmedia.gstreamer", AVVERSION
"created FrameGrabber " << pFrameGrabber
);
962 return pFrameGrabber
;
966 OUString SAL_CALL
Player::getImplementationName()
968 return OUString( AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME
);
972 sal_Bool SAL_CALL
Player::supportsService( const OUString
& ServiceName
)
974 return cppu::supportsService(this, ServiceName
);
978 uno::Sequence
< OUString
> SAL_CALL
Player::getSupportedServiceNames()
980 return { AVMEDIA_GST_PLAYER_SERVICENAME
};
983 } // namespace gstreamer
984 } // namespace avmedia
986 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */