Version 5.4.3.2, tag libreoffice-5.4.3.2
[LibreOffice.git] / avmedia / source / gstreamer / gstplayer.cxx
blob144a1961715e1ef91f633dec4b3ee6a54d0bb515
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include <algorithm>
23 #include <cassert>
24 #include <chrono>
25 #include <cstddef>
26 #include <cstring>
27 #include <map>
28 #include <set>
29 #include <vector>
30 #include <math.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"
47 #else
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"
51 #endif
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: "
58 #else
59 # define AVVERSION "gst 1.0: "
60 #endif
62 using namespace ::com::sun::star;
64 namespace avmedia { namespace gstreamer {
66 namespace {
68 class FlagGuard {
69 public:
70 explicit FlagGuard(bool & flag): flag_(flag) { flag_ = true; }
72 ~FlagGuard() { flag_ = false; }
74 private:
75 bool & flag_;
78 class MissingPluginInstallerThread: public salhelper::Thread {
79 public:
80 MissingPluginInstallerThread(): Thread("MissingPluginInstaller") {}
82 private:
83 void execute() override;
87 class MissingPluginInstaller {
88 friend class MissingPluginInstallerThread;
90 public:
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);
102 private:
103 void processQueue();
105 DECL_STATIC_LINK(MissingPluginInstaller, launchUi, void*, void);
107 osl::Mutex mutex_;
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_;
114 bool inCleanUp_;
118 MissingPluginInstaller::~MissingPluginInstaller() {
119 osl::MutexGuard g(mutex_);
120 SAL_WARN_IF(currentThread_.is(), "avmedia.gstreamer", "unjoined thread");
121 inCleanUp_ = true;
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) {
131 SAL_WARN(
132 "avmedia.gstreamer",
133 "gst_missing_plugin_message_get_installer_detail failed");
134 return;
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");
139 g_free(det);
140 return;
142 OString detStr(det, len);
143 g_free(det);
144 rtl::Reference<MissingPluginInstallerThread> join;
145 rtl::Reference<MissingPluginInstallerThread> launch;
147 osl::MutexGuard g(mutex_);
148 if (reported_.find(detStr) != reported_.end()) {
149 return;
151 auto & i = queued_[detStr];
152 bool fresh = i.empty();
153 i.insert(source);
154 if (!(fresh && launchNewThread_)) {
155 return;
157 join = currentThread_;
158 currentThread_ = new MissingPluginInstallerThread;
160 FlagGuard f(inCleanUp_);
161 currentSources_.clear();
163 processQueue();
164 launchNewThread_ = false;
165 launch = currentThread_;
167 if (join.is()) {
168 join->join();
170 launch->acquire();
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()) {
184 set.erase(i);
189 void MissingPluginInstaller::detach(Player const * source) {
190 rtl::Reference<MissingPluginInstallerThread> join;
192 osl::MutexGuard g(mutex_);
193 if (inCleanUp_) {
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
198 // destroyed:
199 return;
201 for (auto i = queued_.begin(); i != queued_.end();) {
202 eraseSource(i->second, source);
203 if (i->second.empty()) {
204 i = queued_.erase(i);
205 } else {
206 ++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;
219 if (join.is()) {
220 // missing cancellability of gst_install_plugins_sync
221 join->join();
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());
234 queued_.clear();
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);
242 gst_pb_utils_init();
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
249 // gst_pb_utils_init
250 ref->launch();
254 struct TheMissingPluginInstaller:
255 public rtl::Static<MissingPluginInstaller, TheMissingPluginInstaller>
259 void MissingPluginInstallerThread::execute() {
260 MissingPluginInstaller & inst = TheMissingPluginInstaller::get();
261 for (;;) {
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;
278 break;
280 inst.processQueue();
285 } // end anonymous namespace
288 Player::Player() :
289 GstPlayer_BASE( m_aMutex ),
290 mpPlaybin( nullptr ),
291 mpVolumeControl( nullptr ),
292 #if defined(ENABLE_GTKSINK)
293 mpGtkWidget( nullptr ),
294 #endif
295 mbUseGtkSink( false ),
296 mbFakeVideo (false ),
297 mnUnmutedVolume( 0 ),
298 mbPlayPending ( false ),
299 mbMuted( false ),
300 mbLooping( false ),
301 mbInitialized( false ),
302 mnWindowID( 0 ),
303 mpXOverlay( nullptr ),
304 mnDuration( 0 ),
305 mnWidth( 0 ),
306 mnHeight( 0 ),
307 mnWatchID( 0 ),
308 mbWatchID( false )
310 // Initialize GStreamer library
311 int argc = 1;
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);
330 Player::~Player()
332 SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::~Player" );
333 if( mbInitialized )
334 disposing();
338 void SAL_CALL Player::disposing()
340 TheMissingPluginInstaller::get().detach(this);
342 ::osl::MutexGuard aGuard(m_aMutex);
344 stop();
346 SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::disposing" );
348 // Release the elements and pipeline
349 if( mbInitialized )
351 #if defined(ENABLE_GTKSINK)
352 if (mpGtkWidget)
354 gtk_widget_destroy(mpGtkWidget);
355 mpGtkWidget = nullptr;
357 #endif
359 if( mpPlaybin )
361 gst_element_set_state( mpPlaybin, GST_STATE_NULL );
362 g_object_unref( G_OBJECT( mpPlaybin ) );
364 mpPlaybin = nullptr;
365 mpVolumeControl = nullptr;
368 if( mpXOverlay ) {
369 g_object_unref( G_OBJECT ( mpXOverlay ) );
370 mpXOverlay = nullptr;
374 if (mbWatchID)
376 g_source_remove(mnWatchID);
377 mbWatchID = false;
382 static gboolean pipeline_bus_callback( GstBus *, GstMessage *message, gpointer data )
384 Player* pPlayer = static_cast<Player*>(data);
386 pPlayer->processMessage( message );
388 return TRUE;
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;
406 if (mbLooping)
407 start();
408 break;
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);
422 if (mbPlayPending)
423 mbPlayPending = ((newstate == GST_STATE_READY) || (newstate == GST_STATE_PAUSED));
425 break;
426 default:
427 break;
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;
437 #else
438 return gst_element_query_position( element, format, cur );
439 #endif
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;
448 #else
449 return gst_element_query_duration( element, format, duration );
450 #endif
454 GstBusSyncReply Player::processSyncMessage( GstMessage *message )
456 #if OSL_DEBUG_LEVEL > 0
457 if ( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR )
459 GError* error;
460 gchar* error_debug;
462 gst_message_parse_error( message, &error, &error_debug );
463 SAL_WARN(
464 "avmedia.gstreamer",
465 "error: '" << error->message << "' debug: '"
466 << error_debug << "'");
468 #endif
470 if (!mbUseGtkSink)
472 #ifdef AVMEDIA_GST_0_10
473 if (message->structure &&
474 !strcmp( gst_structure_get_name( message->structure ), "prepare-xwindow-id" ) )
475 #else
476 if (gst_is_video_overlay_prepare_window_handle_message (message) )
477 #endif
479 SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " processSyncMessage prepare window id: " <<
480 GST_MESSAGE_TYPE_NAME( message ) << " " << (int)mnWindowID );
481 if( mpXOverlay )
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 );
488 return GST_BUS_DROP;
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;
511 if( mnWidth == 0 ) {
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 );
519 if( !pInfo )
520 continue;
522 int nType;
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,
525 nType );
527 if( !g_ascii_strcasecmp( pValue->value_nick, "video" ) ) {
528 GstStructure *pStructure;
529 GstPad *pPad;
531 g_object_get( pInfo, "object", &pPad, nullptr );
532 pStructure = gst_caps_get_structure( GST_PAD_CAPS( pPad ), 0 );
533 if( pStructure ) {
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();
546 #else
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;
554 if( mnWidth == 0 ) {
555 GstPad *pad = nullptr;
557 g_signal_emit_by_name( mpPlaybin, "get-video-pad", 0, &pad );
559 if( pad ) {
560 int w = 0, h = 0;
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,
567 nullptr ) ) {
568 mnWidth = w;
569 mnHeight = 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);
583 if( mnWidth == 0 ) {
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 ) {
588 if( mnWidth == 0 ) {
589 // an error occurred, set condition so that OOo thread doesn't wait for us
590 maSizeCondition.set();
594 return GST_BUS_PASS;
597 void Player::preparePlaybin( const OUString& rURL, GstElement *pSink )
599 #if defined(ENABLE_GTKSINK)
600 if (mpGtkWidget)
602 gtk_widget_destroy(mpGtkWidget);
603 mpGtkWidget = nullptr;
605 #endif
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 );
632 mbFakeVideo = true;
634 else
635 mbFakeVideo = false;
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 );
641 if (mbWatchID)
643 g_source_remove(mnWatchID);
644 mbWatchID = false;
646 mnWatchID = gst_bus_add_watch( pBus, pipeline_bus_callback, this );
647 mbWatchID = true;
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 );
651 #else
652 gst_bus_set_sync_handler( pBus, pipeline_bus_sync_handler, this, nullptr );
653 #endif
654 g_object_unref( pBus );
658 bool Player::create( const OUString& rURL )
660 bool bRet = false;
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;
674 bRet = true;
677 if( bRet )
678 maURL = rURL;
679 else
680 maURL.clear();
682 return bRet;
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
704 if( mpPlaybin )
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 );
726 return 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;
741 return duration;
745 void SAL_CALL Player::setMediaTime( double fTime )
747 ::osl::MutexGuard aGuard(m_aMutex);
749 if( mpPlaybin ) {
750 gint64 gst_position = llround (fTime * GST_SECOND);
752 gst_element_seek( mpPlaybin, 1.0,
753 GST_FORMAT_TIME,
754 GST_SEEK_FLAG_FLUSH,
755 GST_SEEK_TYPE_SET, gst_position,
756 GST_SEEK_TYPE_NONE, 0 );
757 if( !isPlaying() )
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;
771 if( mpPlaybin ) {
772 // get current position in the stream
773 gint64 gst_position;
774 if( wrap_element_query_position( mpPlaybin, GST_FORMAT_TIME, &gst_position ) )
775 position = gst_position / GST_SECOND;
778 return position;
782 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
784 ::osl::MutexGuard aGuard(m_aMutex);
785 // TODO check how to do with GST
786 mbLooping = bSet;
790 sal_Bool SAL_CALL Player::isPlaybackLoop()
792 ::osl::MutexGuard aGuard(m_aMutex);
793 // TODO check how to do with GST
794 return mbLooping;
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;
808 if( bSet )
810 nVolume = 0.0;
813 g_object_set( G_OBJECT( mpVolumeControl ), "volume", nVolume, nullptr );
815 mbMuted = bSet;
820 sal_Bool SAL_CALL Player::isMute()
822 ::osl::MutexGuard aGuard(m_aMutex);
824 return mbMuted;
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 );
836 // change volume
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);
850 if( mpPlaybin ) {
851 double nGstVolume = 0.0;
853 g_object_get( G_OBJECT( mpVolumeControl ), "volume", &nGstVolume, nullptr );
855 nVolumeDB = (sal_Int16) ( 20.0*log10 ( nGstVolume ) );
858 return nVolumeDB;
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" );
871 return aSize;
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;
885 return aSize;
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() );
896 if( mbFakeVideo )
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;
905 xRet = pWindow;
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);
914 if (pEnvData)
916 #if defined(ENABLE_GTKSINK)
917 GstElement *pVideosink = g_strcmp0(pEnvData->pToolkit, "gtk3") == 0 ?
918 gst_element_factory_make("gtksink", "gtksink") : nullptr;
919 if (pVideosink)
921 mbUseGtkSink = true;
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);
933 else
934 #endif
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 );
948 return xRet;
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: */