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      throw( uno::RuntimeException )
273 {
274     ::osl::MutexGuard aGuard(m_aMutex);
275     if( implInitPlayer() && !isPlaying() )
276     {
277         gst_element_set_state( mpPlayer, GST_STATE_PLAYING );
278     }
279 }
280 
281 // ------------------------------------------------------------------------------
stop()282 void SAL_CALL Player::stop()
283      throw( uno::RuntimeException )
284 {
285     ::osl::MutexGuard aGuard(m_aMutex);
286     if( implInitPlayer() && isPlaying() )
287     {
288         gst_element_set_state( mpPlayer, GST_STATE_PAUSED );
289     }
290 }
291 
292 // ------------------------------------------------------------------------------
isPlaying()293 sal_Bool SAL_CALL Player::isPlaying()
294      throw( uno::RuntimeException )
295 {
296     GstState aState = GST_STATE_NULL;
297     ::osl::MutexGuard aGuard(m_aMutex);
298     if( mpPlayer )
299     {
300         gst_element_get_state( mpPlayer, &aState, NULL, GST_MAX_TIMEOUT );
301     }
302 
303     return( GST_STATE_PLAYING == aState );
304 }
305 
306 // ------------------------------------------------------------------------------
getDuration()307 double SAL_CALL Player::getDuration()
308      throw( uno::RuntimeException )
309 {
310     ::osl::MutexGuard aGuard(m_aMutex);
311     gint64 nDuration = 0;
312 
313     if( implInitPlayer() )
314     {
315         if( !gst_element_query_duration( mpPlayer, GST_FORMAT_TIME, &nDuration ) ||
316            ( nDuration < 0 ) )
317         {
318             nDuration = 0;
319         }
320     }
321 
322     return( static_cast< double >( nDuration ) / NANO_TIME_FACTOR );
323 }
324 
325 // ------------------------------------------------------------------------------
setMediaTime(double fTime)326 void SAL_CALL Player::setMediaTime( double fTime )
327      throw( uno::RuntimeException )
328 {
329     ::osl::MutexGuard aGuard(m_aMutex);
330     if( implInitPlayer() )
331     {
332         fTime = ::std::min( ::std::max( fTime, 0.0 ), getDuration() );
333 
334         gst_element_seek_simple( mpPlayer, GST_FORMAT_TIME,
335                                 (GstSeekFlags) ( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT ),
336                                 static_cast< gint64 >( fTime * NANO_TIME_FACTOR ) );
337     }
338 }
339 
340 // ------------------------------------------------------------------------------
getMediaTime()341 double SAL_CALL Player::getMediaTime()
342      throw( uno::RuntimeException )
343 {
344     double fRet = 0.0;
345     ::osl::MutexGuard aGuard(m_aMutex);
346     if( implInitPlayer() )
347     {
348         gint64 nCurTime = 0;
349 
350         if( gst_element_query_position( mpPlayer, GST_FORMAT_TIME, &nCurTime ) &&
351            ( nCurTime >= 0 ) )
352         {
353             fRet = static_cast< double >( nCurTime ) / NANO_TIME_FACTOR;
354         }
355     }
356 
357     return( fRet );
358 }
359 
360 // ------------------------------------------------------------------------------
setStopTime(double)361 void SAL_CALL Player::setStopTime( double /* fTime */ )
362      throw( uno::RuntimeException )
363 {
364     OSL_TRACE( "GStreamer method avmedia::gst::Player::setStopTime needs to be implemented" );
365 
366     /*  Currently no need for implementation since higher levels of code don't use this method at all
367         !!! TODO: needs to be implemented if this functionality is needed at a later point of time
368     if( implInitPlayer() )
369     {
370     }
371 
372      */
373 }
374 
375 // ------------------------------------------------------------------------------
getStopTime()376 double SAL_CALL Player::getStopTime()
377      throw( uno::RuntimeException )
378 {
379     /*
380         Currently no need for implementation since higher levels of code don't set a stop time ATM
381         !!! TODO: needs to be fully implemented if this functionality is needed at a later point of time
382     */
383     ::osl::MutexGuard aGuard(m_aMutex);
384     return( getDuration() );
385 }
386 
387 // ------------------------------------------------------------------------------
setRate(double)388 void SAL_CALL Player::setRate( double /* fRate */ )
389      throw( uno::RuntimeException )
390 {
391     OSL_TRACE( "GStreamer method avmedia::gst::Player::setRate needs to be implemented" );
392 
393     /*  Currently no need for implementation since higher levels of code don't use this method at all
394         !!! TODO: needs to be implemented if this functionality is needed at a later point of time
395     */
396 }
397 
398 // ------------------------------------------------------------------------------
getRate()399 double SAL_CALL Player::getRate()
400      throw( uno::RuntimeException )
401 {
402     /*
403         Currently no need for implementation since higher levels of code don't set a different rate than 1 ATM
404         !!! TODO: needs to be fully implemented if this functionality is needed at a later point of time
405     */
406 
407     return( 1.0 );
408 }
409 
410 // ------------------------------------------------------------------------------
setPlaybackLoop(sal_Bool bSet)411 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
412      throw( uno::RuntimeException )
413 {
414     ::osl::MutexGuard aGuard(m_aMutex);
415     if (bSet)
416     {
417         g_atomic_int_compare_and_exchange(&mnLooping, 0, 1);
418     }
419     else
420     {
421         g_atomic_int_compare_and_exchange(&mnLooping, 1, 0);
422     }
423 }
424 
425 // ------------------------------------------------------------------------------
isPlaybackLoop()426 sal_Bool SAL_CALL Player::isPlaybackLoop()
427      throw( uno::RuntimeException )
428 {
429     ::osl::MutexGuard aGuard(m_aMutex);
430     return( g_atomic_int_get( &mnLooping ) > 0 );
431 }
432 
433 // ------------------------------------------------------------------------------
setMute(sal_Bool bSet)434 void SAL_CALL Player::setMute( sal_Bool bSet )
435      throw( uno::RuntimeException )
436 {
437     ::osl::MutexGuard aGuard(m_aMutex);
438     if( implInitPlayer() && ( bSet != isMute() ) )
439     {
440         if( bSet )
441         {
442             g_object_set( mpPlayer, "volume", 0.0, NULL );
443         }
444         else
445         {
446             setVolumeDB( mnVolumeDB );
447         }
448     }
449 }
450 
451 // ------------------------------------------------------------------------------
isMute()452 sal_Bool SAL_CALL Player::isMute()
453      throw( uno::RuntimeException )
454 {
455     gdouble fGstVolume = 1.0;
456     ::osl::MutexGuard aGuard(m_aMutex);
457 
458     if( implInitPlayer() )
459     {
460         g_object_get( mpPlayer, "volume", &fGstVolume, NULL );
461     }
462 
463     return( 0.0 == fGstVolume );
464 }
465 
466 // ------------------------------------------------------------------------------
setVolumeDB(sal_Int16 nVolumeDB)467 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
468      throw( uno::RuntimeException )
469 {
470     ::osl::MutexGuard aGuard(m_aMutex);
471     if( implInitPlayer() )
472     {
473         g_mutex_lock( mpMutex );
474         mnVolumeDB = nVolumeDB;
475         g_mutex_unlock( mpMutex );
476 
477         // maximum gain for gstreamer volume is 10
478         double fGstVolume = pow( 10.0, static_cast< double >( ::std::min(
479                                                                   nVolumeDB, static_cast< sal_Int16 >( 20 ) ) / 20.0 ) );
480 
481         g_object_set( mpPlayer, "volume", fGstVolume, NULL );
482     }
483 }
484 
485 // ------------------------------------------------------------------------------
getVolumeDB()486 sal_Int16 SAL_CALL Player::getVolumeDB()
487      throw( uno::RuntimeException )
488 {
489     ::osl::MutexGuard aGuard(m_aMutex);
490     return( static_cast< sal_Int16 >( g_atomic_int_get( &mnVolumeDB ) ) );
491 }
492 
493 // ------------------------------------------------------------------------------
getPreferredPlayerWindowSize()494 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
495      throw( uno::RuntimeException )
496 {
497     awt::Size aSize( 0, 0 );
498     ::osl::MutexGuard aGuard(m_aMutex);
499 
500     if( implInitPlayer() && ( g_atomic_int_get( &mnIsVideoSource ) > 0 ) )
501     {
502         aSize.Width = g_atomic_int_get( &mnVideoWidth );
503         aSize.Height = g_atomic_int_get( &mnVideoHeight );
504 
505         // if we have a video source, but no size is given => use default size
506         if( ( aSize.Width <= 0 ) || ( aSize.Height <= 0 ) )
507         {
508             aSize.Width = VIDEO_DEFAULT_WIDTH;
509             aSize.Height = VIDEO_DEFAULT_HEIGHT;
510         }
511     }
512 
513     OSL_TRACE( ">>> Requested preferred video size is: %d x %d pixel", aSize.Width, aSize.Height );
514 
515     return( aSize );
516 }
517 
518 // ------------------------------------------------------------------------------
createPlayerWindow(const uno::Sequence<uno::Any> & rArguments)519 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow(
520     const uno::Sequence< uno::Any >& rArguments )
521      throw( uno::RuntimeException )
522 {
523     ::osl::MutexGuard aGuard(m_aMutex);
524     uno::Reference< ::media::XPlayerWindow > xRet;
525     awt::Size aSize( getPreferredPlayerWindowSize() );
526 
527     OSL_ENSURE( !g_atomic_pointer_get( &mpPlayerWindow ), "::avmedia::gst::Player already has a player window" );
528 
529     if( ( aSize.Width > 0 ) && ( aSize.Height > 0 ) )
530     {
531         Window* pPlayerWindow = new Window( *this );
532 
533         xRet = pPlayerWindow;
534 
535         if( !pPlayerWindow->create( rArguments ) )
536         {
537             OSL_ENSURE( false, "could not create player window\n" );
538             xRet.clear();
539         }
540         else
541         {
542             // try to use gconf user configurable video sink first
543             GstElement* pVideoSink = gst_element_factory_make( "gconfvideosink", NULL );
544 
545             if( ( NULL != pVideoSink ) ||
546                 ( NULL != ( pVideoSink = gst_element_factory_make( "autovideosink", NULL ) ) ) ||
547                 ( NULL != ( pVideoSink = gst_element_factory_make( "xvimagesink", NULL ) ) ) ||
548                 ( NULL != ( pVideoSink = gst_element_factory_make( "ximagesink", NULL ) ) ) )
549             {
550                 GstState aOldState = GST_STATE_NULL;
551 
552                 mpPlayerWindow = pPlayerWindow;
553                 gst_element_get_state( mpPlayer, &aOldState, NULL, GST_MAX_TIMEOUT );
554                 gst_element_set_state( mpPlayer, GST_STATE_READY );
555                 g_object_set( mpPlayer, "video-sink", pVideoSink, NULL );
556                 gst_video_overlay_set_window_handle( GST_VIDEO_OVERLAY( mpPlayer ),
557                     pPlayerWindow->getXWindowHandle() );
558                 gst_element_set_state( mpPlayer, aOldState );
559             }
560             else
561                 OSL_ENSURE( false, "no video sink available\n" );
562         }
563     }
564 
565     return( xRet );
566 }
567 
568 // ------------------------------------------------------------------------------
createFrameGrabber()569 uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber()
570      throw( ::com::sun::star::uno::RuntimeException )
571 {
572     ::osl::MutexGuard aGuard(m_aMutex);
573     FrameGrabber* pFrameGrabber = NULL;
574     const awt::Size aPrefSize( getPreferredPlayerWindowSize() );
575 
576     if( ( aPrefSize.Width > 0 ) && ( aPrefSize.Height > 0 ) )
577     {
578         pFrameGrabber = FrameGrabber::create( mpURI );
579     }
580 
581     return( pFrameGrabber );
582 }
583 
584 // ------------------------------------------------------------------------------
disposing()585 void SAL_CALL Player::disposing()
586 {
587     ::osl::MutexGuard aGuard(m_aMutex);
588     if( mpPlayer )
589     {
590         stop();
591         implQuitThread();
592     }
593 
594     OSL_ASSERT( NULL == mpPlayer );
595 }
596 
597 // ------------------------------------------------------------------------------
getImplementationName()598 ::rtl::OUString SAL_CALL Player::getImplementationName()
599      throw( uno::RuntimeException )
600 {
601     return( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_GSTREAMER_PLAYER_IMPLEMENTATIONNAME ) ) );
602 }
603 
604 // ------------------------------------------------------------------------------
supportsService(const::rtl::OUString & ServiceName)605 sal_Bool SAL_CALL Player::supportsService( const ::rtl::OUString& ServiceName )
606      throw( uno::RuntimeException )
607 {
608     return( ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( AVMEDIA_GSTREAMER_PLAYER_SERVICENAME ) ) );
609 }
610 
611 // ------------------------------------------------------------------------------
getSupportedServiceNames()612 uno::Sequence< ::rtl::OUString > SAL_CALL Player::getSupportedServiceNames()
613      throw( uno::RuntimeException )
614 {
615     uno::Sequence< ::rtl::OUString > aRet( 1 );
616     aRet[ 0 ] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_GSTREAMER_PLAYER_SERVICENAME ) );
617 
618     return( aRet );
619 }
620 
621 // ------------------------------------------------------------------------------
implQuitThread()622 void Player::implQuitThread()
623 {
624     if( mpThread )
625     {
626         // set quit flag to 1 so that the main loop will be quit in idle
627         // handler the next time it is called from the thread's main loop
628         g_atomic_int_inc( &mnQuit );
629 
630         // wait until loop and as such the thread has quit
631         g_thread_join( mpThread );
632         mpThread = NULL;
633     }
634 }
635 
636 // ------------------------------------------------------------------------------
implInitPlayer()637 bool Player::implInitPlayer()
638 {
639     bool bRet = false;
640 
641     if( mpPlayer && (mnInitFail < 3) )
642     {
643         GstState aState = GST_STATE_NULL;
644 
645         if( gst_element_get_state( mpPlayer, &aState, NULL, GST_MAX_TIMEOUT ) == GST_STATE_CHANGE_SUCCESS )
646         {
647             bRet = ( GST_STATE_PAUSED == aState ) || ( GST_STATE_PLAYING == aState );
648 
649             if( !bRet )
650             {
651                 gst_element_set_state( mpPlayer, GST_STATE_PAUSED );
652                 bRet = ( gst_element_get_state( mpPlayer, &aState, NULL,
653                                                 GST_MAX_TIMEOUT ) == GST_STATE_CHANGE_SUCCESS ) &&
654                     ( GST_STATE_PAUSED == aState );
655             }
656         }
657 
658 		if( ! bRet )
659 			mnInitFail++;
660     }
661 
662     return( bRet );
663 }
664 
665 // ------------------------------------------------------------------------------
busCallback(GstBus *,GstMessage * pMsg)666 gboolean Player::busCallback( GstBus* /*pBus*/,
667                               GstMessage* pMsg )
668 {
669     if( pMsg && mpLoop )
670     {
671         switch( GST_MESSAGE_TYPE( pMsg ) )
672         {
673             case ( GST_MESSAGE_EOS ):
674             {
675                 if( g_atomic_int_get( &mnLooping ) > 0 )
676                 {
677                     setMediaTime( 0.0 );
678                     start();
679                 }
680                 else
681                 {
682                     stop();
683                 }
684             }
685             break;
686 
687             case ( GST_MESSAGE_ERROR ):
688             {
689                 gchar* pDebug;
690                 GError* pErr;
691 
692                 gst_message_parse_error( pMsg, &pErr, &pDebug );
693                 fprintf( stderr, "Error: %s\n", pErr->message );
694 
695                 g_free( pDebug );
696                 g_error_free( pErr );
697             }
698             break;
699 
700             default:
701             {
702                 break;
703             }
704         }
705     }
706 
707     return( true );
708 }
709 
710 // ------------------------------------------------------------------------------
implHandleNewElementFunc(GstBin *,GstElement * pElement,gpointer pData)711 void Player::implHandleNewElementFunc( GstBin* /* pBin */,
712                                        GstElement* pElement,
713                                        gpointer pData )
714 {
715     if( pElement )
716     {
717 #ifdef DEBUG
718         gchar* pElementName = gst_element_get_name( pElement );
719 
720         if( pElementName )
721         {
722             OSL_TRACE( ">>> Bin has element: %s", pElementName );
723             g_free( pElementName );
724         }
725 #endif
726 
727         if( GST_IS_BIN( pElement ) )
728         {
729             // set this handler in case we have a GstBin element
730             g_signal_connect( GST_BIN( pElement ), "element-added",
731                               G_CALLBACK( Player::implHandleNewElementFunc ), pData );
732         }
733 
734         // watch for all pads that are going to be added to this element;
735         g_signal_connect( pElement, "pad-added",
736                           G_CALLBACK( Player::implHandleNewPadFunc ), pData );
737     }
738 }
739 
740 // ------------------------------------------------------------------------------
implHandleNewPadFunc(GstElement * pElement,GstPad * pPad,gpointer pData)741 void Player::implHandleNewPadFunc( GstElement* pElement,
742                                    GstPad* pPad,
743                                    gpointer pData )
744 {
745     Player* pPlayer = static_cast< Player* >( pData );
746 
747     if( pPlayer && pElement && pPad )
748     {
749 #ifdef DEBUG
750         gchar* pElementName = gst_element_get_name( pElement );
751         gchar* pPadName = gst_pad_get_name( pPad );
752 
753         OSL_TRACE( ">>> Element %s has pad: %s", pElementName, pPadName );
754 
755         g_free( pPadName );
756         g_free( pElementName );
757 #endif
758 
759         GstCaps* pCaps = gst_pad_get_current_caps( pPad );
760 
761         // we are interested only in getting video properties
762         // width and height or if we have a video source at all
763         if( pCaps )
764         {
765             for( gint i = 0, nSize = gst_caps_get_size( pCaps ); i < nSize; ++i )
766             {
767                 const GstStructure* pStruct = gst_caps_get_structure( pCaps, i );
768 
769                 if( pStruct )
770                 {
771                     const gchar* pStructName = gst_structure_get_name( pStruct );
772 
773 #ifdef DEBUG
774                     OSL_TRACE( "\t>>> Pad has structure: %s", pStructName );
775 
776                     for( gint n = 0, nFields = gst_structure_n_fields( pStruct ); n < nFields; ++n )
777                     {
778                         OSL_TRACE( "\t\t>>> Structure has field: %s", gst_structure_nth_field_name( pStruct, n ) );
779                     }
780 #endif
781 
782                     // just look for structures having 'video' in their names
783                     if( ::std::string( pStructName ).find( "video" ) != ::std::string::npos )
784                     {
785                         g_atomic_int_inc( &pPlayer->mnIsVideoSource );
786 
787                         for( gint n = 0, nFields = gst_structure_n_fields( pStruct ); n < nFields; ++n )
788                         {
789                             const gchar* pFieldName = gst_structure_nth_field_name( pStruct, n );
790                             gint nValue;
791 
792                             if( ( ::std::string( pFieldName ).find( "width" ) != ::std::string::npos ) &&
793                                gst_structure_get_int( pStruct, pFieldName, &nValue ) )
794                             {
795                                 const gint nDiff = nValue - g_atomic_int_get( &pPlayer->mnVideoWidth );
796                                 g_atomic_int_add( &pPlayer->mnVideoWidth, ::std::max( nDiff, 0 ) );
797                             }
798                             else if( ( ::std::string( pFieldName ).find( "height" ) != ::std::string::npos ) &&
799                                     gst_structure_get_int( pStruct, pFieldName, &nValue ) )
800                             {
801                                 const gint nDiff = nValue - g_atomic_int_get( &pPlayer->mnVideoHeight );
802                                 g_atomic_int_add( &pPlayer->mnVideoHeight, ::std::max( nDiff, 0 ) );
803                             }
804                         }
805                     }
806                 }
807             }
808 
809             gst_caps_unref( pCaps );
810         }
811     }
812 }
813 
814 // ------------------------------------------------------------------------------
idle()815 gboolean Player::idle()
816 {
817     // test if main loop should quit by comparing with 1
818     // and set flag mnQuit to 0 so we call g_main_loop_quit exactly once
819     bool const bQuit = g_atomic_int_compare_and_exchange( &mnQuit, 1, 0 );
820 
821     if( bQuit )
822     {
823         g_main_loop_quit( mpLoop );
824     }
825 
826     // don't eat up all cpu time
827     usleep( 1000 );
828 
829     return( true );
830 }
831 
832 // ------------------------------------------------------------------------------
run()833 gpointer Player::run()
834 {
835     static GSourceFuncs aSourceFuncs =
836     {
837         &lcl_implBusPrepare,
838         &lcl_implBusCheck,
839         &lcl_implBusDispatch,
840         &lcl_implBusFinalize,
841         NULL,
842         NULL
843     };
844 
845     if( NULL != ( mpPlayer = gst_element_factory_make( "playbin", NULL ) ) )
846     {
847         // initialization
848         // no mutex necessary since initialization
849         // is synchronous until loop is started
850         mpContext = g_main_context_new();
851         mpLoop = g_main_loop_new( mpContext, false );
852 
853         // add idle callback
854         GSource* pIdleSource = g_idle_source_new();
855         g_source_set_callback( pIdleSource, &lcl_implIdleFunc, this, NULL );
856         g_source_attach( pIdleSource, mpContext );
857 
858         // add bus callback
859         GSource* pBusSource = g_source_new( &aSourceFuncs, sizeof( GstBusSource ) );
860         static_cast< GstBusSource* >( pBusSource )->mpBus = gst_pipeline_get_bus( GST_PIPELINE( mpPlayer ) );
861         g_source_set_callback( pBusSource, NULL, this, NULL );
862         g_source_attach( pBusSource, mpContext );
863 
864         // add bus sync handler to intercept video window creation for setting our own window
865         gst_bus_set_sync_handler( static_cast< GstBusSource* >( pBusSource )->mpBus,
866                                   &lcl_implHandleCreateWindowFunc, this, NULL );
867 
868         // watch for all elements (and pads) that will be added to the playbin,
869         // in order to retrieve properties like video width and height
870         g_signal_connect( GST_BIN( mpPlayer ), "element-added",
871                           G_CALLBACK( Player::implHandleNewElementFunc ), this );
872 
873         // set source URI for player
874         g_object_set( mpPlayer, "uri", mpURI->str, NULL );
875 
876         // set video fake sink first, since we only create a player without window here
877         // and don't want to have the gstreamer default window appearing
878         g_object_set( mpPlayer, "video-sink", gst_element_factory_make( "fakesink", NULL ), NULL );
879 
880         // This isn't ever going to happen, as createPlayerWindow() has to be called first
881         // to set the mpPlayerWindow, but let's keep it here in case it is called first some day:
882         if ( g_atomic_pointer_get( &mpPlayerWindow ) )
883         {
884             gst_video_overlay_set_window_handle( GST_VIDEO_OVERLAY( mpPlayer ), static_cast< Window* >( g_atomic_pointer_get(
885                                                                  &mpPlayerWindow ) )->getXWindowHandle() );
886         }
887 
888         // set state of player to READY or destroy object in case of FAILURE
889         if( gst_element_set_state( mpPlayer, GST_STATE_READY ) == GST_STATE_CHANGE_FAILURE )
890         {
891             gst_object_unref( mpPlayer );
892             mpPlayer = NULL;
893         }
894 
895         g_atomic_int_add( &mnInitialized, 1 );
896         g_cond_signal( mpCond );
897 
898         // run the main loop
899         g_main_loop_run( mpLoop );
900 
901         // clenanup
902         // no mutex necessary since other thread joined us (this thread)
903         // after setting the quit flag
904         if( mpPlayer )
905         {
906             gst_element_set_state( mpPlayer, GST_STATE_NULL );
907             gst_object_unref( mpPlayer );
908             mpPlayer = NULL;
909         }
910 
911         g_main_loop_unref( mpLoop );
912         mpLoop = NULL;
913 
914         g_source_destroy( pBusSource );
915         g_source_unref( pBusSource );
916 
917         g_source_destroy( pIdleSource );
918         g_source_unref( pIdleSource );
919 
920         g_main_context_unref( mpContext );
921         mpContext = NULL;
922     }
923     else
924     {
925         g_atomic_int_add( &mnInitialized, 1 );
926         g_cond_signal( mpCond );
927     }
928 
929     return( NULL );
930 }
931 
932 // ------------------------------------------------------------------------------
handleCreateWindow(GstBus *,GstMessage * pMsg)933 GstBusSyncReply Player::handleCreateWindow( GstBus* /* pBus */,
934                                             GstMessage* pMsg )
935 {
936     GstBusSyncReply eRet = GST_BUS_PASS;
937 
938     if( pMsg &&
939        gst_is_video_overlay_prepare_window_handle_message( pMsg ) &&
940        g_atomic_pointer_get( &mpPlayerWindow ) )
941     {
942         OSL_TRACE( ">>> Got Request to create XOverlay" );
943 
944         gst_video_overlay_set_window_handle( GST_VIDEO_OVERLAY( GST_MESSAGE_SRC( pMsg ) ),
945                                      static_cast< Window* >( g_atomic_pointer_get(
946                                                                 &mpPlayerWindow ) )->getXWindowHandle() );
947 
948         gst_message_unref( pMsg );
949         eRet = GST_BUS_DROP;
950     }
951 
952     return( eRet );
953 }
954 }     // namespace gst
955 } // namespace avmedia
956