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/interfaces/xoverlay.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 GstFormat aFormat = GST_FORMAT_TIME;
316
317 if( !gst_element_query_duration( mpPlayer, &aFormat, &nDuration ) ||
318 ( GST_FORMAT_TIME != aFormat ) ||
319 ( nDuration < 0 ) )
320 {
321 nDuration = 0;
322 }
323 }
324
325 return( static_cast< double >( nDuration ) / NANO_TIME_FACTOR );
326 }
327
328 // ------------------------------------------------------------------------------
setMediaTime(double fTime)329 void SAL_CALL Player::setMediaTime( double fTime )
330 throw( uno::RuntimeException )
331 {
332 ::osl::MutexGuard aGuard(m_aMutex);
333 if( implInitPlayer() )
334 {
335 fTime = ::std::min( ::std::max( fTime, 0.0 ), getDuration() );
336
337 gst_element_seek_simple( mpPlayer, GST_FORMAT_TIME,
338 (GstSeekFlags) ( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT ),
339 static_cast< gint64 >( fTime * NANO_TIME_FACTOR ) );
340 }
341 }
342
343 // ------------------------------------------------------------------------------
getMediaTime()344 double SAL_CALL Player::getMediaTime()
345 throw( uno::RuntimeException )
346 {
347 double fRet = 0.0;
348 ::osl::MutexGuard aGuard(m_aMutex);
349 if( implInitPlayer() )
350 {
351 GstFormat aFormat = GST_FORMAT_TIME;
352 gint64 nCurTime = 0;
353
354 if( gst_element_query_position( mpPlayer, &aFormat, &nCurTime ) &&
355 ( GST_FORMAT_TIME == aFormat ) &&
356 ( nCurTime >= 0 ) )
357 {
358 fRet = static_cast< double >( nCurTime ) / NANO_TIME_FACTOR;
359 }
360 }
361
362 return( fRet );
363 }
364
365 // ------------------------------------------------------------------------------
setStopTime(double)366 void SAL_CALL Player::setStopTime( double /* fTime */ )
367 throw( uno::RuntimeException )
368 {
369 OSL_TRACE( "GStreamer method avmedia::gst::Player::setStopTime needs to be implemented" );
370
371 /* Currently no need for implementation since higher levels of code don't use this method at all
372 !!! TODO: needs to be implemented if this functionality is needed at a later point of time
373 if( implInitPlayer() )
374 {
375 }
376
377 */
378 }
379
380 // ------------------------------------------------------------------------------
getStopTime()381 double SAL_CALL Player::getStopTime()
382 throw( uno::RuntimeException )
383 {
384 /*
385 Currently no need for implementation since higher levels of code don't set a stop time ATM
386 !!! TODO: needs to be fully implemented if this functionality is needed at a later point of time
387 */
388 ::osl::MutexGuard aGuard(m_aMutex);
389 return( getDuration() );
390 }
391
392 // ------------------------------------------------------------------------------
setRate(double)393 void SAL_CALL Player::setRate( double /* fRate */ )
394 throw( uno::RuntimeException )
395 {
396 OSL_TRACE( "GStreamer method avmedia::gst::Player::setRate needs to be implemented" );
397
398 /* Currently no need for implementation since higher levels of code don't use this method at all
399 !!! TODO: needs to be implemented if this functionality is needed at a later point of time
400 */
401 }
402
403 // ------------------------------------------------------------------------------
getRate()404 double SAL_CALL Player::getRate()
405 throw( uno::RuntimeException )
406 {
407 /*
408 Currently no need for implementation since higher levels of code don't set a different rate than 1 ATM
409 !!! TODO: needs to be fully implemented if this functionality is needed at a later point of time
410 */
411
412 return( 1.0 );
413 }
414
415 // ------------------------------------------------------------------------------
setPlaybackLoop(sal_Bool bSet)416 void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet )
417 throw( uno::RuntimeException )
418 {
419 ::osl::MutexGuard aGuard(m_aMutex);
420 if (bSet)
421 {
422 g_atomic_int_compare_and_exchange(&mnLooping, 0, 1);
423 }
424 else
425 {
426 g_atomic_int_compare_and_exchange(&mnLooping, 1, 0);
427 }
428 }
429
430 // ------------------------------------------------------------------------------
isPlaybackLoop()431 sal_Bool SAL_CALL Player::isPlaybackLoop()
432 throw( uno::RuntimeException )
433 {
434 ::osl::MutexGuard aGuard(m_aMutex);
435 return( g_atomic_int_get( &mnLooping ) > 0 );
436 }
437
438 // ------------------------------------------------------------------------------
setMute(sal_Bool bSet)439 void SAL_CALL Player::setMute( sal_Bool bSet )
440 throw( uno::RuntimeException )
441 {
442 ::osl::MutexGuard aGuard(m_aMutex);
443 if( implInitPlayer() && ( bSet != isMute() ) )
444 {
445 if( bSet )
446 {
447 g_object_set( mpPlayer, "volume", 0.0, NULL );
448 }
449 else
450 {
451 setVolumeDB( mnVolumeDB );
452 }
453 }
454 }
455
456 // ------------------------------------------------------------------------------
isMute()457 sal_Bool SAL_CALL Player::isMute()
458 throw( uno::RuntimeException )
459 {
460 gdouble fGstVolume = 1.0;
461 ::osl::MutexGuard aGuard(m_aMutex);
462
463 if( implInitPlayer() )
464 {
465 g_object_get( mpPlayer, "volume", &fGstVolume, NULL );
466 }
467
468 return( 0.0 == fGstVolume );
469 }
470
471 // ------------------------------------------------------------------------------
setVolumeDB(sal_Int16 nVolumeDB)472 void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB )
473 throw( uno::RuntimeException )
474 {
475 ::osl::MutexGuard aGuard(m_aMutex);
476 if( implInitPlayer() )
477 {
478 g_mutex_lock( mpMutex );
479 mnVolumeDB = nVolumeDB;
480 g_mutex_unlock( mpMutex );
481
482 // maximum gain for gstreamer volume is 10
483 double fGstVolume = pow( 10.0, static_cast< double >( ::std::min(
484 nVolumeDB, static_cast< sal_Int16 >( 20 ) ) / 20.0 ) );
485
486 g_object_set( mpPlayer, "volume", fGstVolume, NULL );
487 }
488 }
489
490 // ------------------------------------------------------------------------------
getVolumeDB()491 sal_Int16 SAL_CALL Player::getVolumeDB()
492 throw( uno::RuntimeException )
493 {
494 ::osl::MutexGuard aGuard(m_aMutex);
495 return( static_cast< sal_Int16 >( g_atomic_int_get( &mnVolumeDB ) ) );
496 }
497
498 // ------------------------------------------------------------------------------
getPreferredPlayerWindowSize()499 awt::Size SAL_CALL Player::getPreferredPlayerWindowSize()
500 throw( uno::RuntimeException )
501 {
502 awt::Size aSize( 0, 0 );
503 ::osl::MutexGuard aGuard(m_aMutex);
504
505 if( implInitPlayer() && ( g_atomic_int_get( &mnIsVideoSource ) > 0 ) )
506 {
507 aSize.Width = g_atomic_int_get( &mnVideoWidth );
508 aSize.Height = g_atomic_int_get( &mnVideoHeight );
509
510 // if we have a video source, but no size is given => use default size
511 if( ( aSize.Width <= 0 ) || ( aSize.Height <= 0 ) )
512 {
513 aSize.Width = VIDEO_DEFAULT_WIDTH;
514 aSize.Height = VIDEO_DEFAULT_HEIGHT;
515 }
516 }
517
518 OSL_TRACE( ">>> Requested preferred video size is: %d x %d pixel", aSize.Width, aSize.Height );
519
520 return( aSize );
521 }
522
523 // ------------------------------------------------------------------------------
createPlayerWindow(const uno::Sequence<uno::Any> & rArguments)524 uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow(
525 const uno::Sequence< uno::Any >& rArguments )
526 throw( uno::RuntimeException )
527 {
528 ::osl::MutexGuard aGuard(m_aMutex);
529 uno::Reference< ::media::XPlayerWindow > xRet;
530 awt::Size aSize( getPreferredPlayerWindowSize() );
531
532 OSL_ENSURE( !g_atomic_pointer_get( &mpPlayerWindow ), "::avmedia::gst::Player already has a player window" );
533
534 if( ( aSize.Width > 0 ) && ( aSize.Height > 0 ) )
535 {
536 Window* pPlayerWindow = new Window( *this );
537
538 xRet = pPlayerWindow;
539
540 if( !pPlayerWindow->create( rArguments ) )
541 {
542 xRet.clear();
543 }
544 else
545 {
546 // try to use gconf user configurable video sink first
547 GstElement* pVideoSink = gst_element_factory_make( "gconfvideosink", NULL );
548
549 if( ( NULL != pVideoSink ) ||
550 ( NULL != ( pVideoSink = gst_element_factory_make( "autovideosink", NULL ) ) ) ||
551 ( NULL != ( pVideoSink = gst_element_factory_make( "xvimagesink", NULL ) ) ) ||
552 ( NULL != ( pVideoSink = gst_element_factory_make( "ximagesink", NULL ) ) ) )
553 {
554 GstState aOldState = GST_STATE_NULL;
555
556 mpPlayerWindow = pPlayerWindow;
557 gst_element_get_state( mpPlayer, &aOldState, NULL, GST_MAX_TIMEOUT );
558 gst_element_set_state( mpPlayer, GST_STATE_READY );
559 g_object_set( mpPlayer, "video-sink", pVideoSink, NULL );
560 gst_element_set_state( mpPlayer, aOldState );
561 }
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_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 );
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 // set state of player to READY or destroy object in case of FAILURE
881 if( gst_element_set_state( mpPlayer, GST_STATE_READY ) == GST_STATE_CHANGE_FAILURE )
882 {
883 gst_object_unref( mpPlayer );
884 mpPlayer = NULL;
885 }
886
887 g_atomic_int_add( &mnInitialized, 1 );
888 g_cond_signal( mpCond );
889
890 // run the main loop
891 g_main_loop_run( mpLoop );
892
893 // clenanup
894 // no mutex necessary since other thread joined us (this thread)
895 // after setting the quit flag
896 if( mpPlayer )
897 {
898 gst_element_set_state( mpPlayer, GST_STATE_NULL );
899 gst_object_unref( mpPlayer );
900 mpPlayer = NULL;
901 }
902
903 g_main_loop_unref( mpLoop );
904 mpLoop = NULL;
905
906 g_source_destroy( pBusSource );
907 g_source_unref( pBusSource );
908
909 g_source_destroy( pIdleSource );
910 g_source_unref( pIdleSource );
911
912 g_main_context_unref( mpContext );
913 mpContext = NULL;
914 }
915 else
916 {
917 g_atomic_int_add( &mnInitialized, 1 );
918 g_cond_signal( mpCond );
919 }
920
921 return( NULL );
922 }
923
924 // ------------------------------------------------------------------------------
handleCreateWindow(GstBus *,GstMessage * pMsg)925 GstBusSyncReply Player::handleCreateWindow( GstBus* /* pBus */,
926 GstMessage* pMsg )
927 {
928 GstBusSyncReply eRet = GST_BUS_PASS;
929
930 if( pMsg &&
931 ( GST_MESSAGE_TYPE( pMsg ) == GST_MESSAGE_ELEMENT ) &&
932 gst_structure_has_name( pMsg->structure, "prepare-xwindow-id" ) &&
933 g_atomic_pointer_get( &mpPlayerWindow ) )
934 {
935 OSL_TRACE( ">>> Got Request to create XOverlay" );
936
937 gst_x_overlay_set_xwindow_id( GST_X_OVERLAY( GST_MESSAGE_SRC( pMsg ) ),
938 static_cast< Window* >( g_atomic_pointer_get(
939 &mpPlayerWindow ) )->getXWindowHandle() );
940
941 gst_message_unref( pMsg );
942 eRet = GST_BUS_DROP;
943 }
944
945 return( eRet );
946 }
947 } // namespace gst
948 } // namespace avmedia
949