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"
41 # if OSL_DEBUG_LEVEL > 2
42 #ifdef AVMEDIA_GST_0_10
43 # define AVVERSION "gst 0.10: "
45 # define AVVERSION "gst 1.0: "
47 #define DBG(...) do { fprintf (stderr, "%s", AVVERSION); fprintf (stderr, __VA_ARGS__); fprintf (stderr, "\n"); } while (0);
53 using namespace ::com::sun::star
;
55 namespace avmedia
{ namespace gstreamer
{
61 Player::Player( const uno::Reference
< lang::XMultiServiceFactory
>& rxMgr
) :
62 GstPlayer_BASE( m_aMutex
),
65 mbFakeVideo (sal_False
),
67 mbPlayPending ( false ),
70 mbInitialized( false ),
77 // Initialize GStreamer library
79 char name
[] = "libreoffice";
80 char *arguments
[] = { name
};
81 char** argv
= arguments
;
82 GError
* pError
= NULL
;
84 mbInitialized
= gst_init_check( &argc
, &argv
, &pError
);
86 DBG( "%p Player::Player", this );
90 // TODO: thow an exception?
91 DBG( "%p Player::Player error '%s'", this, pError
->message
);
92 g_error_free (pError
);
96 // ------------------------------------------------------------------------------
100 DBG( "%p Player::~Player", this );
105 void SAL_CALL
Player::disposing()
107 ::osl::MutexGuard
aGuard(m_aMutex
);
111 DBG( "%p Player::disposing", this );
113 // Release the elements and pipeline
118 gst_element_set_state( mpPlaybin
, GST_STATE_NULL
);
119 g_object_unref( G_OBJECT( mpPlaybin
) );
125 g_object_unref( G_OBJECT ( mpXOverlay
) );
131 // ------------------------------------------------------------------------------
133 static gboolean
pipeline_bus_callback( GstBus
*, GstMessage
*message
, gpointer data
)
135 Player
* pPlayer
= static_cast<Player
*>(data
);
137 pPlayer
->processMessage( message
);
142 static GstBusSyncReply
pipeline_bus_sync_handler( GstBus
*, GstMessage
* message
, gpointer data
)
144 Player
* pPlayer
= static_cast<Player
*>(data
);
146 return pPlayer
->processSyncMessage( message
);
149 void Player::processMessage( GstMessage
*message
)
151 switch( GST_MESSAGE_TYPE( message
) ) {
152 case GST_MESSAGE_EOS
:
153 gst_element_set_state( mpPlaybin
, GST_STATE_READY
);
154 mbPlayPending
= false;
158 case GST_MESSAGE_STATE_CHANGED
:
159 if( message
->src
== GST_OBJECT( mpPlaybin
) ) {
160 GstState newstate
, pendingstate
;
162 gst_message_parse_state_changed (message
, NULL
, &newstate
, &pendingstate
);
164 if( newstate
== GST_STATE_PAUSED
&&
165 pendingstate
== GST_STATE_VOID_PENDING
&&
167 gst_video_overlay_expose( mpXOverlay
);
170 mbPlayPending
= ((newstate
== GST_STATE_READY
) || (newstate
== GST_STATE_PAUSED
));
177 static gboolean
wrap_element_query_position (GstElement
*element
, GstFormat format
, gint64
*cur
)
179 #ifdef AVMEDIA_GST_0_10
180 GstFormat my_format
= format
;
181 return gst_element_query_position( element
, &my_format
, cur
) && my_format
== format
&& *cur
> 0L;
183 return gst_element_query_position( element
, format
, cur
);
187 static gboolean
wrap_element_query_duration (GstElement
*element
, GstFormat format
, gint64
*duration
)
189 #ifdef AVMEDIA_GST_0_10
190 GstFormat my_format
= format
;
191 return gst_element_query_duration( element
, &my_format
, duration
) && my_format
== format
&& *duration
> 0L;
193 return gst_element_query_duration( element
, format
, duration
);
197 GstBusSyncReply
Player::processSyncMessage( GstMessage
*message
)
199 // DBG( "%p processSyncMessage has handle: %s", this, GST_MESSAGE_TYPE_NAME( message ) );
201 #if OSL_DEBUG_LEVEL > 0
202 if ( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ERROR
)
207 gst_message_parse_error( message
, &error
, &error_debug
);
210 "gstreamer error: '" << error
->message
<< "' debug: '"
211 << error_debug
<< "'");
215 #ifdef AVMEDIA_GST_0_10
216 if (message
->structure
&&
217 !strcmp( gst_structure_get_name( message
->structure
), "prepare-xwindow-id" ) )
219 if (gst_is_video_overlay_prepare_window_handle_message (message
) )
222 DBG( "%p processSyncMessage prepare window id: %s %d", this,
223 GST_MESSAGE_TYPE_NAME( message
), (int)mnWindowID
);
225 g_object_unref( G_OBJECT ( mpXOverlay
) );
226 g_object_set( GST_MESSAGE_SRC( message
), "force-aspect-ratio", FALSE
, NULL
);
227 mpXOverlay
= GST_VIDEO_OVERLAY( GST_MESSAGE_SRC( message
) );
228 g_object_ref( G_OBJECT ( mpXOverlay
) );
229 if ( mnWindowID
!= 0 )
230 gst_video_overlay_set_window_handle( mpXOverlay
, mnWindowID
);
234 #ifdef AVMEDIA_GST_0_10
235 if( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_STATE_CHANGED
) {
236 if( message
->src
== GST_OBJECT( mpPlaybin
) ) {
237 GstState newstate
, pendingstate
;
239 gst_message_parse_state_changed (message
, NULL
, &newstate
, &pendingstate
);
241 DBG( "%p state change received, new state %d pending %d", this,
242 (int)newstate
, (int)pendingstate
);
243 if( newstate
== GST_STATE_PAUSED
&&
244 pendingstate
== GST_STATE_VOID_PENDING
) {
246 DBG( "%p change to paused received", this );
248 if( mnDuration
== 0) {
249 gint64 gst_duration
= 0L;
250 if( wrap_element_query_duration( mpPlaybin
, GST_FORMAT_TIME
, &gst_duration
) )
251 mnDuration
= gst_duration
;
255 GList
*pStreamInfo
= NULL
;
257 g_object_get( G_OBJECT( mpPlaybin
), "stream-info", &pStreamInfo
, NULL
);
259 for ( ; pStreamInfo
!= NULL
; pStreamInfo
= pStreamInfo
->next
) {
260 GObject
*pInfo
= G_OBJECT( pStreamInfo
->data
);
266 g_object_get( pInfo
, "type", &nType
, NULL
);
267 GEnumValue
*pValue
= g_enum_get_value( G_PARAM_SPEC_ENUM( g_object_class_find_property( G_OBJECT_GET_CLASS( pInfo
), "type" ) )->enum_class
,
270 if( !g_ascii_strcasecmp( pValue
->value_nick
, "video" ) ) {
271 GstStructure
*pStructure
;
274 g_object_get( pInfo
, "object", &pPad
, NULL
);
275 pStructure
= gst_caps_get_structure( GST_PAD_CAPS( pPad
), 0 );
277 gst_structure_get_int( pStructure
, "width", &mnWidth
);
278 gst_structure_get_int( pStructure
, "height", &mnHeight
);
279 DBG( "queried size: %d x %d", mnWidth
, mnHeight
);
281 g_object_unref (pPad
);
285 maSizeCondition
.set();
290 // We get to use the exciting new playbin2 ! (now known as playbin)
291 if( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ASYNC_DONE
) {
292 if( mnDuration
== 0) {
293 gint64 gst_duration
= 0L;
294 if( wrap_element_query_duration( mpPlaybin
, GST_FORMAT_TIME
, &gst_duration
) )
295 mnDuration
= gst_duration
;
301 g_signal_emit_by_name( mpPlaybin
, "get-video-pad", 0, &pad
);
306 caps
= gst_pad_get_current_caps( pad
);
308 if( gst_structure_get( gst_caps_get_structure( caps
, 0 ),
309 "width", G_TYPE_INT
, &w
,
310 "height", G_TYPE_INT
, &h
,
315 DBG( "queried size: %d x %d", mnWidth
, mnHeight
);
317 maSizeCondition
.set();
319 gst_caps_unref( caps
);
320 g_object_unref( pad
);
324 } else if( GST_MESSAGE_TYPE( message
) == GST_MESSAGE_ERROR
) {
327 // an error occurred, set condition so that OOo thread doesn't wait for us
328 maSizeCondition
.set();
335 void Player::preparePlaybin( const OUString
& rURL
, GstElement
*pSink
)
339 if( mpPlaybin
!= NULL
) {
340 gst_element_set_state( mpPlaybin
, GST_STATE_NULL
);
341 mbPlayPending
= false;
342 g_object_unref( mpPlaybin
);
345 mpPlaybin
= gst_element_factory_make( "playbin", NULL
);
346 if( pSink
!= NULL
) // used for getting prefered size etc.
348 g_object_set( G_OBJECT( mpPlaybin
), "video-sink", pSink
, NULL
);
354 OString ascURL
= OUStringToOString( rURL
, RTL_TEXTENCODING_UTF8
);
355 g_object_set( G_OBJECT( mpPlaybin
), "uri", ascURL
.getStr() , NULL
);
357 pBus
= gst_element_get_bus( mpPlaybin
);
358 gst_bus_add_watch( pBus
, pipeline_bus_callback
, this );
359 DBG( "%p set sync handler", this );
360 #ifdef AVMEDIA_GST_0_10
361 gst_bus_set_sync_handler( pBus
, pipeline_bus_sync_handler
, this );
363 gst_bus_set_sync_handler( pBus
, pipeline_bus_sync_handler
, this, NULL
);
365 g_object_unref( pBus
);
368 bool Player::create( const OUString
& rURL
)
372 // create all the elements and link them
374 DBG("create player, URL: %s", OUStringToOString( rURL
, RTL_TEXTENCODING_UTF8
).getStr());
376 if( mbInitialized
&& !rURL
.isEmpty() )
378 // fakesink for pre-roll & sizing ...
379 preparePlaybin( rURL
, gst_element_factory_make( "fakesink", NULL
) );
381 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
382 mbPlayPending
= false;
395 // ------------------------------------------------------------------------------
397 void SAL_CALL
Player::start()
398 throw (uno::RuntimeException
)
400 ::osl::MutexGuard
aGuard(m_aMutex
);
402 // set the pipeline state to READY and run the loop
403 if( mbInitialized
&& NULL
!= mpPlaybin
)
405 gst_element_set_state( mpPlaybin
, GST_STATE_PLAYING
);
406 mbPlayPending
= true;
410 // ------------------------------------------------------------------------------
412 void SAL_CALL
Player::stop()
413 throw (uno::RuntimeException
)
415 ::osl::MutexGuard
aGuard(m_aMutex
);
417 // set the pipeline in PAUSED STATE
419 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
421 mbPlayPending
= false;
422 DBG( "stop %p", mpPlaybin
);
425 // ------------------------------------------------------------------------------
427 sal_Bool SAL_CALL
Player::isPlaying()
428 throw (uno::RuntimeException
)
430 ::osl::MutexGuard
aGuard(m_aMutex
);
432 bool bRet
= mbPlayPending
;
434 // return whether the pipeline is in PLAYING STATE or not
435 if( !mbPlayPending
&& mbInitialized
&& mpPlaybin
)
437 bRet
= GST_STATE_PLAYING
== GST_STATE( mpPlaybin
);
440 DBG( "isPlaying %d", bRet
);
445 // ------------------------------------------------------------------------------
447 double SAL_CALL
Player::getDuration()
448 throw (uno::RuntimeException
)
450 ::osl::MutexGuard
aGuard(m_aMutex
);
452 // slideshow checks for non-zero duration, so cheat here
453 double duration
= 0.01;
455 if( mpPlaybin
&& mnDuration
> 0 ) {
456 duration
= mnDuration
/ 1E9
;
462 // ------------------------------------------------------------------------------
464 void SAL_CALL
Player::setMediaTime( double fTime
)
465 throw (uno::RuntimeException
)
467 ::osl::MutexGuard
aGuard(m_aMutex
);
470 gint64 gst_position
= llround (fTime
* 1E9
);
472 gst_element_seek( mpPlaybin
, 1.0,
475 GST_SEEK_TYPE_SET
, gst_position
,
476 GST_SEEK_TYPE_NONE
, 0 );
478 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
480 DBG( "seek to: %" SAL_PRIdINT64
" ns original: %lf s", gst_position
, fTime
);
484 // ------------------------------------------------------------------------------
486 double SAL_CALL
Player::getMediaTime()
487 throw (uno::RuntimeException
)
489 ::osl::MutexGuard
aGuard(m_aMutex
);
491 double position
= 0.0;
494 // get current position in the stream
496 if( wrap_element_query_position( mpPlaybin
, GST_FORMAT_TIME
, &gst_position
) )
497 position
= gst_position
/ 1E9
;
503 // ------------------------------------------------------------------------------
505 double SAL_CALL
Player::getRate()
506 throw (uno::RuntimeException
)
508 ::osl::MutexGuard
aGuard(m_aMutex
);
512 // TODO get the window rate - but no need since
513 // higher levels never set rate > 1
518 // ------------------------------------------------------------------------------
520 void SAL_CALL
Player::setPlaybackLoop( sal_Bool bSet
)
521 throw (uno::RuntimeException
)
523 ::osl::MutexGuard
aGuard(m_aMutex
);
524 // TODO check how to do with GST
528 // ------------------------------------------------------------------------------
530 sal_Bool SAL_CALL
Player::isPlaybackLoop()
531 throw (uno::RuntimeException
)
533 ::osl::MutexGuard
aGuard(m_aMutex
);
534 // TODO check how to do with GST
538 // ------------------------------------------------------------------------------
540 void SAL_CALL
Player::setMute( sal_Bool bSet
)
541 throw (uno::RuntimeException
)
543 ::osl::MutexGuard
aGuard(m_aMutex
);
545 DBG( "set mute: %d muted: %d unmuted volume: %lf", bSet
, mbMuted
, mnUnmutedVolume
);
547 // change the volume to 0 or the unmuted volume
548 if( mpPlaybin
&& mbMuted
!= bSet
)
550 double nVolume
= mnUnmutedVolume
;
556 g_object_set( G_OBJECT( mpPlaybin
), "volume", nVolume
, NULL
);
562 // ------------------------------------------------------------------------------
564 sal_Bool SAL_CALL
Player::isMute()
565 throw (uno::RuntimeException
)
567 ::osl::MutexGuard
aGuard(m_aMutex
);
572 // ------------------------------------------------------------------------------
574 void SAL_CALL
Player::setVolumeDB( sal_Int16 nVolumeDB
)
575 throw (uno::RuntimeException
)
577 ::osl::MutexGuard
aGuard(m_aMutex
);
579 mnUnmutedVolume
= pow( 10.0, nVolumeDB
/ 20.0 );
581 DBG( "set volume: %d gst volume: %lf", nVolumeDB
, mnUnmutedVolume
);
584 if( !mbMuted
&& mpPlaybin
)
586 g_object_set( G_OBJECT( mpPlaybin
), "volume", (gdouble
) mnUnmutedVolume
, NULL
);
590 // ------------------------------------------------------------------------------
592 sal_Int16 SAL_CALL
Player::getVolumeDB()
593 throw (uno::RuntimeException
)
595 ::osl::MutexGuard
aGuard(m_aMutex
);
597 sal_Int16
nVolumeDB(0);
600 double nGstVolume
= 0.0;
602 g_object_get( G_OBJECT( mpPlaybin
), "volume", &nGstVolume
, NULL
);
604 nVolumeDB
= (sal_Int16
) ( 20.0*log10 ( nGstVolume
) );
610 // ------------------------------------------------------------------------------
612 awt::Size SAL_CALL
Player::getPreferredPlayerWindowSize()
613 throw (uno::RuntimeException
)
615 ::osl::MutexGuard
aGuard(m_aMutex
);
617 awt::Size
aSize( 0, 0 );
619 if( maURL
.isEmpty() )
621 DBG( "%p Player::getPreferredPlayerWindowSize - empty URL => 0x0", this );
625 DBG( "%p pre-Player::getPreferredPlayerWindowSize, member %d x %d", this, mnWidth
, mnHeight
);
627 TimeValue aTimeout
= { 10, 0 };
628 #if OSL_DEBUG_LEVEL > 2
629 osl::Condition::Result aResult
=
631 maSizeCondition
.wait( &aTimeout
);
633 DBG( "%p Player::getPreferredPlayerWindowSize after waitCondition %d, member %d x %d", this, aResult
, mnWidth
, mnHeight
);
635 if( mnWidth
!= 0 && mnHeight
!= 0 ) {
636 aSize
.Width
= mnWidth
;
637 aSize
.Height
= mnHeight
;
643 // ------------------------------------------------------------------------------
645 uno::Reference
< ::media::XPlayerWindow
> SAL_CALL
Player::createPlayerWindow( const uno::Sequence
< uno::Any
>& rArguments
)
646 throw (uno::RuntimeException
)
648 ::osl::MutexGuard
aGuard(m_aMutex
);
650 uno::Reference
< ::media::XPlayerWindow
> xRet
;
651 awt::Size
aSize( getPreferredPlayerWindowSize() );
654 preparePlaybin( maURL
, NULL
);
656 DBG( "Player::createPlayerWindow %d %d length: %d", aSize
.Width
, aSize
.Height
, rArguments
.getLength() );
658 if( aSize
.Width
> 0 && aSize
.Height
> 0 )
660 ::avmedia::gstreamer::Window
* pWindow
= new ::avmedia::gstreamer::Window( mxMgr
, *this );
664 if( rArguments
.getLength() > 2 )
666 sal_IntPtr pIntPtr
= 0;
667 rArguments
[ 2 ] >>= pIntPtr
;
668 SystemChildWindow
*pParentWindow
= reinterpret_cast< SystemChildWindow
* >( pIntPtr
);
669 const SystemEnvData
* pEnvData
= pParentWindow
? pParentWindow
->GetSystemData() : NULL
;
670 OSL_ASSERT(pEnvData
);
673 mnWindowID
= pEnvData
->aWindow
;
674 DBG( "set window id to %d XOverlay %p\n", (int)mnWindowID
, mpXOverlay
);
675 gst_element_set_state( mpPlaybin
, GST_STATE_PAUSED
);
676 if ( mpXOverlay
!= NULL
)
677 gst_video_overlay_set_window_handle( mpXOverlay
, mnWindowID
);
685 // ------------------------------------------------------------------------------
687 uno::Reference
< media::XFrameGrabber
> SAL_CALL
Player::createFrameGrabber()
688 throw (uno::RuntimeException
)
690 ::osl::MutexGuard
aGuard(m_aMutex
);
691 FrameGrabber
* pFrameGrabber
= NULL
;
692 const awt::Size
aPrefSize( getPreferredPlayerWindowSize() );
694 if( ( aPrefSize
.Width
> 0 ) && ( aPrefSize
.Height
> 0 ) )
695 pFrameGrabber
= FrameGrabber::create( maURL
);
696 DBG( "created FrameGrabber %p", pFrameGrabber
);
698 return pFrameGrabber
;
701 // ------------------------------------------------------------------------------
703 OUString SAL_CALL
Player::getImplementationName()
704 throw (uno::RuntimeException
)
706 return OUString( AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME
);
709 // ------------------------------------------------------------------------------
711 sal_Bool SAL_CALL
Player::supportsService( const OUString
& ServiceName
)
712 throw (uno::RuntimeException
)
714 return ServiceName
== AVMEDIA_GST_PLAYER_SERVICENAME
;
717 // ------------------------------------------------------------------------------
719 uno::Sequence
< OUString
> SAL_CALL
Player::getSupportedServiceNames()
720 throw (uno::RuntimeException
)
722 uno::Sequence
< OUString
> aRet(1);
723 aRet
[0] = AVMEDIA_GST_PLAYER_SERVICENAME
;
728 } // namespace gstreamer
729 } // namespace avmedia
731 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */