1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 #include "gstplayer.hxx"
29 #include "gstwindow.hxx"
30 #include "gstframegrabber.hxx"
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <math.h>
34 #include <string>
35 #include <gst/gstelement.h>
36 #include <gst/interfaces/xoverlay.h>
37 
38 
39 // maximum timeout time in nanoseconds
40 #define GST_MAX_TIMEOUT (2500 * GST_MSECOND)
41 
42 using namespace ::com::sun::star;
43 
44 namespace avmedia
45 {
46 namespace gst
47 {
48 const double NANO_TIME_FACTOR = 1000000000.0;
49 
50 const long      VIDEO_DEFAULT_WIDTH = 256;
51 const long      VIDEO_DEFAULT_HEIGHT = 192;
52 
53 // ----------------
54 // - GstBusSource -
55 // ----------------
56 
57 struct GstBusSource : public GSource
58 {
59     GstBus* mpBus;
60 
61     GstBusSource() :
62         mpBus( NULL )
63     {}
64 
65     ~GstBusSource()
66     {}
67 };
68 
69 
70 // -----------------------------------------------------------------------
71 extern "C"
72 {
73 
74 static gpointer
75 lcl_implThreadFunc( gpointer pData )
76 {
77     return( pData ? static_cast< Player* >( pData )->run() : NULL );
78 }
79 
80 static gboolean
81 lcl_implBusCheck( GSource* pSource )
82 {
83     GstBusSource* pBusSource = static_cast< GstBusSource* >( pSource );
84 
85     return( pBusSource &&
86            GST_IS_BUS( pBusSource->mpBus ) &&
87            gst_bus_have_pending( GST_BUS_CAST( pBusSource->mpBus ) ) );
88 }
89 
90 
91 static gboolean
92 lcl_implBusPrepare( GSource* pSource, gint* pTimeout )
93 {
94     if (pTimeout)
95     {
96         *pTimeout = 0;
97     }
98 
99     return lcl_implBusCheck(pSource);
100 }
101 
102 static gboolean
103 lcl_implBusDispatch( GSource* pSource,
104                      GSourceFunc /*aCallback*/,
105                      gpointer pData )
106 {
107     GstBusSource* pBusSource = static_cast< GstBusSource* >( pSource );
108     gboolean bRet = false;
109 
110     if( pData && pBusSource && GST_IS_BUS( pBusSource->mpBus ) )
111     {
112         GstMessage* pMsg = gst_bus_pop( pBusSource->mpBus );
113 
114         if( pMsg )
115         {
116             bRet = static_cast< Player* >( pData )->busCallback(
117                         pBusSource->mpBus, pMsg );
118             gst_message_unref( pMsg );
119         }
120     }
121 
122     return( bRet );
123 }
124 
125 static void
126 lcl_implBusFinalize( GSource* pSource )
127 {
128     GstBusSource* pBusSource = static_cast< GstBusSource* >( pSource );
129 
130     if( pBusSource && pBusSource->mpBus )
131     {
132         gst_object_unref( pBusSource->mpBus );
133         pBusSource->mpBus = NULL;
134     }
135 }
136 
137 static gboolean
138 lcl_implIdleFunc( gpointer pData )
139 {
140     return( pData ? static_cast< Player* >( pData )->idle() : true );
141 }
142 
143 static GstBusSyncReply
144 lcl_implHandleCreateWindowFunc( GstBus* pBus, GstMessage* pMsg, gpointer pData )
145 {
146     return (pData)
147         ? static_cast< Player* >( pData )->handleCreateWindow( pBus, pMsg )
148         : GST_BUS_PASS;
149 }
150 
151 } // extern "C"
152 
153 
154 
155 // ---------------
156 // - Player -
157 // ---------------
158 Player::Player( GString* pURI ) :
159     Player_BASE(m_aMutex),
160     mpMutex( g_mutex_new() ),
161     mpCond( g_cond_new() ),
162     mpThread( NULL ),
163     mpContext( NULL ),
164     mpLoop( NULL ),
165     mpPlayer( NULL ),
166     mpURI( pURI ),
167     mpPlayerWindow( NULL ),
168     mnIsVideoSource( 0 ),
169     mnVideoWidth( 0 ),
170     mnVideoHeight( 0 ),
171     mnInitialized( 0 ),
172     mnVolumeDB( 0 ),
173     mnLooping( 0 ),
174     mnQuit( 0 ),
175     mnVideoWindowSet( 0 ),
176 	mnInitFail( 0 )
177 {
178     // initialize GStreamer framework only once
179     static bool bGstInitialized = false;
180 
181     if( !bGstInitialized )
182     {
183         gst_init( NULL, NULL );
184         bGstInitialized = true;
185     }
186 
187     if( pURI )
188     {
189         OSL_TRACE( ">>> --------------------------------" );
190         OSL_TRACE( ">>> Creating Player object with URL: %s", pURI->str );
191 
192         mpThread = g_thread_create( &lcl_implThreadFunc, this, true, NULL );
193     }
194 }
195 
196 // ------------------------------------------------------------------------------
197 
198 Player::~Player()
199 {
200     if( g_atomic_pointer_get( &mpPlayer ) )
201     {
202         implQuitThread();
203     }
204 
205     // cleanup
206     g_cond_free( mpCond );
207     g_mutex_free( mpMutex );
208     g_string_free( mpURI, false );
209 }
210 
211 // ------------------------------------------------------------------------------
212 Player* Player::create( const ::rtl::OUString& rURL )
213 {
214     Player* pPlayer = NULL;
215 
216     if( rURL.getLength() )
217     {
218         // safely initialize GLib threading framework
219         try
220         {
221             if( !g_thread_supported() )
222             {
223                 g_thread_init( NULL );
224             }
225         }
226         catch( ... )
227         {}
228 
229         if( g_thread_supported() )
230         {
231             const INetURLObject aURL( rURL );
232 
233             if( aURL.GetProtocol() != INET_PROT_NOT_VALID )
234             {
235                 GString* pURI = g_string_new( ::rtl::OUStringToOString(
236                                                  aURL.GetMainURL( INetURLObject::NO_DECODE ),
237                                                  RTL_TEXTENCODING_UTF8 ).getStr() );
238 
239                 if( pURI->len )
240                 {
241                     pPlayer = new Player( pURI );
242 
243                     // wait until thread signals that it has finished initialization
244                     if( pPlayer->mpThread )
245                     {
246                         g_mutex_lock( pPlayer->mpMutex );
247 
248                         while( !pPlayer->implIsInitialized() )
249                         {
250                             g_cond_wait( pPlayer->mpCond, pPlayer->mpMutex );
251                         }
252 
253                         g_mutex_unlock( pPlayer->mpMutex );
254                     }
255 
256                     // check if player pipeline could be initialized
257                     if( !pPlayer->mpPlayer )
258                     {
259                         delete pPlayer;
260                         pPlayer = NULL;
261                     }
262                 }
263                 else
264                 {
265                     g_string_free( pURI, false );
266                 }
267             }
268         }
269     }
270 
271     return( pPlayer );
272 }
273 
274 // ------------------------------------------------------------------------------
275 void SAL_CALL Player::start()
276      throw( uno::RuntimeException )
277 {
278     ::osl::MutexGuard aGuard(m_aMutex);
279     if( implInitPlayer() && !isPlaying() )
280     {
281         gst_element_set_state( mpPlayer, GST_STATE_PLAYING );
282     }
283 }
284 
285 // ------------------------------------------------------------------------------
286 void SAL_CALL Player::stop()
287      throw( uno::RuntimeException )
288 {
289     ::osl::MutexGuard aGuard(m_aMutex);
290     if( implInitPlayer() && isPlaying() )
291     {
292         gst_element_set_state( mpPlayer, GST_STATE_PAUSED );
293     }
294 }
295 
296 // ------------------------------------------------------------------------------
297 sal_Bool SAL_CALL Player::isPlaying()
298      throw( uno::RuntimeException )
299 {
300     GstState aState = GST_STATE_NULL;
301     ::osl::MutexGuard aGuard(m_aMutex);
302     if( mpPlayer )
303     {
304         gst_element_get_state( mpPlayer, &aState, NULL, GST_MAX_TIMEOUT );
305     }
306 
307     return( GST_STATE_PLAYING == aState );
308 }
309 
310 // ------------------------------------------------------------------------------
311 double SAL_CALL Player::getDuration()
312      throw( uno::RuntimeException )
313 {
314     ::osl::MutexGuard aGuard(m_aMutex);
315     gint64 nDuration = 0;
316 
317     if( implInitPlayer() )
318     {
319         GstFormat aFormat = GST_FORMAT_TIME;
320 
321         if( !gst_element_query_duration( mpPlayer, &aFormat, &nDuration ) ||
322            ( GST_FORMAT_TIME != aFormat ) ||
323            ( nDuration < 0 ) )
324         {
325             nDuration = 0;
326         }
327     }
328 
329     return( static_cast< double >( nDuration ) / NANO_TIME_FACTOR );
330 }
331 
332 // ------------------------------------------------------------------------------
333 void SAL_CALL Player::setMediaTime( double fTime )
334      throw( uno::RuntimeException )
335 {
336     ::osl::MutexGuard aGuard(m_aMutex);
337     if( implInitPlayer() )
338     {
339         fTime = ::std::min( ::std::max( fTime, 0.0 ), getDuration() );
340 
341         gst_element_seek_simple( mpPlayer, GST_FORMAT_TIME,
342                                 (GstSeekFlags) ( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT ),
343                                 static_cast< gint64 >( fTime * NANO_TIME_FACTOR ) );
344     }
345 }
346 
347 // ------------------------------------------------------------------------------
348 double SAL_CALL Player::getMediaTime()
349      throw( uno::RuntimeException )
350 {
351     double fRet = 0.0;
352     ::osl::MutexGuard aGuard(m_aMutex);
353     if( implInitPlayer() )
354     {
355         GstFormat aFormat = GST_FORMAT_TIME;
356         gint64 nCurTime = 0;
357 
358         if( gst_element_query_position( mpPlayer, &aFormat, &nCurTime ) &&
359            ( GST_FORMAT_TIME == aFormat ) &&
360            ( nCurTime >= 0 ) )
361         {
362             fRet = static_cast< double >( nCurTime ) / NANO_TIME_FACTOR;
363         }
364     }
365 
366     return( fRet );
367 }
368 
369 // ------------------------------------------------------------------------------
370 void SAL_CALL Player::setStopTime( double /* fTime */ )
371      throw( uno::RuntimeException )
372 {
373     OSL_TRACE( "GStreamer method avmedia::gst::Player::setStopTime needs to be implemented" );
374 
375     /*  Currently no need for implementation since higher levels of code don't use this method at all
376         !!! TODO: needs to be implemented if this functionality is needed at a later point of time
377     if( implInitPlayer() )
378     {
379     }
380 
381      */
382 }
383 
384 // ------------------------------------------------------------------------------
385 double SAL_CALL Player::getStopTime()
386      throw( uno::RuntimeException )
387 {
388     /*
389         Currently no need for implementation since higher levels of code don't set a stop time ATM
390         !!! TODO: needs to be fully implemented if this functionality is needed at a later point of time
391     */
392     ::osl::MutexGuard aGuard(m_aMutex);
393     return( getDuration() );
394 }
395 
396 // ------------------------------------------------------------------------------
397 void SAL_CALL Player::setRate( double /* fRate */ )
398      throw( uno::RuntimeException )
399 {
400     OSL_TRACE( "GStreamer method avmedia::gst::Player::setRate needs to be implemented" );
401 
402     /*  Currently no need for implementation since higher levels of code don't use this method at all
403         !!! TODO: needs to be implemented if this functionality is needed at a later point of time
404     */
405 }
406 
407 // ------------------------------------------------------------------------------
408 double SAL_CALL Player::getRate()
409      throw( uno::RuntimeException )
410 {
411     /*
412         Currently no need for implementation since higher levels of code don't set a different rate than 1 ATM
413         !!! TODO: needs to be fully implemented if this functionality is needed at a later point of time
414     */
415 
416     return( 1.0 );
417 }
418 
419 // ------------------------------------------------------------------------------
420 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
421      throw( uno::RuntimeException )
422 {
423     ::osl::MutexGuard aGuard(m_aMutex);
424     if (bSet)
425     {
426         g_atomic_int_compare_and_exchange(&mnLooping, 0, 1);
427     }
428     else
429     {
430         g_atomic_int_compare_and_exchange(&mnLooping, 1, 0);
431     }
432 }
433 
434 // ------------------------------------------------------------------------------
435 sal_Bool SAL_CALL Player::isPlaybackLoop()
436      throw( uno::RuntimeException )
437 {
438     ::osl::MutexGuard aGuard(m_aMutex);
439     return( g_atomic_int_get( &mnLooping ) > 0 );
440 }
441 
442 // ------------------------------------------------------------------------------
443 void SAL_CALL Player::setMute( sal_Bool bSet )
444      throw( uno::RuntimeException )
445 {
446     ::osl::MutexGuard aGuard(m_aMutex);
447     if( implInitPlayer() && ( bSet != isMute() ) )
448     {
449         if( bSet )
450         {
451             g_object_set( mpPlayer, "volume", 0.0, NULL );
452         }
453         else
454         {
455             setVolumeDB( mnVolumeDB );
456         }
457     }
458 }
459 
460 // ------------------------------------------------------------------------------
461 sal_Bool SAL_CALL Player::isMute()
462      throw( uno::RuntimeException )
463 {
464     gdouble fGstVolume = 1.0;
465     ::osl::MutexGuard aGuard(m_aMutex);
466 
467     if( implInitPlayer() )
468     {
469         g_object_get( mpPlayer, "volume", &fGstVolume, NULL );
470     }
471 
472     return( 0.0 == fGstVolume );
473 }
474 
475 // ------------------------------------------------------------------------------
476 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
477      throw( uno::RuntimeException )
478 {
479     ::osl::MutexGuard aGuard(m_aMutex);
480     if( implInitPlayer() )
481     {
482         g_mutex_lock( mpMutex );
483         mnVolumeDB = nVolumeDB;
484         g_mutex_unlock( mpMutex );
485 
486         // maximum gain for gstreamer volume is 10
487         double fGstVolume = pow( 10.0, static_cast< double >( ::std::min(
488                                                                   nVolumeDB, static_cast< sal_Int16 >( 20 ) ) / 20.0 ) );
489 
490         g_object_set( mpPlayer, "volume", fGstVolume, NULL );
491     }
492 }
493 
494 // ------------------------------------------------------------------------------
495 sal_Int16 SAL_CALL Player::getVolumeDB()
496      throw( uno::RuntimeException )
497 {
498     ::osl::MutexGuard aGuard(m_aMutex);
499     return( static_cast< sal_Int16 >( g_atomic_int_get( &mnVolumeDB ) ) );
500 }
501 
502 // ------------------------------------------------------------------------------
503 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
504      throw( uno::RuntimeException )
505 {
506     awt::Size aSize( 0, 0 );
507     ::osl::MutexGuard aGuard(m_aMutex);
508 
509     if( implInitPlayer() && ( g_atomic_int_get( &mnIsVideoSource ) > 0 ) )
510     {
511         aSize.Width = g_atomic_int_get( &mnVideoWidth );
512         aSize.Height = g_atomic_int_get( &mnVideoHeight );
513 
514         // if we have a video source, but no size is given => use default size
515         if( ( aSize.Width <= 0 ) || ( aSize.Height <= 0 ) )
516         {
517             aSize.Width = VIDEO_DEFAULT_WIDTH;
518             aSize.Height = VIDEO_DEFAULT_HEIGHT;
519         }
520     }
521 
522     OSL_TRACE( ">>> Requested preferred video size is: %d x %d pixel", aSize.Width, aSize.Height );
523 
524     return( aSize );
525 }
526 
527 // ------------------------------------------------------------------------------
528 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow(
529     const uno::Sequence< uno::Any >& rArguments )
530      throw( uno::RuntimeException )
531 {
532     ::osl::MutexGuard aGuard(m_aMutex);
533     uno::Reference< ::media::XPlayerWindow > xRet;
534     awt::Size aSize( getPreferredPlayerWindowSize() );
535 
536     OSL_ENSURE( !g_atomic_pointer_get( &mpPlayerWindow ), "::avmedia::gst::Player already has a player window" );
537 
538     if( ( aSize.Width > 0 ) && ( aSize.Height > 0 ) )
539     {
540         Window* pPlayerWindow = new Window( *this );
541 
542         xRet = pPlayerWindow;
543 
544         if( !pPlayerWindow->create( rArguments ) )
545         {
546             xRet.clear();
547         }
548         else
549         {
550             // try to use gconf user configurable video sink first
551             GstElement* pVideoSink = gst_element_factory_make( "gconfvideosink", NULL );
552 
553             if( ( NULL != pVideoSink ) ||
554                 ( NULL != ( pVideoSink = gst_element_factory_make( "autovideosink", NULL ) ) ) ||
555                 ( NULL != ( pVideoSink = gst_element_factory_make( "xvimagesink", NULL ) ) ) ||
556                 ( NULL != ( pVideoSink = gst_element_factory_make( "ximagesink", NULL ) ) ) )
557             {
558                 GstState aOldState = GST_STATE_NULL;
559 
560                 mpPlayerWindow = pPlayerWindow;
561                 gst_element_get_state( mpPlayer, &aOldState, NULL, GST_MAX_TIMEOUT );
562                 gst_element_set_state( mpPlayer, GST_STATE_READY );
563                 g_object_set( mpPlayer, "video-sink", pVideoSink, NULL );
564                 gst_element_set_state( mpPlayer, aOldState );
565             }
566         }
567     }
568 
569     return( xRet );
570 }
571 
572 // ------------------------------------------------------------------------------
573 uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
574      throw( ::com::sun::star::uno::RuntimeException )
575 {
576     ::osl::MutexGuard aGuard(m_aMutex);
577     FrameGrabber* pFrameGrabber = NULL;
578     const awt::Size aPrefSize( getPreferredPlayerWindowSize() );
579 
580     if( ( aPrefSize.Width > 0 ) && ( aPrefSize.Height > 0 ) )
581     {
582         pFrameGrabber = FrameGrabber::create( mpURI );
583     }
584 
585     return( pFrameGrabber );
586 }
587 
588 // ------------------------------------------------------------------------------
589 void SAL_CALL Player::disposing()
590 {
591     ::osl::MutexGuard aGuard(m_aMutex);
592     if( mpPlayer )
593     {
594         stop();
595         implQuitThread();
596     }
597 
598     OSL_ASSERT( NULL == mpPlayer );
599 }
600 
601 // ------------------------------------------------------------------------------
602 ::rtl::OUString SAL_CALL Player::getImplementationName()
603      throw( uno::RuntimeException )
604 {
605     return( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_GSTREAMER_PLAYER_IMPLEMENTATIONNAME ) ) );
606 }
607 
608 // ------------------------------------------------------------------------------
609 sal_Bool SAL_CALL Player::supportsService( const ::rtl::OUString& ServiceName )
610      throw( uno::RuntimeException )
611 {
612     return( ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( AVMEDIA_GSTREAMER_PLAYER_SERVICENAME ) ) );
613 }
614 
615 // ------------------------------------------------------------------------------
616 uno::Sequence< ::rtl::OUString > SAL_CALL Player::getSupportedServiceNames()
617      throw( uno::RuntimeException )
618 {
619     uno::Sequence< ::rtl::OUString > aRet( 1 );
620     aRet[ 0 ] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_GSTREAMER_PLAYER_SERVICENAME ) );
621 
622     return( aRet );
623 }
624 
625 // ------------------------------------------------------------------------------
626 void Player::implQuitThread()
627 {
628     if( mpThread )
629     {
630         // set quit flag to 1 so that the main loop will be quit in idle
631         // handler the next time it is called from the thread's main loop
632         g_atomic_int_inc( &mnQuit );
633 
634         // wait until loop and as such the thread has quit
635         g_thread_join( mpThread );
636         mpThread = NULL;
637     }
638 }
639 
640 // ------------------------------------------------------------------------------
641 bool Player::implInitPlayer()
642 {
643     bool bRet = false;
644 
645     if( mpPlayer && (mnInitFail < 3) )
646     {
647         GstState aState = GST_STATE_NULL;
648 
649         if( gst_element_get_state( mpPlayer, &aState, NULL, GST_MAX_TIMEOUT ) == GST_STATE_CHANGE_SUCCESS )
650         {
651             bRet = ( GST_STATE_PAUSED == aState ) || ( GST_STATE_PLAYING == aState );
652 
653             if( !bRet )
654             {
655                 gst_element_set_state( mpPlayer, GST_STATE_PAUSED );
656                 bRet = ( gst_element_get_state( mpPlayer, &aState, NULL,
657                                                 GST_MAX_TIMEOUT ) == GST_STATE_CHANGE_SUCCESS ) &&
658                     ( GST_STATE_PAUSED == aState );
659             }
660         }
661 
662 		if( ! bRet )
663 			mnInitFail++;
664     }
665 
666     return( bRet );
667 }
668 
669 // ------------------------------------------------------------------------------
670 gboolean Player::busCallback( GstBus* /*pBus*/,
671                               GstMessage* pMsg )
672 {
673     if( pMsg && mpLoop )
674     {
675         switch( GST_MESSAGE_TYPE( pMsg ) )
676         {
677             case ( GST_MESSAGE_EOS ):
678             {
679                 if( g_atomic_int_get( &mnLooping ) > 0 )
680                 {
681                     setMediaTime( 0.0 );
682                     start();
683                 }
684                 else
685                 {
686                     stop();
687                 }
688             }
689             break;
690 
691             case ( GST_MESSAGE_ERROR ):
692             {
693                 gchar* pDebug;
694                 GError* pErr;
695 
696                 gst_message_parse_error( pMsg, &pErr, &pDebug );
697                 fprintf( stderr, "Error: %s\n", pErr->message );
698 
699                 g_free( pDebug );
700                 g_error_free( pErr );
701             }
702             break;
703 
704             default:
705             {
706                 break;
707             }
708         }
709     }
710 
711     return( true );
712 }
713 
714 // ------------------------------------------------------------------------------
715 void Player::implHandleNewElementFunc( GstBin* /* pBin */,
716                                        GstElement* pElement,
717                                        gpointer pData )
718 {
719     if( pElement )
720     {
721 #ifdef DEBUG
722         gchar* pElementName = gst_element_get_name( pElement );
723 
724         if( pElementName )
725         {
726             OSL_TRACE( ">>> Bin has element: %s", pElementName );
727             g_free( pElementName );
728         }
729 #endif
730 
731         if( GST_IS_BIN( pElement ) )
732         {
733             // set this handler in case we have a GstBin element
734             g_signal_connect( GST_BIN( pElement ), "element-added",
735                               G_CALLBACK( Player::implHandleNewElementFunc ), pData );
736         }
737 
738         // watch for all pads that are going to be added to this element;
739         g_signal_connect( pElement, "pad-added",
740                           G_CALLBACK( Player::implHandleNewPadFunc ), pData );
741     }
742 }
743 
744 // ------------------------------------------------------------------------------
745 void Player::implHandleNewPadFunc( GstElement* pElement,
746                                    GstPad* pPad,
747                                    gpointer pData )
748 {
749     Player* pPlayer = static_cast< Player* >( pData );
750 
751     if( pPlayer && pElement && pPad )
752     {
753 #ifdef DEBUG
754         gchar* pElementName = gst_element_get_name( pElement );
755         gchar* pPadName = gst_pad_get_name( pPad );
756 
757         OSL_TRACE( ">>> Element %s has pad: %s", pElementName, pPadName );
758 
759         g_free( pPadName );
760         g_free( pElementName );
761 #endif
762 
763         GstCaps* pCaps = gst_pad_get_caps( pPad );
764 
765         // we are interested only in getting video properties
766         // width and height or if we have a video source at all
767         if( pCaps )
768         {
769             for( gint i = 0, nSize = gst_caps_get_size( pCaps ); i < nSize; ++i )
770             {
771                 const GstStructure* pStruct = gst_caps_get_structure( pCaps, i );
772 
773                 if( pStruct )
774                 {
775                     const gchar* pStructName = gst_structure_get_name( pStruct );
776 
777 #ifdef DEBUG
778                     OSL_TRACE( "\t>>> Pad has structure: %s", pStructName );
779 
780                     for( gint n = 0, nFields = gst_structure_n_fields( pStruct ); n < nFields; ++n )
781                     {
782                         OSL_TRACE( "\t\t>>> Structure has field: %s", gst_structure_nth_field_name( pStruct, n ) );
783                     }
784 #endif
785 
786                     // just look for structures having 'video' in their names
787                     if( ::std::string( pStructName ).find( "video" ) != ::std::string::npos )
788                     {
789                         g_atomic_int_inc( &pPlayer->mnIsVideoSource );
790 
791                         for( gint n = 0, nFields = gst_structure_n_fields( pStruct ); n < nFields; ++n )
792                         {
793                             const gchar* pFieldName = gst_structure_nth_field_name( pStruct, n );
794                             gint nValue;
795 
796                             if( ( ::std::string( pFieldName ).find( "width" ) != ::std::string::npos ) &&
797                                gst_structure_get_int( pStruct, pFieldName, &nValue ) )
798                             {
799                                 const gint nDiff = nValue - g_atomic_int_get( &pPlayer->mnVideoWidth );
800                                 g_atomic_int_add( &pPlayer->mnVideoWidth, ::std::max( nDiff, 0 ) );
801                             }
802                             else if( ( ::std::string( pFieldName ).find( "height" ) != ::std::string::npos ) &&
803                                     gst_structure_get_int( pStruct, pFieldName, &nValue ) )
804                             {
805                                 const gint nDiff = nValue - g_atomic_int_get( &pPlayer->mnVideoHeight );
806                                 g_atomic_int_add( &pPlayer->mnVideoHeight, ::std::max( nDiff, 0 ) );
807                             }
808                         }
809                     }
810                 }
811             }
812 
813             gst_caps_unref( pCaps );
814         }
815     }
816 }
817 
818 // ------------------------------------------------------------------------------
819 gboolean Player::idle()
820 {
821     // test if main loop should quit by comparing with 1
822     // and set flag mnQuit to 0 so we call g_main_loop_quit exactly once
823     bool const bQuit = g_atomic_int_compare_and_exchange( &mnQuit, 1, 0 );
824 
825     if( bQuit )
826     {
827         g_main_loop_quit( mpLoop );
828     }
829 
830     // don't eat up all cpu time
831     usleep( 1000 );
832 
833     return( true );
834 }
835 
836 // ------------------------------------------------------------------------------
837 gpointer Player::run()
838 {
839     static GSourceFuncs aSourceFuncs =
840     {
841         &lcl_implBusPrepare,
842         &lcl_implBusCheck,
843         &lcl_implBusDispatch,
844         &lcl_implBusFinalize,
845         NULL,
846         NULL
847     };
848 
849     if( NULL != ( mpPlayer = gst_element_factory_make( "playbin", NULL ) ) )
850     {
851         // initialization
852         // no mutex necessary since initialization
853         // is synchronous until loop is started
854         mpContext = g_main_context_new();
855         mpLoop = g_main_loop_new( mpContext, false );
856 
857         // add idle callback
858         GSource* pIdleSource = g_idle_source_new();
859         g_source_set_callback( pIdleSource, &lcl_implIdleFunc, this, NULL );
860         g_source_attach( pIdleSource, mpContext );
861 
862         // add bus callback
863         GSource* pBusSource = g_source_new( &aSourceFuncs, sizeof( GstBusSource ) );
864         static_cast< GstBusSource* >( pBusSource )->mpBus = gst_pipeline_get_bus( GST_PIPELINE( mpPlayer ) );
865         g_source_set_callback( pBusSource, NULL, this, NULL );
866         g_source_attach( pBusSource, mpContext );
867 
868         // add bus sync handler to intercept video window creation for setting our own window
869         gst_bus_set_sync_handler( static_cast< GstBusSource* >( pBusSource )->mpBus,
870                                   &lcl_implHandleCreateWindowFunc, this );
871 
872         // watch for all elements (and pads) that will be added to the playbin,
873         // in order to retrieve properties like video width and height
874         g_signal_connect( GST_BIN( mpPlayer ), "element-added",
875                           G_CALLBACK( Player::implHandleNewElementFunc ), this );
876 
877         // set source URI for player
878         g_object_set( mpPlayer, "uri", mpURI->str, NULL );
879 
880         // set video fake sink first, since we only create a player without window here
881         // and don't want to have the gstreamer default window appearing
882         g_object_set( mpPlayer, "video-sink", gst_element_factory_make( "fakesink", NULL ), NULL );
883 
884         // set state of player to READY or destroy object in case of FAILURE
885         if( gst_element_set_state( mpPlayer, GST_STATE_READY ) == GST_STATE_CHANGE_FAILURE )
886         {
887             gst_object_unref( mpPlayer );
888             mpPlayer = NULL;
889         }
890 
891         g_atomic_int_add( &mnInitialized, 1 );
892         g_cond_signal( mpCond );
893 
894         // run the main loop
895         g_main_loop_run( mpLoop );
896 
897         // clenanup
898         // no mutex necessary since other thread joined us (this thread)
899         // after setting the quit flag
900         if( mpPlayer )
901         {
902             gst_element_set_state( mpPlayer, GST_STATE_NULL );
903             gst_object_unref( mpPlayer );
904             mpPlayer = NULL;
905         }
906 
907         g_main_loop_unref( mpLoop );
908         mpLoop = NULL;
909 
910         g_source_destroy( pBusSource );
911         g_source_unref( pBusSource );
912 
913         g_source_destroy( pIdleSource );
914         g_source_unref( pIdleSource );
915 
916         g_main_context_unref( mpContext );
917         mpContext = NULL;
918     }
919     else
920     {
921         g_atomic_int_add( &mnInitialized, 1 );
922         g_cond_signal( mpCond );
923     }
924 
925     return( NULL );
926 }
927 
928 // ------------------------------------------------------------------------------
929 GstBusSyncReply Player::handleCreateWindow( GstBus* /* pBus */,
930                                             GstMessage* pMsg )
931 {
932     GstBusSyncReply eRet = GST_BUS_PASS;
933 
934     if( pMsg &&
935        ( GST_MESSAGE_TYPE( pMsg ) == GST_MESSAGE_ELEMENT ) &&
936        gst_structure_has_name( pMsg->structure, "prepare-xwindow-id" ) &&
937        g_atomic_pointer_get( &mpPlayerWindow ) )
938     {
939         OSL_TRACE( ">>> Got Request to create XOverlay" );
940 
941         gst_x_overlay_set_xwindow_id( GST_X_OVERLAY( GST_MESSAGE_SRC( pMsg ) ),
942                                      static_cast< Window* >( g_atomic_pointer_get(
943                                                                 &mpPlayerWindow ) )->getXWindowHandle() );
944 
945         gst_message_unref( pMsg );
946         eRet = GST_BUS_DROP;
947     }
948 
949     return( eRet );
950 }
951 }     // namespace gst
952 } // namespace avmedia
953