Version 4.0.0.1, tag libreoffice-4.0.0.1
[LibreOffice.git] / avmedia / source / gstreamer / gstplayer.cxx
blob6e002301194db39938869d3d95d5447f3c62889e
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 <math.h>
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"
34 #else
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"
38 #endif
40 #ifdef AVMEDIA_GST_0_10
41 # define AVVERSION "gst 0.10: "
42 #else
43 # define AVVERSION "gst 1.0: "
44 #endif
46 #if !defined DBG
47 # if OSL_DEBUG_LEVEL > 2
48 #define DBG(...) do { fprintf (stderr, "%s", AVVERSION); fprintf (stderr, __VA_ARGS__); fprintf (stderr, "\n"); } while (0);
49 # else
50 #define DBG(...)
51 # endif
52 #endif
54 using namespace ::com::sun::star;
56 namespace avmedia { namespace gstreamer {
58 // ----------------
59 // - Player -
60 // ----------------
62 Player::Player( const uno::Reference< lang::XMultiServiceFactory >& rxMgr ) :
63 GstPlayer_BASE( m_aMutex ),
64 mxMgr( rxMgr ),
65 mpPlaybin( NULL ),
66 mbFakeVideo (sal_False ),
67 mnUnmutedVolume( 0 ),
68 mbPlayPending ( false ),
69 mbMuted( false ),
70 mbLooping( false ),
71 mbInitialized( false ),
72 mnWindowID( 0 ),
73 mpXOverlay( NULL ),
74 mnDuration( 0 ),
75 mnWidth( 0 ),
76 mnHeight( 0 )
78 // Initialize GStreamer library
79 int argc = 1;
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 );
89 if (pError != NULL)
91 // TODO: thow an exception?
92 DBG( "%p Player::Player error '%s'", this, pError->message );
93 g_error_free (pError);
97 // ------------------------------------------------------------------------------
99 Player::~Player()
101 DBG( "%p Player::~Player", this );
102 if( mbInitialized )
103 disposing();
106 void SAL_CALL Player::disposing()
108 ::osl::MutexGuard aGuard(m_aMutex);
110 stop();
112 DBG( "%p Player::disposing", this );
114 // Release the elements and pipeline
115 if( mbInitialized )
117 if( mpPlaybin )
119 gst_element_set_state( mpPlaybin, GST_STATE_NULL );
120 g_object_unref( G_OBJECT( mpPlaybin ) );
122 mpPlaybin = NULL;
125 if( mpXOverlay ) {
126 g_object_unref( G_OBJECT ( mpXOverlay ) );
127 mpXOverlay = NULL;
132 // ------------------------------------------------------------------------------
134 static gboolean pipeline_bus_callback( GstBus *, GstMessage *message, gpointer data )
136 Player* pPlayer = static_cast<Player*>(data);
138 pPlayer->processMessage( message );
140 return TRUE;
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;
156 if (mbLooping)
157 start();
158 break;
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 &&
167 mpXOverlay )
168 gst_video_overlay_expose( mpXOverlay );
170 if (mbPlayPending)
171 mbPlayPending = ((newstate == GST_STATE_READY) || (newstate == GST_STATE_PAUSED));
173 default:
174 break;
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;
183 #else
184 return gst_element_query_position( element, format, cur );
185 #endif
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;
193 #else
194 return gst_element_query_duration( element, format, duration );
195 #endif
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 )
205 GError* error;
206 gchar* error_debug;
208 gst_message_parse_error( message, &error, &error_debug );
209 fprintf(stderr, "gstreamer error: '%s' debug: '%s'", error->message, error_debug);
211 #endif
213 #ifdef AVMEDIA_GST_0_10
214 if (message->structure &&
215 !strcmp( gst_structure_get_name( message->structure ), "prepare-xwindow-id" ) )
216 #else
217 if (gst_is_video_overlay_prepare_window_handle_message (message) )
218 #endif
220 DBG( "%p processSyncMessage prepare window id: %s %d", this,
221 GST_MESSAGE_TYPE_NAME( message ), (int)mnWindowID );
222 if( mpXOverlay )
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 );
229 return GST_BUS_DROP;
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;
252 if( mnWidth == 0 ) {
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 );
260 if( !pInfo )
261 continue;
263 int nType;
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,
266 nType );
268 if( !g_ascii_strcasecmp( pValue->value_nick, "video" ) ) {
269 GstStructure *pStructure;
270 GstPad *pPad;
272 g_object_get( pInfo, "object", &pPad, NULL );
273 pStructure = gst_caps_get_structure( GST_PAD_CAPS( pPad ), 0 );
274 if( pStructure ) {
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();
287 #else
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;
295 if( mnWidth == 0 ) {
296 GstPad *pad = NULL;
297 GstCaps *caps;
299 g_signal_emit_by_name( mpPlaybin, "get-video-pad", 0, &pad );
301 if( pad ) {
302 int w = 0, h = 0;
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,
309 NULL ) ) {
310 mnWidth = w;
311 mnHeight = h;
313 DBG( "queried size: %d x %d", mnWidth, mnHeight );
315 maSizeCondition.set();
317 gst_caps_unref( caps );
318 g_object_unref( pad );
321 #endif
322 } else if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR ) {
323 DBG( "Error !\n" );
324 if( mnWidth == 0 ) {
325 // an error occurred, set condition so that OOo thread doesn't wait for us
326 maSizeCondition.set();
330 return GST_BUS_PASS;
333 void Player::preparePlaybin( const OUString& rURL, GstElement *pSink )
335 GstBus *pBus;
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 );
347 mbFakeVideo = true;
349 else
350 mbFakeVideo = false;
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 );
360 #else
361 gst_bus_set_sync_handler( pBus, pipeline_bus_sync_handler, this, NULL );
362 #endif
363 g_object_unref( pBus );
366 bool Player::create( const OUString& rURL )
368 bool bRet = false;
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;
382 bRet = true;
385 if( bRet )
386 maURL = rURL;
387 else
388 maURL = OUString();
390 return bRet;
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
416 if( mpPlaybin )
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 );
440 return 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;
457 return duration;
460 // ------------------------------------------------------------------------------
462 void SAL_CALL Player::setMediaTime( double fTime )
463 throw (uno::RuntimeException)
465 ::osl::MutexGuard aGuard(m_aMutex);
467 if( mpPlaybin ) {
468 gint64 gst_position = llround (fTime * 1E9);
470 gst_element_seek( mpPlaybin, 1.0,
471 GST_FORMAT_TIME,
472 GST_SEEK_FLAG_FLUSH,
473 GST_SEEK_TYPE_SET, gst_position,
474 GST_SEEK_TYPE_NONE, 0 );
475 if( !isPlaying() )
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;
491 if( mpPlaybin ) {
492 // get current position in the stream
493 gint64 gst_position;
494 if( wrap_element_query_position( mpPlaybin, GST_FORMAT_TIME, &gst_position ) )
495 position = gst_position / 1E9;
498 return position;
501 // ------------------------------------------------------------------------------
503 double SAL_CALL Player::getRate()
504 throw (uno::RuntimeException)
506 ::osl::MutexGuard aGuard(m_aMutex);
508 double rate = 1.0;
510 // TODO get the window rate - but no need since
511 // higher levels never set rate > 1
513 return rate;
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
523 mbLooping = bSet;
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
533 return mbLooping;
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;
549 if( bSet )
551 nVolume = 0.0;
554 g_object_set( G_OBJECT( mpPlaybin ), "volume", nVolume, NULL );
556 mbMuted = bSet;
560 // ------------------------------------------------------------------------------
562 sal_Bool SAL_CALL Player::isMute()
563 throw (uno::RuntimeException)
565 ::osl::MutexGuard aGuard(m_aMutex);
567 return mbMuted;
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 );
581 // change volume
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);
597 if( mpPlaybin ) {
598 double nGstVolume = 0.0;
600 g_object_get( G_OBJECT( mpPlaybin ), "volume", &nGstVolume, NULL );
602 nVolumeDB = (sal_Int16) ( 20.0*log10 ( nGstVolume ) );
605 return nVolumeDB;
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 );
620 return aSize;
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 =
628 #endif
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;
638 return aSize;
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() );
651 if( mbFakeVideo )
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 );
660 xRet = pWindow;
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);
669 if (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 );
680 return xRet;
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 ;
723 return aRet;
726 } // namespace gstreamer
727 } // namespace avmedia
729 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */