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 .
22 #include <rtl/string.hxx>
24 #include <vcl/syschild.hxx>
25 #include <vcl/sysdata.hxx>
27 #include "gstplayer.hxx"
28 #include "gstframegrabber.hxx"
29 #include "gstwindow.hxx"
31 #ifdef AVMEDIA_GST_0_10
32 # define AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_GStreamer_0_10"
33 # define AVMEDIA_GST_PLAYER_SERVICENAME "com.sun.star.media.Player_GStreamer_0_10"
35 # include <gst/video/videooverlay.h>
36 # define AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_GStreamer"
37 # define AVMEDIA_GST_PLAYER_SERVICENAME "com.sun.star.media.Player_GStreamer"
40 #ifdef AVMEDIA_GST_0_10
41 # define AVVERSION "gst 0.10: "
43 # define AVVERSION "gst 1.0: "
47 # if OSL_DEBUG_LEVEL > 2
48 #define DBG(...) do { fprintf (stderr, "%s", AVVERSION); fprintf (stderr, __VA_ARGS__); fprintf (stderr, "\n"); } while (0);
54 using namespace ::com::sun::star
;
56 namespace avmedia
{ namespace gstreamer
{
62 Player::Player( const uno::Reference
< lang::XMultiServiceFactory
>& rxMgr
) :
63 GstPlayer_BASE( m_aMutex
),
66 mbFakeVideo (sal_False
),
68 mbPlayPending ( false ),
71 mbInitialized( false ),
78 // Initialize GStreamer library
80 char name
[] = "libreoffice";
81 char *arguments
[] = { name
};
82 char** argv
= arguments
;
83 GError
* pError
= NULL
;
85 mbInitialized
= gst_init_check( &argc
, &argv
, &pError
);
87 DBG( "%p Player::Player", this );
91 // TODO: thow an exception?
92 DBG( "%p Player::Player error '%s'", this, pError
->message
);
93 g_error_free (pError
);
97 // ------------------------------------------------------------------------------
101 DBG( "%p Player::~Player", this );
106 void SAL_CALL
Player::disposing()
108 ::osl::MutexGuard
aGuard(m_aMutex
);
112 DBG( "%p Player::disposing", this );
114 // Release the elements and pipeline
119 gst_element_set_state( mpPlaybin
, GST_STATE_NULL
);
120 g_object_unref( G_OBJECT( mpPlaybin
) );
126 g_object_unref( G_OBJECT ( mpXOverlay
) );
132 // ------------------------------------------------------------------------------
134 static gboolean
pipeline_bus_callback( GstBus
*, GstMessage
*message
, gpointer data
)
136 Player
* pPlayer
= static_cast<Player
*>(data
);
138 pPlayer
->processMessage( message
);
143 static GstBusSyncReply
pipeline_bus_sync_handler( GstBus
*, GstMessage
* message
, gpointer data
)
145 Player
* pPlayer
= static_cast<Player
*>(data
);
147 return pPlayer
->processSyncMessage( message
);
150 void Player::processMessage( GstMessage
*message
)
152 switch( GST_MESSAGE_TYPE( message
) ) {
153 case GST_MESSAGE_EOS
:
154 gst_element_set_state( mpPlaybin
, GST_STATE_READY
);
155 mbPlayPending
= false;
159 case GST_MESSAGE_STATE_CHANGED
:
160 if( message
->src
== GST_OBJECT( mpPlaybin
) ) {
161 GstState newstate
, pendingstate
;
163 gst_message_parse_state_changed (message
, NULL
, &newstate
, &pendingstate
);
165 if( newstate
== GST_STATE_PAUSED
&&
166 pendingstate
== GST_STATE_VOID_PENDING
&&
168 gst_video_overlay_expose( mpXOverlay
);
171 mbPlayPending
= ((newstate
== GST_STATE_READY
) || (newstate
== GST_STATE_PAUSED
));
178 static gboolean
wrap_element_query_position (GstElement
*element
, GstFormat format
, gint64
*cur
)
180 #ifdef AVMEDIA_GST_0_10
181 GstFormat my_format
= format
;
182 return gst_element_query_position( element
, &my_format
, cur
) && my_format
== format
&& *cur
> 0L;
184 return gst_element_query_position( element
, format
, cur
);
188 static gboolean
wrap_element_query_duration (GstElement
*element
, GstFormat format
, gint64
*duration
)
190 #ifdef AVMEDIA_GST_0_10
191 GstFormat my_format
= format
;
192 return gst_element_query_duration( element
, &my_format
, duration
) && my_format
== format
&& *duration
> 0L;
194 return gst_element_query_duration( element
, format
, duration
);
198 GstBusSyncReply
Player::processSyncMessage( GstMessage
*message
)
200 // DBG( "%p processSyncMessage has handle: %s", this, GST_MESSAGE_TYPE_NAME( message ) );
202 #if OSL_DEBUG_LEVEL > 0
203 if ( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ERROR
)
208 gst_message_parse_error( message
, &error
, &error_debug
);
209 fprintf(stderr
, "gstreamer error: '%s' debug: '%s'", error
->message
, error_debug
);
213 #ifdef AVMEDIA_GST_0_10
214 if (message
->structure
&&
215 !strcmp( gst_structure_get_name( message
->structure
), "prepare-xwindow-id" ) )
217 if (gst_is_video_overlay_prepare_window_handle_message (message
) )
220 DBG( "%p processSyncMessage prepare window id: %s %d", this,
221 GST_MESSAGE_TYPE_NAME( message
), (int)mnWindowID
);
223 g_object_unref( G_OBJECT ( mpXOverlay
) );
224 g_object_set( GST_MESSAGE_SRC( message
), "force-aspect-ratio", FALSE
, NULL
);
225 mpXOverlay
= GST_VIDEO_OVERLAY( GST_MESSAGE_SRC( message
) );
226 g_object_ref( G_OBJECT ( mpXOverlay
) );
227 if ( mnWindowID
!= 0 )
228 gst_video_overlay_set_window_handle( mpXOverlay
, mnWindowID
);
232 #ifdef AVMEDIA_GST_0_10
233 if( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_STATE_CHANGED
) {
234 if( message
->src
== GST_OBJECT( mpPlaybin
) ) {
235 GstState newstate
, pendingstate
;
237 gst_message_parse_state_changed (message
, NULL
, &newstate
, &pendingstate
);
239 DBG( "%p state change received, new state %d pending %d", this,
240 (int)newstate
, (int)pendingstate
);
241 if( newstate
== GST_STATE_PAUSED
&&
242 pendingstate
== GST_STATE_VOID_PENDING
) {
244 DBG( "%p change to paused received", this );
246 if( mnDuration
== 0) {
247 gint64 gst_duration
= 0L;
248 if( wrap_element_query_duration( mpPlaybin
, GST_FORMAT_TIME
, &gst_duration
) )
249 mnDuration
= gst_duration
;
253 GList
*pStreamInfo
= NULL
;
255 g_object_get( G_OBJECT( mpPlaybin
), "stream-info", &pStreamInfo
, NULL
);
257 for ( ; pStreamInfo
!= NULL
; pStreamInfo
= pStreamInfo
->next
) {
258 GObject
*pInfo
= G_OBJECT( pStreamInfo
->data
);
264 g_object_get( pInfo
, "type", &nType
, NULL
);
265 GEnumValue
*pValue
= g_enum_get_value( G_PARAM_SPEC_ENUM( g_object_class_find_property( G_OBJECT_GET_CLASS( pInfo
), "type" ) )->enum_class
,
268 if( !g_ascii_strcasecmp( pValue
->value_nick
, "video" ) ) {
269 GstStructure
*pStructure
;
272 g_object_get( pInfo
, "object", &pPad
, NULL
);
273 pStructure
= gst_caps_get_structure( GST_PAD_CAPS( pPad
), 0 );
275 gst_structure_get_int( pStructure
, "width", &mnWidth
);
276 gst_structure_get_int( pStructure
, "height", &mnHeight
);
277 DBG( "queried size: %d x %d", mnWidth
, mnHeight
);
279 g_object_unref (pPad
);
283 maSizeCondition
.set();
288 // We get to use the exciting new playbin2 ! (now known as playbin)
289 if( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ASYNC_DONE
) {
290 if( mnDuration
== 0) {
291 gint64 gst_duration
= 0L;
292 if( wrap_element_query_duration( mpPlaybin
, GST_FORMAT_TIME
, &gst_duration
) )
293 mnDuration
= gst_duration
;
299 g_signal_emit_by_name( mpPlaybin
, "get-video-pad", 0, &pad
);
304 caps
= gst_pad_get_current_caps( pad
);
306 if( gst_structure_get( gst_caps_get_structure( caps
, 0 ),
307 "width", G_TYPE_INT
, &w
,
308 "height", G_TYPE_INT
, &h
,
313 DBG( "queried size: %d x %d", mnWidth
, mnHeight
);
315 maSizeCondition
.set();
317 gst_caps_unref( caps
);
318 g_object_unref( pad
);
322 } else if( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ERROR
) {
325 // an error occurred, set condition so that OOo thread doesn't wait for us
326 maSizeCondition
.set();
333 void Player::preparePlaybin( const OUString
& rURL
, GstElement
*pSink
)
337 if( mpPlaybin
!= NULL
) {
338 gst_element_set_state( mpPlaybin
, GST_STATE_NULL
);
339 mbPlayPending
= false;
340 g_object_unref( mpPlaybin
);
343 mpPlaybin
= gst_element_factory_make( "playbin", NULL
);
344 if( pSink
!= NULL
) // used for getting prefered size etc.
346 g_object_set( G_OBJECT( mpPlaybin
), "video-sink", pSink
, NULL
);
352 rtl::OString ascURL
= OUStringToOString( rURL
, RTL_TEXTENCODING_UTF8
);
353 g_object_set( G_OBJECT( mpPlaybin
), "uri", ascURL
.getStr() , NULL
);
355 pBus
= gst_element_get_bus( mpPlaybin
);
356 gst_bus_add_watch( pBus
, pipeline_bus_callback
, this );
357 DBG( "%p set sync handler", this );
358 #ifdef AVMEDIA_GST_0_10
359 gst_bus_set_sync_handler( pBus
, pipeline_bus_sync_handler
, this );
361 gst_bus_set_sync_handler( pBus
, pipeline_bus_sync_handler
, this, NULL
);
363 g_object_unref( pBus
);
366 bool Player::create( const OUString
& rURL
)
370 // create all the elements and link them
372 DBG("create player, URL: %s", OUStringToOString( rURL
, RTL_TEXTENCODING_UTF8
).getStr());
374 if( mbInitialized
&& !rURL
.isEmpty() )
376 // fakesink for pre-roll & sizing ...
377 preparePlaybin( rURL
, gst_element_factory_make( "fakesink", NULL
) );
379 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
380 mbPlayPending
= false;
393 // ------------------------------------------------------------------------------
395 void SAL_CALL
Player::start()
396 throw (uno::RuntimeException
)
398 ::osl::MutexGuard
aGuard(m_aMutex
);
400 // set the pipeline state to READY and run the loop
401 if( mbInitialized
&& NULL
!= mpPlaybin
)
403 gst_element_set_state( mpPlaybin
, GST_STATE_PLAYING
);
404 mbPlayPending
= true;
408 // ------------------------------------------------------------------------------
410 void SAL_CALL
Player::stop()
411 throw (uno::RuntimeException
)
413 ::osl::MutexGuard
aGuard(m_aMutex
);
415 // set the pipeline in PAUSED STATE
417 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
419 mbPlayPending
= false;
420 DBG( "stop %p", mpPlaybin
);
423 // ------------------------------------------------------------------------------
425 sal_Bool SAL_CALL
Player::isPlaying()
426 throw (uno::RuntimeException
)
428 ::osl::MutexGuard
aGuard(m_aMutex
);
430 bool bRet
= mbPlayPending
;
432 // return whether the pipeline is in PLAYING STATE or not
433 if( !mbPlayPending
&& mbInitialized
&& mpPlaybin
)
435 bRet
= GST_STATE_PLAYING
== GST_STATE( mpPlaybin
);
438 DBG( "isPlaying %d", bRet
);
443 // ------------------------------------------------------------------------------
445 double SAL_CALL
Player::getDuration()
446 throw (uno::RuntimeException
)
448 ::osl::MutexGuard
aGuard(m_aMutex
);
450 // slideshow checks for non-zero duration, so cheat here
451 double duration
= 0.01;
453 if( mpPlaybin
&& mnDuration
> 0 ) {
454 duration
= mnDuration
/ 1E9
;
460 // ------------------------------------------------------------------------------
462 void SAL_CALL
Player::setMediaTime( double fTime
)
463 throw (uno::RuntimeException
)
465 ::osl::MutexGuard
aGuard(m_aMutex
);
468 gint64 gst_position
= llround (fTime
* 1E9
);
470 gst_element_seek( mpPlaybin
, 1.0,
473 GST_SEEK_TYPE_SET
, gst_position
,
474 GST_SEEK_TYPE_NONE
, 0 );
476 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
478 DBG( "seek to: %" SAL_PRIdINT64
" ns original: %lf s", gst_position
, fTime
);
482 // ------------------------------------------------------------------------------
484 double SAL_CALL
Player::getMediaTime()
485 throw (uno::RuntimeException
)
487 ::osl::MutexGuard
aGuard(m_aMutex
);
489 double position
= 0.0;
492 // get current position in the stream
494 if( wrap_element_query_position( mpPlaybin
, GST_FORMAT_TIME
, &gst_position
) )
495 position
= gst_position
/ 1E9
;
501 // ------------------------------------------------------------------------------
503 double SAL_CALL
Player::getRate()
504 throw (uno::RuntimeException
)
506 ::osl::MutexGuard
aGuard(m_aMutex
);
510 // TODO get the window rate - but no need since
511 // higher levels never set rate > 1
516 // ------------------------------------------------------------------------------
518 void SAL_CALL
Player::setPlaybackLoop( sal_Bool bSet
)
519 throw (uno::RuntimeException
)
521 ::osl::MutexGuard
aGuard(m_aMutex
);
522 // TODO check how to do with GST
526 // ------------------------------------------------------------------------------
528 sal_Bool SAL_CALL
Player::isPlaybackLoop()
529 throw (uno::RuntimeException
)
531 ::osl::MutexGuard
aGuard(m_aMutex
);
532 // TODO check how to do with GST
536 // ------------------------------------------------------------------------------
538 void SAL_CALL
Player::setMute( sal_Bool bSet
)
539 throw (uno::RuntimeException
)
541 ::osl::MutexGuard
aGuard(m_aMutex
);
543 DBG( "set mute: %d muted: %d unmuted volume: %lf", bSet
, mbMuted
, mnUnmutedVolume
);
545 // change the volume to 0 or the unmuted volume
546 if( mpPlaybin
&& mbMuted
!= bSet
)
548 double nVolume
= mnUnmutedVolume
;
554 g_object_set( G_OBJECT( mpPlaybin
), "volume", nVolume
, NULL
);
560 // ------------------------------------------------------------------------------
562 sal_Bool SAL_CALL
Player::isMute()
563 throw (uno::RuntimeException
)
565 ::osl::MutexGuard
aGuard(m_aMutex
);
570 // ------------------------------------------------------------------------------
572 void SAL_CALL
Player::setVolumeDB( sal_Int16 nVolumeDB
)
573 throw (uno::RuntimeException
)
575 ::osl::MutexGuard
aGuard(m_aMutex
);
577 mnUnmutedVolume
= pow( 10.0, nVolumeDB
/ 20.0 );
579 DBG( "set volume: %d gst volume: %lf", nVolumeDB
, mnUnmutedVolume
);
582 if( !mbMuted
&& mpPlaybin
)
584 g_object_set( G_OBJECT( mpPlaybin
), "volume", (gdouble
) mnUnmutedVolume
, NULL
);
588 // ------------------------------------------------------------------------------
590 sal_Int16 SAL_CALL
Player::getVolumeDB()
591 throw (uno::RuntimeException
)
593 ::osl::MutexGuard
aGuard(m_aMutex
);
595 sal_Int16
nVolumeDB(0);
598 double nGstVolume
= 0.0;
600 g_object_get( G_OBJECT( mpPlaybin
), "volume", &nGstVolume
, NULL
);
602 nVolumeDB
= (sal_Int16
) ( 20.0*log10 ( nGstVolume
) );
608 // ------------------------------------------------------------------------------
610 awt::Size SAL_CALL
Player::getPreferredPlayerWindowSize()
611 throw (uno::RuntimeException
)
613 ::osl::MutexGuard
aGuard(m_aMutex
);
615 awt::Size
aSize( 0, 0 );
617 if( maURL
.isEmpty() )
619 DBG( "%p Player::getPreferredPlayerWindowSize - empty URL => 0x0", this );
623 DBG( "%p pre-Player::getPreferredPlayerWindowSize, member %d x %d", this, mnWidth
, mnHeight
);
625 TimeValue aTimeout
= { 10, 0 };
626 #if OSL_DEBUG_LEVEL > 2
627 osl::Condition::Result aResult
=
629 maSizeCondition
.wait( &aTimeout
);
631 DBG( "%p Player::getPreferredPlayerWindowSize after waitCondition %d, member %d x %d", this, aResult
, mnWidth
, mnHeight
);
633 if( mnWidth
!= 0 && mnHeight
!= 0 ) {
634 aSize
.Width
= mnWidth
;
635 aSize
.Height
= mnHeight
;
641 // ------------------------------------------------------------------------------
643 uno::Reference
< ::media::XPlayerWindow
> SAL_CALL
Player::createPlayerWindow( const uno::Sequence
< uno::Any
>& rArguments
)
644 throw (uno::RuntimeException
)
646 ::osl::MutexGuard
aGuard(m_aMutex
);
648 uno::Reference
< ::media::XPlayerWindow
> xRet
;
649 awt::Size
aSize( getPreferredPlayerWindowSize() );
652 preparePlaybin( maURL
, NULL
);
654 DBG( "Player::createPlayerWindow %d %d length: %d", aSize
.Width
, aSize
.Height
, rArguments
.getLength() );
656 if( aSize
.Width
> 0 && aSize
.Height
> 0 )
658 ::avmedia::gstreamer::Window
* pWindow
= new ::avmedia::gstreamer::Window( mxMgr
, *this );
662 if( rArguments
.getLength() > 2 )
664 sal_IntPtr pIntPtr
= 0;
665 rArguments
[ 2 ] >>= pIntPtr
;
666 SystemChildWindow
*pParentWindow
= reinterpret_cast< SystemChildWindow
* >( pIntPtr
);
667 const SystemEnvData
* pEnvData
= pParentWindow
? pParentWindow
->GetSystemData() : NULL
;
668 OSL_ASSERT(pEnvData
);
671 mnWindowID
= pEnvData
->aWindow
;
672 DBG( "set window id to %d XOverlay %p\n", (int)mnWindowID
, mpXOverlay
);
673 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
674 if ( mpXOverlay
!= NULL
)
675 gst_video_overlay_set_window_handle( mpXOverlay
, mnWindowID
);
683 // ------------------------------------------------------------------------------
685 uno::Reference
< media::XFrameGrabber
> SAL_CALL
Player::createFrameGrabber()
686 throw (uno::RuntimeException
)
688 ::osl::MutexGuard
aGuard(m_aMutex
);
689 FrameGrabber
* pFrameGrabber
= NULL
;
690 const awt::Size
aPrefSize( getPreferredPlayerWindowSize() );
692 if( ( aPrefSize
.Width
> 0 ) && ( aPrefSize
.Height
> 0 ) )
693 pFrameGrabber
= FrameGrabber::create( maURL
);
694 DBG( "created FrameGrabber %p", pFrameGrabber
);
696 return pFrameGrabber
;
699 // ------------------------------------------------------------------------------
701 OUString SAL_CALL
Player::getImplementationName()
702 throw (uno::RuntimeException
)
704 return OUString( AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME
);
707 // ------------------------------------------------------------------------------
709 sal_Bool SAL_CALL
Player::supportsService( const OUString
& ServiceName
)
710 throw (uno::RuntimeException
)
712 return ServiceName
== AVMEDIA_GST_PLAYER_SERVICENAME
;
715 // ------------------------------------------------------------------------------
717 uno::Sequence
< OUString
> SAL_CALL
Player::getSupportedServiceNames()
718 throw (uno::RuntimeException
)
720 uno::Sequence
< OUString
> aRet(1);
721 aRet
[0] = AVMEDIA_GST_PLAYER_SERVICENAME
;
726 } // namespace gstreamer
727 } // namespace avmedia
729 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */