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