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 57 GstBusSource() : 58 mpBus( NULL ) 59 {} 60 61 ~GstBusSource() 62 {} 63 }; 64 65 66 // ----------------------------------------------------------------------- 67 extern "C" 68 { 69 70 static gpointer 71 lcl_implThreadFunc( gpointer pData ) 72 { 73 return( pData ? static_cast< Player* >( pData )->run() : NULL ); 74 } 75 76 static gboolean 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 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 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 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 134 lcl_implIdleFunc( gpointer pData ) 135 { 136 return( pData ? static_cast< Player* >( pData )->idle() : true ); 137 } 138 139 static GstBusSyncReply 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 // --------------- 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 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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 // ------------------------------------------------------------------------------ 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