nss: upgrade to release 3.73
[LibreOffice.git] / avmedia / source / gstreamer / gstplayer.cxx
blob9bc8920fd2d650385fcfd5b51a65916252e97e6f
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>
33 #include <sal/log.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 #include <gst/video/videooverlay.h>
45 #define AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_GStreamer"
46 #define AVMEDIA_GST_PLAYER_SERVICENAME "com.sun.star.media.Player_GStreamer"
48 #include <gst/pbutils/missing-plugins.h>
49 #include <gst/pbutils/pbutils.h>
51 #define AVVERSION "gst 1.0: "
53 using namespace ::com::sun::star;
55 namespace avmedia::gstreamer {
57 namespace {
59 class FlagGuard {
60 public:
61 explicit FlagGuard(bool & flag): flag_(flag) { flag_ = true; }
63 ~FlagGuard() { flag_ = false; }
65 private:
66 bool & flag_;
69 class MissingPluginInstallerThread: public salhelper::Thread {
70 public:
71 MissingPluginInstallerThread(): Thread("MissingPluginInstaller") {}
73 private:
74 void execute() override;
78 class MissingPluginInstaller {
79 friend class MissingPluginInstallerThread;
81 public:
82 MissingPluginInstaller(): launchNewThread_(true), inCleanUp_(false) {}
84 ~MissingPluginInstaller();
86 void report(rtl::Reference<Player> const & source, GstMessage * message);
88 // Player::~Player calls Player::disposing calls
89 // MissingPluginInstaller::detach, so do not take Player by rtl::Reference
90 // here (which would bump its refcount back from 0 to 1):
91 void detach(Player const * source);
93 private:
94 void processQueue();
96 DECL_STATIC_LINK(MissingPluginInstaller, launchUi, void*, void);
98 osl::Mutex mutex_;
99 std::set<OString> reported_;
100 std::map<OString, std::set<rtl::Reference<Player>>> queued_;
101 rtl::Reference<MissingPluginInstallerThread> currentThread_;
102 std::vector<OString> currentDetails_;
103 std::set<rtl::Reference<Player>> currentSources_;
104 bool launchNewThread_;
105 bool inCleanUp_;
109 MissingPluginInstaller::~MissingPluginInstaller() {
110 osl::MutexGuard g(mutex_);
111 SAL_WARN_IF(currentThread_.is(), "avmedia.gstreamer", "unjoined thread");
112 inCleanUp_ = true;
116 void MissingPluginInstaller::report(
117 rtl::Reference<Player> const & source, GstMessage * message)
119 // assert(gst_is_missing_plugin_message(message));
120 gchar * det = gst_missing_plugin_message_get_installer_detail(message);
121 if (det == nullptr) {
122 SAL_WARN(
123 "avmedia.gstreamer",
124 "gst_missing_plugin_message_get_installer_detail failed");
125 return;
127 std::size_t len = std::strlen(det);
128 if (len > SAL_MAX_INT32) {
129 SAL_WARN("avmedia.gstreamer", "detail string too long");
130 g_free(det);
131 return;
133 OString detStr(det, len);
134 g_free(det);
135 rtl::Reference<MissingPluginInstallerThread> join;
136 rtl::Reference<MissingPluginInstallerThread> launch;
138 osl::MutexGuard g(mutex_);
139 if (reported_.find(detStr) != reported_.end()) {
140 return;
142 auto & i = queued_[detStr];
143 bool fresh = i.empty();
144 i.insert(source);
145 if (!(fresh && launchNewThread_)) {
146 return;
148 join = currentThread_;
149 currentThread_ = new MissingPluginInstallerThread;
151 FlagGuard f(inCleanUp_);
152 currentSources_.clear();
154 processQueue();
155 launchNewThread_ = false;
156 launch = currentThread_;
158 if (join.is()) {
159 join->join();
161 launch->acquire();
162 Application::PostUserEvent(
163 LINK(this, MissingPluginInstaller, launchUi), launch.get());
167 void eraseSource(std::set<rtl::Reference<Player>> & set, Player const * source)
169 auto i = std::find_if(
170 set.begin(), set.end(),
171 [source](rtl::Reference<Player> const & el) {
172 return el.get() == source;
174 if (i != set.end()) {
175 set.erase(i);
180 void MissingPluginInstaller::detach(Player const * source) {
181 rtl::Reference<MissingPluginInstallerThread> join;
183 osl::MutexGuard g(mutex_);
184 if (inCleanUp_) {
185 // Guard against ~MissingPluginInstaller with erroneously un-joined
186 // currentThread_ (thus non-empty currentSources_) calling
187 // destructor of currentSources_, calling ~Player, calling here,
188 // which would use currentSources_ while it is already being
189 // destroyed:
190 return;
192 for (auto i = queued_.begin(); i != queued_.end();) {
193 eraseSource(i->second, source);
194 if (i->second.empty()) {
195 i = queued_.erase(i);
196 } else {
197 ++i;
200 if (currentThread_.is()) {
201 assert(!currentSources_.empty());
202 eraseSource(currentSources_, source);
203 if (currentSources_.empty()) {
204 join = currentThread_;
205 currentThread_.clear();
206 launchNewThread_ = true;
210 if (join.is()) {
211 // missing cancellability of gst_install_plugins_sync
212 join->join();
217 void MissingPluginInstaller::processQueue() {
218 assert(!queued_.empty());
219 assert(currentDetails_.empty());
220 for (const auto& rEntry : queued_) {
221 reported_.insert(rEntry.first);
222 currentDetails_.push_back(rEntry.first);
223 currentSources_.insert(rEntry.second.begin(), rEntry.second.end());
225 queued_.clear();
229 IMPL_STATIC_LINK(MissingPluginInstaller, launchUi, void *, p, void)
231 MissingPluginInstallerThread* thread = static_cast<MissingPluginInstallerThread*>(p);
232 rtl::Reference<MissingPluginInstallerThread> ref(thread, SAL_NO_ACQUIRE);
233 gst_pb_utils_init();
234 // not thread safe; hopefully fine to consistently call from our event
235 // loop (which is the only reason to have this
236 // Application::PostUserEvent diversion, in case
237 // MissingPluginInstaller::report might be called from outside our event
238 // loop), and hopefully fine to call gst_is_missing_plugin_message and
239 // gst_missing_plugin_message_get_installer_detail before calling
240 // gst_pb_utils_init
241 ref->launch();
245 struct TheMissingPluginInstaller:
246 public rtl::Static<MissingPluginInstaller, TheMissingPluginInstaller>
250 void MissingPluginInstallerThread::execute() {
251 MissingPluginInstaller & inst = TheMissingPluginInstaller::get();
252 for (;;) {
253 std::vector<OString> details;
255 osl::MutexGuard g(inst.mutex_);
256 assert(!inst.currentDetails_.empty());
257 details.swap(inst.currentDetails_);
259 std::vector<char *> args;
260 args.reserve(details.size());
261 for (auto const& i : details)
263 args.push_back(const_cast<char *>(i.getStr()));
265 args.push_back(nullptr);
266 gst_install_plugins_sync(args.data(), nullptr);
268 osl::MutexGuard g(inst.mutex_);
269 if (inst.queued_.empty() || inst.launchNewThread_) {
270 inst.launchNewThread_ = true;
271 break;
273 inst.processQueue();
278 } // end anonymous namespace
281 Player::Player() :
282 GstPlayer_BASE( m_aMutex ),
283 mpPlaybin( nullptr ),
284 mpVolumeControl( nullptr ),
285 mbUseGtkSink( false ),
286 mbFakeVideo (false ),
287 mnUnmutedVolume( 0 ),
288 mbPlayPending ( false ),
289 mbMuted( false ),
290 mbLooping( false ),
291 mbInitialized( false ),
292 mpDisplay( nullptr ),
293 mnWindowID( 0 ),
294 mpXOverlay( nullptr ),
295 mnDuration( 0 ),
296 mnWidth( 0 ),
297 mnHeight( 0 ),
298 mnWatchID( 0 ),
299 mbWatchID( false )
301 // Initialize GStreamer library
302 int argc = 1;
303 char name[] = "libreoffice";
304 char *arguments[] = { name };
305 char** argv = arguments;
306 GError* pError = nullptr;
308 mbInitialized = gst_init_check( &argc, &argv, &pError );
310 SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::Player" );
312 if (pError != nullptr)
314 // TODO: throw an exception?
315 SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::Player error '" << pError->message << "'" );
316 g_error_free (pError);
321 Player::~Player()
323 SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::~Player" );
324 if( mbInitialized )
325 disposing();
329 void SAL_CALL Player::disposing()
331 TheMissingPluginInstaller::get().detach(this);
333 ::osl::MutexGuard aGuard(m_aMutex);
335 stop();
337 SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::disposing" );
339 // Release the elements and pipeline
340 if( mbInitialized )
342 if( mpPlaybin )
344 gst_element_set_state( mpPlaybin, GST_STATE_NULL );
345 g_object_unref( G_OBJECT( mpPlaybin ) );
347 mpPlaybin = nullptr;
348 mpVolumeControl = nullptr;
351 if( mpXOverlay ) {
352 g_object_unref( G_OBJECT ( mpXOverlay ) );
353 mpXOverlay = nullptr;
357 if (mbWatchID)
359 g_source_remove(mnWatchID);
360 mbWatchID = false;
365 static gboolean pipeline_bus_callback( GstBus *, GstMessage *message, gpointer data )
367 Player* pPlayer = static_cast<Player*>(data);
369 pPlayer->processMessage( message );
371 return true;
375 static GstBusSyncReply pipeline_bus_sync_handler( GstBus *, GstMessage * message, gpointer data )
377 Player* pPlayer = static_cast<Player*>(data);
379 return pPlayer->processSyncMessage( message );
383 void Player::processMessage( GstMessage *message )
385 switch( GST_MESSAGE_TYPE( message ) ) {
386 case GST_MESSAGE_EOS:
387 gst_element_set_state( mpPlaybin, GST_STATE_READY );
388 mbPlayPending = false;
389 if (mbLooping)
390 start();
391 break;
392 case GST_MESSAGE_STATE_CHANGED:
393 if (message->src == GST_OBJECT(mpPlaybin))
395 GstState newstate, pendingstate;
397 gst_message_parse_state_changed (message, nullptr, &newstate, &pendingstate);
399 if (!mbUseGtkSink && newstate == GST_STATE_PAUSED &&
400 pendingstate == GST_STATE_VOID_PENDING && mpXOverlay)
402 gst_video_overlay_expose(mpXOverlay);
405 if (mbPlayPending)
406 mbPlayPending = ((newstate == GST_STATE_READY) || (newstate == GST_STATE_PAUSED));
408 break;
409 default:
410 break;
414 #define LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE "GstWaylandDisplayHandleContextType"
416 static bool lcl_is_wayland_display_handle_need_context_message(GstMessage* msg)
418 g_return_val_if_fail(GST_IS_MESSAGE(msg), false);
420 if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_NEED_CONTEXT)
421 return false;
422 const gchar *type = nullptr;
423 if (!gst_message_parse_context_type(msg, &type))
424 return false;
425 return !g_strcmp0(type, LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE);
428 static GstContext* lcl_wayland_display_handle_context_new(void* display)
430 GstContext *context = gst_context_new(LCL_WAYLAND_DISPLAY_HANDLE_CONTEXT_TYPE, true);
431 gst_structure_set (gst_context_writable_structure (context),
432 "handle", G_TYPE_POINTER, display, nullptr);
433 return context;
436 GstBusSyncReply Player::processSyncMessage( GstMessage *message )
438 #if OSL_DEBUG_LEVEL > 0
439 if ( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR )
441 GError* error;
442 gchar* error_debug;
444 gst_message_parse_error( message, &error, &error_debug );
445 SAL_WARN(
446 "avmedia.gstreamer",
447 "error: '" << error->message << "' debug: '"
448 << error_debug << "'");
450 #endif
452 if (!mbUseGtkSink)
454 if (gst_is_video_overlay_prepare_window_handle_message (message) )
456 SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " processSyncMessage prepare window id: " <<
457 GST_MESSAGE_TYPE_NAME( message ) << " " << static_cast<int>(mnWindowID) );
458 if( mpXOverlay )
459 g_object_unref( G_OBJECT ( mpXOverlay ) );
460 g_object_set( GST_MESSAGE_SRC( message ), "force-aspect-ratio", FALSE, nullptr );
461 mpXOverlay = GST_VIDEO_OVERLAY( GST_MESSAGE_SRC( message ) );
462 g_object_ref( G_OBJECT ( mpXOverlay ) );
463 if ( mnWindowID != 0 )
465 gst_video_overlay_set_window_handle( mpXOverlay, mnWindowID );
466 gst_video_overlay_handle_events(mpXOverlay, 0); // Let the parent window handle events.
467 if (maArea.Width > 0 && maArea.Height > 0)
468 gst_video_overlay_set_render_rectangle(mpXOverlay, maArea.X, maArea.Y, maArea.Width, maArea.Height);
471 return GST_BUS_DROP;
473 else if (lcl_is_wayland_display_handle_need_context_message(message))
475 GstContext *context = lcl_wayland_display_handle_context_new(mpDisplay);
476 gst_element_set_context(GST_ELEMENT(GST_MESSAGE_SRC(message)), context);
478 return GST_BUS_DROP;
482 if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ASYNC_DONE ) {
483 if( mnDuration == 0) {
484 gint64 gst_duration = 0;
485 if( gst_element_query_duration( mpPlaybin, GST_FORMAT_TIME, &gst_duration) )
486 mnDuration = gst_duration;
488 if( mnWidth == 0 ) {
489 GstPad *pad = nullptr;
491 g_signal_emit_by_name( mpPlaybin, "get-video-pad", 0, &pad );
493 if( pad ) {
494 int w = 0, h = 0;
496 GstCaps *caps = gst_pad_get_current_caps( pad );
498 if( gst_structure_get( gst_caps_get_structure( caps, 0 ),
499 "width", G_TYPE_INT, &w,
500 "height", G_TYPE_INT, &h,
501 nullptr ) ) {
502 mnWidth = w;
503 mnHeight = h;
505 SAL_INFO( "avmedia.gstreamer", AVVERSION "queried size: " << mnWidth << "x" << mnHeight );
508 gst_caps_unref( caps );
509 g_object_unref( pad );
512 maSizeCondition.set();
514 } else if (gst_is_missing_plugin_message(message)) {
515 TheMissingPluginInstaller::get().report(this, message);
516 if( mnWidth == 0 ) {
517 // an error occurred, set condition so that OOo thread doesn't wait for us
518 maSizeCondition.set();
520 } else if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR ) {
521 if( mnWidth == 0 ) {
522 // an error occurred, set condition so that OOo thread doesn't wait for us
523 maSizeCondition.set();
527 return GST_BUS_PASS;
530 void Player::preparePlaybin( const OUString& rURL, GstElement *pSink )
532 if (mpPlaybin != nullptr)
534 gst_element_set_state( mpPlaybin, GST_STATE_NULL );
535 mbPlayPending = false;
536 g_object_unref( mpPlaybin );
539 mpPlaybin = gst_element_factory_make( "playbin", nullptr );
541 //tdf#96989 on systems with flat-volumes setting the volume directly on the
542 //playbin to 100% results in setting the global volume to 100% of the
543 //maximum. We expect to set as % of the current volume.
544 mpVolumeControl = gst_element_factory_make( "volume", nullptr );
545 GstElement *pAudioSink = gst_element_factory_make( "autoaudiosink", nullptr );
546 GstElement* pAudioOutput = gst_bin_new("audio-output-bin");
547 assert(pAudioOutput);
548 if (pAudioSink)
549 gst_bin_add(GST_BIN(pAudioOutput), pAudioSink);
550 if (mpVolumeControl)
552 gst_bin_add(GST_BIN(pAudioOutput), mpVolumeControl);
553 if (pAudioSink)
554 gst_element_link(mpVolumeControl, pAudioSink);
555 GstPad *pPad = gst_element_get_static_pad(mpVolumeControl, "sink");
556 gst_element_add_pad(GST_ELEMENT(pAudioOutput), gst_ghost_pad_new("sink", pPad));
557 gst_object_unref(GST_OBJECT(pPad));
559 g_object_set(G_OBJECT(mpPlaybin), "audio-sink", pAudioOutput, nullptr);
561 if( pSink != nullptr ) // used for getting preferred size etc.
563 g_object_set( G_OBJECT( mpPlaybin ), "video-sink", pSink, nullptr );
564 mbFakeVideo = true;
566 else
567 mbFakeVideo = false;
569 OString ascURL = OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 );
570 g_object_set( G_OBJECT( mpPlaybin ), "uri", ascURL.getStr() , nullptr );
572 GstBus *pBus = gst_element_get_bus( mpPlaybin );
573 if (mbWatchID)
575 g_source_remove(mnWatchID);
576 mbWatchID = false;
578 mnWatchID = gst_bus_add_watch( pBus, pipeline_bus_callback, this );
579 mbWatchID = true;
580 SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " set sync handler" );
581 gst_bus_set_sync_handler( pBus, pipeline_bus_sync_handler, this, nullptr );
582 g_object_unref( pBus );
586 bool Player::create( const OUString& rURL )
588 bool bRet = false;
590 // create all the elements and link them
592 SAL_INFO( "avmedia.gstreamer", "create player, URL: '" << rURL << "'" );
594 if( mbInitialized && !rURL.isEmpty() )
596 // fakesink for pre-roll & sizing ...
597 preparePlaybin( rURL, gst_element_factory_make( "fakesink", nullptr ) );
599 gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
600 mbPlayPending = false;
602 bRet = true;
605 if( bRet )
606 maURL = rURL;
607 else
608 maURL.clear();
610 return bRet;
614 void SAL_CALL Player::start()
616 ::osl::MutexGuard aGuard(m_aMutex);
618 // set the pipeline state to READY and run the loop
619 if( mbInitialized && mpPlaybin != nullptr )
621 gst_element_set_state( mpPlaybin, GST_STATE_PLAYING );
622 mbPlayPending = true;
627 void SAL_CALL Player::stop()
629 ::osl::MutexGuard aGuard(m_aMutex);
631 // set the pipeline in PAUSED STATE
632 if( mpPlaybin )
633 gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
635 mbPlayPending = false;
636 SAL_INFO( "avmedia.gstreamer", AVVERSION "stop " << mpPlaybin );
640 sal_Bool SAL_CALL Player::isPlaying()
642 ::osl::MutexGuard aGuard(m_aMutex);
644 bool bRet = mbPlayPending;
646 // return whether the pipeline is in PLAYING STATE or not
647 if( !mbPlayPending && mbInitialized && mpPlaybin )
649 bRet = GST_STATE( mpPlaybin ) == GST_STATE_PLAYING;
652 SAL_INFO( "avmedia.gstreamer", AVVERSION "isPlaying " << bRet );
654 return bRet;
658 double SAL_CALL Player::getDuration()
660 ::osl::MutexGuard aGuard(m_aMutex);
662 // slideshow checks for non-zero duration, so cheat here
663 double duration = 0.3;
665 if( mpPlaybin && mnDuration > 0 ) {
666 duration = mnDuration / GST_SECOND;
669 return duration;
673 void SAL_CALL Player::setMediaTime( double fTime )
675 ::osl::MutexGuard aGuard(m_aMutex);
677 if( !mpPlaybin )
678 return;
680 gint64 gst_position = llround (fTime * GST_SECOND);
682 gst_element_seek( mpPlaybin, 1.0,
683 GST_FORMAT_TIME,
684 GST_SEEK_FLAG_FLUSH,
685 GST_SEEK_TYPE_SET, gst_position,
686 GST_SEEK_TYPE_NONE, 0 );
688 SAL_INFO( "avmedia.gstreamer", AVVERSION "seek to: " << gst_position << " ns original: " << fTime << " s" );
692 double SAL_CALL Player::getMediaTime()
694 ::osl::MutexGuard aGuard(m_aMutex);
696 double position = 0.0;
698 if( mpPlaybin ) {
699 // get current position in the stream
700 gint64 gst_position;
701 if( gst_element_query_position( mpPlaybin, GST_FORMAT_TIME, &gst_position ) )
702 position = gst_position / GST_SECOND;
705 return position;
709 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
711 ::osl::MutexGuard aGuard(m_aMutex);
712 // TODO check how to do with GST
713 mbLooping = bSet;
717 sal_Bool SAL_CALL Player::isPlaybackLoop()
719 ::osl::MutexGuard aGuard(m_aMutex);
720 // TODO check how to do with GST
721 return mbLooping;
725 void SAL_CALL Player::setMute( sal_Bool bSet )
727 ::osl::MutexGuard aGuard(m_aMutex);
729 SAL_INFO( "avmedia.gstreamer", AVVERSION "set mute: " << bSet << " muted: " << mbMuted << " unmuted volume: " << mnUnmutedVolume );
731 // change the volume to 0 or the unmuted volume
732 if (mpVolumeControl && mbMuted != bool(bSet))
734 double nVolume = mnUnmutedVolume;
735 if( bSet )
737 nVolume = 0.0;
740 g_object_set( G_OBJECT( mpVolumeControl ), "volume", nVolume, nullptr );
742 mbMuted = bSet;
747 sal_Bool SAL_CALL Player::isMute()
749 ::osl::MutexGuard aGuard(m_aMutex);
751 return mbMuted;
755 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
757 ::osl::MutexGuard aGuard(m_aMutex);
759 mnUnmutedVolume = pow( 10.0, nVolumeDB / 20.0 );
761 SAL_INFO( "avmedia.gstreamer", AVVERSION "set volume: " << nVolumeDB << " gst volume: " << mnUnmutedVolume );
763 // change volume
764 if (mpVolumeControl && !mbMuted)
766 g_object_set( G_OBJECT( mpVolumeControl ), "volume", mnUnmutedVolume, nullptr );
771 sal_Int16 SAL_CALL Player::getVolumeDB()
773 ::osl::MutexGuard aGuard(m_aMutex);
775 sal_Int16 nVolumeDB(0);
777 if (mpVolumeControl)
779 double nGstVolume = 0.0;
781 g_object_get( G_OBJECT( mpVolumeControl ), "volume", &nGstVolume, nullptr );
783 nVolumeDB = static_cast<sal_Int16>( 20.0*log10 ( nGstVolume ) );
786 return nVolumeDB;
790 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
792 ::osl::MutexGuard aGuard(m_aMutex);
794 awt::Size aSize( 0, 0 );
796 if( maURL.isEmpty() )
798 SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::getPreferredPlayerWindowSize - empty URL => 0x0" );
799 return aSize;
802 SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " pre-Player::getPreferredPlayerWindowSize, member " << mnWidth << "x" << mnHeight );
804 osl::Condition::Result aResult = maSizeCondition.wait( std::chrono::seconds(10) );
806 SAL_INFO( "avmedia.gstreamer", AVVERSION << this << " Player::getPreferredPlayerWindowSize after waitCondition " << aResult << ", member " << mnWidth << "x" << mnHeight );
808 if( mnWidth != 0 && mnHeight != 0 ) {
809 aSize.Width = mnWidth;
810 aSize.Height = mnHeight;
813 return aSize;
816 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& rArguments )
818 ::osl::MutexGuard aGuard(m_aMutex);
820 uno::Reference< ::media::XPlayerWindow > xRet;
822 if (rArguments.getLength() > 1)
823 rArguments[1] >>= maArea;
825 awt::Size aSize = getPreferredPlayerWindowSize();
827 if( mbFakeVideo )
828 preparePlaybin( maURL, nullptr );
830 SAL_INFO( "avmedia.gstreamer", AVVERSION "Player::createPlayerWindow " << aSize.Width << "x" << aSize.Height << " length: " << rArguments.getLength() );
832 if( aSize.Width > 0 && aSize.Height > 0 )
834 if (rArguments.getLength() <= 2)
836 xRet = new ::avmedia::gstreamer::Window;
837 return xRet;
840 sal_IntPtr pIntPtr = 0;
841 rArguments[ 2 ] >>= pIntPtr;
842 SystemChildWindow *pParentWindow = reinterpret_cast< SystemChildWindow* >( pIntPtr );
843 if (!pParentWindow)
844 return nullptr;
846 const SystemEnvData* pEnvData = pParentWindow->GetSystemData();
847 if (!pEnvData)
848 return nullptr;
850 // tdf#124027: the position of embedded window is identical w/ the position
851 // of media object in all other vclplugs (kf5, gen), in gtk3 w/o gtksink it
852 // needs to be translated
853 if (pEnvData->toolkit == SystemEnvData::Toolkit::Gtk3)
855 Point aPoint = pParentWindow->GetPosPixel();
856 maArea.X = aPoint.getX();
857 maArea.Y = aPoint.getY();
860 mbUseGtkSink = false;
862 GstElement *pVideosink = static_cast<GstElement*>(pParentWindow->CreateGStreamerSink());
863 if (pVideosink)
865 if (pEnvData->toolkit == SystemEnvData::Toolkit::Gtk3)
866 mbUseGtkSink = true;
868 else
870 if (pEnvData->platform == SystemEnvData::Platform::Wayland)
871 pVideosink = gst_element_factory_make("waylandsink", "video-output");
872 else
873 pVideosink = gst_element_factory_make("autovideosink", "video-output");
874 if (!pVideosink)
875 return nullptr;
878 xRet = new ::avmedia::gstreamer::Window;
880 g_object_set(G_OBJECT(mpPlaybin), "video-sink", pVideosink, nullptr);
881 g_object_set(G_OBJECT(mpPlaybin), "force-aspect-ratio", FALSE, nullptr);
883 if (!mbUseGtkSink)
885 mnWindowID = pEnvData->GetWindowHandle(pParentWindow->ImplGetFrame());
886 mpDisplay = pEnvData->pDisplay;
887 SAL_INFO( "avmedia.gstreamer", AVVERSION "set window id to " << static_cast<int>(mnWindowID) << " XOverlay " << mpXOverlay);
889 gst_element_set_state( mpPlaybin, GST_STATE_PAUSED );
890 if (!mbUseGtkSink && mpXOverlay)
891 gst_video_overlay_set_window_handle( mpXOverlay, mnWindowID );
894 return xRet;
897 uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
899 ::osl::MutexGuard aGuard(m_aMutex);
900 FrameGrabber* pFrameGrabber = nullptr;
901 const awt::Size aPrefSize( getPreferredPlayerWindowSize() );
903 if( ( aPrefSize.Width > 0 ) && ( aPrefSize.Height > 0 ) )
904 pFrameGrabber = FrameGrabber::create( maURL );
905 SAL_INFO( "avmedia.gstreamer", AVVERSION "created FrameGrabber " << pFrameGrabber );
907 return pFrameGrabber;
911 OUString SAL_CALL Player::getImplementationName()
913 return AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME;
917 sal_Bool SAL_CALL Player::supportsService( const OUString& ServiceName )
919 return cppu::supportsService(this, ServiceName);
923 uno::Sequence< OUString > SAL_CALL Player::getSupportedServiceNames()
925 return { AVMEDIA_GST_PLAYER_SERVICENAME };
928 } // namespace
930 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */