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