/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ #include "gstplayer.hxx" #include "gstwindow.hxx" #include "gstframegrabber.hxx" #include #include #include #include #include #include // maximum timeout time in nanoseconds #define GST_MAX_TIMEOUT (2500 * GST_MSECOND) using namespace ::com::sun::star; namespace avmedia { namespace gst { const double NANO_TIME_FACTOR = 1000000000.0; const long VIDEO_DEFAULT_WIDTH = 256; const long VIDEO_DEFAULT_HEIGHT = 192; // ---------------- // - GstBusSource - // ---------------- struct GstBusSource : public GSource { GstBus* mpBus; GstBusSource() : mpBus( NULL ) {} ~GstBusSource() {} }; // ----------------------------------------------------------------------- extern "C" { static gpointer lcl_implThreadFunc( gpointer pData ) { return( pData ? static_cast< Player* >( pData )->run() : NULL ); } static gboolean lcl_implBusCheck( GSource* pSource ) { GstBusSource* pBusSource = static_cast< GstBusSource* >( pSource ); return( pBusSource && GST_IS_BUS( pBusSource->mpBus ) && gst_bus_have_pending( GST_BUS_CAST( pBusSource->mpBus ) ) ); } static gboolean lcl_implBusPrepare( GSource* pSource, gint* pTimeout ) { if (pTimeout) { *pTimeout = 0; } return lcl_implBusCheck(pSource); } static gboolean lcl_implBusDispatch( GSource* pSource, GSourceFunc /*aCallback*/, gpointer pData ) { GstBusSource* pBusSource = static_cast< GstBusSource* >( pSource ); gboolean bRet = false; if( pData && pBusSource && GST_IS_BUS( pBusSource->mpBus ) ) { GstMessage* pMsg = gst_bus_pop( pBusSource->mpBus ); if( pMsg ) { bRet = static_cast< Player* >( pData )->busCallback( pBusSource->mpBus, pMsg ); gst_message_unref( pMsg ); } } return( bRet ); } static void lcl_implBusFinalize( GSource* pSource ) { GstBusSource* pBusSource = static_cast< GstBusSource* >( pSource ); if( pBusSource && pBusSource->mpBus ) { gst_object_unref( pBusSource->mpBus ); pBusSource->mpBus = NULL; } } static gboolean lcl_implIdleFunc( gpointer pData ) { return( pData ? static_cast< Player* >( pData )->idle() : true ); } static GstBusSyncReply lcl_implHandleCreateWindowFunc( GstBus* pBus, GstMessage* pMsg, gpointer pData ) { return (pData) ? static_cast< Player* >( pData )->handleCreateWindow( pBus, pMsg ) : GST_BUS_PASS; } } // extern "C" // --------------- // - Player - // --------------- Player::Player( GString* pURI ) : Player_BASE(m_aMutex), mpMutex( g_mutex_new() ), mpCond( g_cond_new() ), mpThread( NULL ), mpContext( NULL ), mpLoop( NULL ), mpPlayer( NULL ), mpURI( pURI ), mpPlayerWindow( NULL ), mnIsVideoSource( 0 ), mnVideoWidth( 0 ), mnVideoHeight( 0 ), mnInitialized( 0 ), mnVolumeDB( 0 ), mnLooping( 0 ), mnQuit( 0 ), mnVideoWindowSet( 0 ), mnInitFail( 0 ) { // initialize GStreamer framework only once static bool bGstInitialized = false; if( !bGstInitialized ) { gst_init( NULL, NULL ); bGstInitialized = true; } if( pURI ) { OSL_TRACE( ">>> --------------------------------" ); OSL_TRACE( ">>> Creating Player object with URL: %s", pURI->str ); mpThread = g_thread_create( &lcl_implThreadFunc, this, true, NULL ); } } // ------------------------------------------------------------------------------ Player::~Player() { if( g_atomic_pointer_get( &mpPlayer ) ) { implQuitThread(); } // cleanup g_cond_free( mpCond ); g_mutex_free( mpMutex ); g_string_free( mpURI, false ); } // ------------------------------------------------------------------------------ Player* Player::create( const ::rtl::OUString& rURL ) { Player* pPlayer = NULL; if( !rURL.isEmpty() ) { // safely initialize GLib threading framework try { if( !g_thread_supported() ) { g_thread_init( NULL ); } } catch( ... ) {} if( g_thread_supported() ) { const INetURLObject aURL( rURL ); if( aURL.GetProtocol() != INET_PROT_NOT_VALID ) { GString* pURI = g_string_new( ::rtl::OUStringToOString( aURL.GetMainURL( INetURLObject::NO_DECODE ), RTL_TEXTENCODING_UTF8 ).getStr() ); if( pURI->len ) { pPlayer = new Player( pURI ); // wait until thread signals that it has finished initialization if( pPlayer->mpThread ) { g_mutex_lock( pPlayer->mpMutex ); while( !pPlayer->implIsInitialized() ) { g_cond_wait( pPlayer->mpCond, pPlayer->mpMutex ); } g_mutex_unlock( pPlayer->mpMutex ); } // check if player pipeline could be initialized if( !pPlayer->mpPlayer ) { delete pPlayer; pPlayer = NULL; } } else { g_string_free( pURI, false ); } } } } return( pPlayer ); } // ------------------------------------------------------------------------------ void SAL_CALL Player::start() throw( uno::RuntimeException ) { ::osl::MutexGuard aGuard(m_aMutex); if( implInitPlayer() && !isPlaying() ) { gst_element_set_state( mpPlayer, GST_STATE_PLAYING ); } } // ------------------------------------------------------------------------------ void SAL_CALL Player::stop() throw( uno::RuntimeException ) { ::osl::MutexGuard aGuard(m_aMutex); if( implInitPlayer() && isPlaying() ) { gst_element_set_state( mpPlayer, GST_STATE_PAUSED ); } } // ------------------------------------------------------------------------------ sal_Bool SAL_CALL Player::isPlaying() throw( uno::RuntimeException ) { GstState aState = GST_STATE_NULL; ::osl::MutexGuard aGuard(m_aMutex); if( mpPlayer ) { gst_element_get_state( mpPlayer, &aState, NULL, GST_MAX_TIMEOUT ); } return( GST_STATE_PLAYING == aState ); } // ------------------------------------------------------------------------------ double SAL_CALL Player::getDuration() throw( uno::RuntimeException ) { ::osl::MutexGuard aGuard(m_aMutex); gint64 nDuration = 0; if( implInitPlayer() ) { if( !gst_element_query_duration( mpPlayer, GST_FORMAT_TIME, &nDuration ) || ( nDuration < 0 ) ) { nDuration = 0; } } return( static_cast< double >( nDuration ) / NANO_TIME_FACTOR ); } // ------------------------------------------------------------------------------ void SAL_CALL Player::setMediaTime( double fTime ) throw( uno::RuntimeException ) { ::osl::MutexGuard aGuard(m_aMutex); if( implInitPlayer() ) { fTime = ::std::min( ::std::max( fTime, 0.0 ), getDuration() ); gst_element_seek_simple( mpPlayer, GST_FORMAT_TIME, (GstSeekFlags) ( GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT ), static_cast< gint64 >( fTime * NANO_TIME_FACTOR ) ); } } // ------------------------------------------------------------------------------ double SAL_CALL Player::getMediaTime() throw( uno::RuntimeException ) { double fRet = 0.0; ::osl::MutexGuard aGuard(m_aMutex); if( implInitPlayer() ) { gint64 nCurTime = 0; if( gst_element_query_position( mpPlayer, GST_FORMAT_TIME, &nCurTime ) && ( nCurTime >= 0 ) ) { fRet = static_cast< double >( nCurTime ) / NANO_TIME_FACTOR; } } return( fRet ); } // ------------------------------------------------------------------------------ void SAL_CALL Player::setStopTime( double /* fTime */ ) throw( uno::RuntimeException ) { OSL_TRACE( "GStreamer method avmedia::gst::Player::setStopTime needs to be implemented" ); /* Currently no need for implementation since higher levels of code don't use this method at all !!! TODO: needs to be implemented if this functionality is needed at a later point of time if( implInitPlayer() ) { } */ } // ------------------------------------------------------------------------------ double SAL_CALL Player::getStopTime() throw( uno::RuntimeException ) { /* Currently no need for implementation since higher levels of code don't set a stop time ATM !!! TODO: needs to be fully implemented if this functionality is needed at a later point of time */ ::osl::MutexGuard aGuard(m_aMutex); return( getDuration() ); } // ------------------------------------------------------------------------------ void SAL_CALL Player::setRate( double /* fRate */ ) throw( uno::RuntimeException ) { OSL_TRACE( "GStreamer method avmedia::gst::Player::setRate needs to be implemented" ); /* Currently no need for implementation since higher levels of code don't use this method at all !!! TODO: needs to be implemented if this functionality is needed at a later point of time */ } // ------------------------------------------------------------------------------ double SAL_CALL Player::getRate() throw( uno::RuntimeException ) { /* Currently no need for implementation since higher levels of code don't set a different rate than 1 ATM !!! TODO: needs to be fully implemented if this functionality is needed at a later point of time */ return( 1.0 ); } // ------------------------------------------------------------------------------ void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet ) throw( uno::RuntimeException ) { ::osl::MutexGuard aGuard(m_aMutex); if (bSet) { g_atomic_int_compare_and_exchange(&mnLooping, 0, 1); } else { g_atomic_int_compare_and_exchange(&mnLooping, 1, 0); } } // ------------------------------------------------------------------------------ sal_Bool SAL_CALL Player::isPlaybackLoop() throw( uno::RuntimeException ) { ::osl::MutexGuard aGuard(m_aMutex); return( g_atomic_int_get( &mnLooping ) > 0 ); } // ------------------------------------------------------------------------------ void SAL_CALL Player::setMute( sal_Bool bSet ) throw( uno::RuntimeException ) { ::osl::MutexGuard aGuard(m_aMutex); if( implInitPlayer() && ( bSet != isMute() ) ) { if( bSet ) { g_object_set( mpPlayer, "volume", 0.0, NULL ); } else { setVolumeDB( mnVolumeDB ); } } } // ------------------------------------------------------------------------------ sal_Bool SAL_CALL Player::isMute() throw( uno::RuntimeException ) { gdouble fGstVolume = 1.0; ::osl::MutexGuard aGuard(m_aMutex); if( implInitPlayer() ) { g_object_get( mpPlayer, "volume", &fGstVolume, NULL ); } return( 0.0 == fGstVolume ); } // ------------------------------------------------------------------------------ void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB ) throw( uno::RuntimeException ) { ::osl::MutexGuard aGuard(m_aMutex); if( implInitPlayer() ) { g_mutex_lock( mpMutex ); mnVolumeDB = nVolumeDB; g_mutex_unlock( mpMutex ); // maximum gain for gstreamer volume is 10 double fGstVolume = pow( 10.0, static_cast< double >( ::std::min( nVolumeDB, static_cast< sal_Int16 >( 20 ) ) / 20.0 ) ); g_object_set( mpPlayer, "volume", fGstVolume, NULL ); } } // ------------------------------------------------------------------------------ sal_Int16 SAL_CALL Player::getVolumeDB() throw( uno::RuntimeException ) { ::osl::MutexGuard aGuard(m_aMutex); return( static_cast< sal_Int16 >( g_atomic_int_get( &mnVolumeDB ) ) ); } // ------------------------------------------------------------------------------ awt::Size SAL_CALL Player::getPreferredPlayerWindowSize() throw( uno::RuntimeException ) { awt::Size aSize( 0, 0 ); ::osl::MutexGuard aGuard(m_aMutex); if( implInitPlayer() && ( g_atomic_int_get( &mnIsVideoSource ) > 0 ) ) { aSize.Width = g_atomic_int_get( &mnVideoWidth ); aSize.Height = g_atomic_int_get( &mnVideoHeight ); // if we have a video source, but no size is given => use default size if( ( aSize.Width <= 0 ) || ( aSize.Height <= 0 ) ) { aSize.Width = VIDEO_DEFAULT_WIDTH; aSize.Height = VIDEO_DEFAULT_HEIGHT; } } OSL_TRACE( ">>> Requested preferred video size is: %d x %d pixel", aSize.Width, aSize.Height ); return( aSize ); } // ------------------------------------------------------------------------------ uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& rArguments ) throw( uno::RuntimeException ) { ::osl::MutexGuard aGuard(m_aMutex); uno::Reference< ::media::XPlayerWindow > xRet; awt::Size aSize( getPreferredPlayerWindowSize() ); OSL_ENSURE( !g_atomic_pointer_get( &mpPlayerWindow ), "::avmedia::gst::Player already has a player window" ); if( ( aSize.Width > 0 ) && ( aSize.Height > 0 ) ) { Window* pPlayerWindow = new Window( *this ); xRet = pPlayerWindow; if( !pPlayerWindow->create( rArguments ) ) { OSL_ENSURE( false, "could not create player window\n" ); xRet.clear(); } else { // try to use gconf user configurable video sink first GstElement* pVideoSink = gst_element_factory_make( "gconfvideosink", NULL ); if( ( NULL != pVideoSink ) || ( NULL != ( pVideoSink = gst_element_factory_make( "autovideosink", NULL ) ) ) || ( NULL != ( pVideoSink = gst_element_factory_make( "xvimagesink", NULL ) ) ) || ( NULL != ( pVideoSink = gst_element_factory_make( "ximagesink", NULL ) ) ) ) { GstState aOldState = GST_STATE_NULL; mpPlayerWindow = pPlayerWindow; gst_element_get_state( mpPlayer, &aOldState, NULL, GST_MAX_TIMEOUT ); gst_element_set_state( mpPlayer, GST_STATE_READY ); g_object_set( mpPlayer, "video-sink", pVideoSink, NULL ); gst_video_overlay_set_window_handle( GST_VIDEO_OVERLAY( mpPlayer ), pPlayerWindow->getXWindowHandle() ); gst_element_set_state( mpPlayer, aOldState ); } else OSL_ENSURE( false, "no video sink available\n" ); } } return( xRet ); } // ------------------------------------------------------------------------------ uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber() throw( ::com::sun::star::uno::RuntimeException ) { ::osl::MutexGuard aGuard(m_aMutex); FrameGrabber* pFrameGrabber = NULL; const awt::Size aPrefSize( getPreferredPlayerWindowSize() ); if( ( aPrefSize.Width > 0 ) && ( aPrefSize.Height > 0 ) ) { pFrameGrabber = FrameGrabber::create( mpURI ); } return( pFrameGrabber ); } // ------------------------------------------------------------------------------ void SAL_CALL Player::disposing() { ::osl::MutexGuard aGuard(m_aMutex); if( mpPlayer ) { stop(); implQuitThread(); } OSL_ASSERT( NULL == mpPlayer ); } // ------------------------------------------------------------------------------ ::rtl::OUString SAL_CALL Player::getImplementationName() throw( uno::RuntimeException ) { return( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_GSTREAMER_PLAYER_IMPLEMENTATIONNAME ) ) ); } // ------------------------------------------------------------------------------ sal_Bool SAL_CALL Player::supportsService( const ::rtl::OUString& ServiceName ) throw( uno::RuntimeException ) { return( ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( AVMEDIA_GSTREAMER_PLAYER_SERVICENAME ) ) ); } // ------------------------------------------------------------------------------ uno::Sequence< ::rtl::OUString > SAL_CALL Player::getSupportedServiceNames() throw( uno::RuntimeException ) { uno::Sequence< ::rtl::OUString > aRet( 1 ); aRet[ 0 ] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_GSTREAMER_PLAYER_SERVICENAME ) ); return( aRet ); } // ------------------------------------------------------------------------------ void Player::implQuitThread() { if( mpThread ) { // set quit flag to 1 so that the main loop will be quit in idle // handler the next time it is called from the thread's main loop g_atomic_int_inc( &mnQuit ); // wait until loop and as such the thread has quit g_thread_join( mpThread ); mpThread = NULL; } } // ------------------------------------------------------------------------------ bool Player::implInitPlayer() { bool bRet = false; if( mpPlayer && (mnInitFail < 3) ) { GstState aState = GST_STATE_NULL; if( gst_element_get_state( mpPlayer, &aState, NULL, GST_MAX_TIMEOUT ) == GST_STATE_CHANGE_SUCCESS ) { bRet = ( GST_STATE_PAUSED == aState ) || ( GST_STATE_PLAYING == aState ); if( !bRet ) { gst_element_set_state( mpPlayer, GST_STATE_PAUSED ); bRet = ( gst_element_get_state( mpPlayer, &aState, NULL, GST_MAX_TIMEOUT ) == GST_STATE_CHANGE_SUCCESS ) && ( GST_STATE_PAUSED == aState ); } } if( ! bRet ) mnInitFail++; } return( bRet ); } // ------------------------------------------------------------------------------ gboolean Player::busCallback( GstBus* /*pBus*/, GstMessage* pMsg ) { if( pMsg && mpLoop ) { switch( GST_MESSAGE_TYPE( pMsg ) ) { case ( GST_MESSAGE_EOS ): { if( g_atomic_int_get( &mnLooping ) > 0 ) { setMediaTime( 0.0 ); start(); } else { stop(); } } break; case ( GST_MESSAGE_ERROR ): { gchar* pDebug; GError* pErr; gst_message_parse_error( pMsg, &pErr, &pDebug ); fprintf( stderr, "Error: %s\n", pErr->message ); g_free( pDebug ); g_error_free( pErr ); } break; default: { break; } } } return( true ); } // ------------------------------------------------------------------------------ void Player::implHandleNewElementFunc( GstBin* /* pBin */, GstElement* pElement, gpointer pData ) { if( pElement ) { #ifdef DEBUG gchar* pElementName = gst_element_get_name( pElement ); if( pElementName ) { OSL_TRACE( ">>> Bin has element: %s", pElementName ); g_free( pElementName ); } #endif if( GST_IS_BIN( pElement ) ) { // set this handler in case we have a GstBin element g_signal_connect( GST_BIN( pElement ), "element-added", G_CALLBACK( Player::implHandleNewElementFunc ), pData ); } // watch for all pads that are going to be added to this element; g_signal_connect( pElement, "pad-added", G_CALLBACK( Player::implHandleNewPadFunc ), pData ); } } // ------------------------------------------------------------------------------ void Player::implHandleNewPadFunc( GstElement* pElement, GstPad* pPad, gpointer pData ) { Player* pPlayer = static_cast< Player* >( pData ); if( pPlayer && pElement && pPad ) { #ifdef DEBUG gchar* pElementName = gst_element_get_name( pElement ); gchar* pPadName = gst_pad_get_name( pPad ); OSL_TRACE( ">>> Element %s has pad: %s", pElementName, pPadName ); g_free( pPadName ); g_free( pElementName ); #endif GstCaps* pCaps = gst_pad_get_current_caps( pPad ); // we are interested only in getting video properties // width and height or if we have a video source at all if( pCaps ) { for( gint i = 0, nSize = gst_caps_get_size( pCaps ); i < nSize; ++i ) { const GstStructure* pStruct = gst_caps_get_structure( pCaps, i ); if( pStruct ) { const gchar* pStructName = gst_structure_get_name( pStruct ); #ifdef DEBUG OSL_TRACE( "\t>>> Pad has structure: %s", pStructName ); for( gint n = 0, nFields = gst_structure_n_fields( pStruct ); n < nFields; ++n ) { OSL_TRACE( "\t\t>>> Structure has field: %s", gst_structure_nth_field_name( pStruct, n ) ); } #endif // just look for structures having 'video' in their names if( ::std::string( pStructName ).find( "video" ) != ::std::string::npos ) { g_atomic_int_inc( &pPlayer->mnIsVideoSource ); for( gint n = 0, nFields = gst_structure_n_fields( pStruct ); n < nFields; ++n ) { const gchar* pFieldName = gst_structure_nth_field_name( pStruct, n ); gint nValue; if( ( ::std::string( pFieldName ).find( "width" ) != ::std::string::npos ) && gst_structure_get_int( pStruct, pFieldName, &nValue ) ) { const gint nDiff = nValue - g_atomic_int_get( &pPlayer->mnVideoWidth ); g_atomic_int_add( &pPlayer->mnVideoWidth, ::std::max( nDiff, 0 ) ); } else if( ( ::std::string( pFieldName ).find( "height" ) != ::std::string::npos ) && gst_structure_get_int( pStruct, pFieldName, &nValue ) ) { const gint nDiff = nValue - g_atomic_int_get( &pPlayer->mnVideoHeight ); g_atomic_int_add( &pPlayer->mnVideoHeight, ::std::max( nDiff, 0 ) ); } } } } } gst_caps_unref( pCaps ); } } } // ------------------------------------------------------------------------------ gboolean Player::idle() { // test if main loop should quit by comparing with 1 // and set flag mnQuit to 0 so we call g_main_loop_quit exactly once bool const bQuit = g_atomic_int_compare_and_exchange( &mnQuit, 1, 0 ); if( bQuit ) { g_main_loop_quit( mpLoop ); } // don't eat up all cpu time usleep( 1000 ); return( true ); } // ------------------------------------------------------------------------------ gpointer Player::run() { static GSourceFuncs aSourceFuncs = { &lcl_implBusPrepare, &lcl_implBusCheck, &lcl_implBusDispatch, &lcl_implBusFinalize, NULL, NULL }; if( NULL != ( mpPlayer = gst_element_factory_make( "playbin", NULL ) ) ) { // initialization // no mutex necessary since initialization // is synchronous until loop is started mpContext = g_main_context_new(); mpLoop = g_main_loop_new( mpContext, false ); // add idle callback GSource* pIdleSource = g_idle_source_new(); g_source_set_callback( pIdleSource, &lcl_implIdleFunc, this, NULL ); g_source_attach( pIdleSource, mpContext ); // add bus callback GSource* pBusSource = g_source_new( &aSourceFuncs, sizeof( GstBusSource ) ); static_cast< GstBusSource* >( pBusSource )->mpBus = gst_pipeline_get_bus( GST_PIPELINE( mpPlayer ) ); g_source_set_callback( pBusSource, NULL, this, NULL ); g_source_attach( pBusSource, mpContext ); // add bus sync handler to intercept video window creation for setting our own window gst_bus_set_sync_handler( static_cast< GstBusSource* >( pBusSource )->mpBus, &lcl_implHandleCreateWindowFunc, this, NULL ); // watch for all elements (and pads) that will be added to the playbin, // in order to retrieve properties like video width and height g_signal_connect( GST_BIN( mpPlayer ), "element-added", G_CALLBACK( Player::implHandleNewElementFunc ), this ); // set source URI for player g_object_set( mpPlayer, "uri", mpURI->str, NULL ); // set video fake sink first, since we only create a player without window here // and don't want to have the gstreamer default window appearing g_object_set( mpPlayer, "video-sink", gst_element_factory_make( "fakesink", NULL ), NULL ); // This isn't ever going to happen, as createPlayerWindow() has to be called first // to set the mpPlayerWindow, but let's keep it here in case it is called first some day: if ( g_atomic_pointer_get( &mpPlayerWindow ) ) { gst_video_overlay_set_window_handle( GST_VIDEO_OVERLAY( mpPlayer ), static_cast< Window* >( g_atomic_pointer_get( &mpPlayerWindow ) )->getXWindowHandle() ); } // set state of player to READY or destroy object in case of FAILURE if( gst_element_set_state( mpPlayer, GST_STATE_READY ) == GST_STATE_CHANGE_FAILURE ) { gst_object_unref( mpPlayer ); mpPlayer = NULL; } g_atomic_int_add( &mnInitialized, 1 ); g_cond_signal( mpCond ); // run the main loop g_main_loop_run( mpLoop ); // clenanup // no mutex necessary since other thread joined us (this thread) // after setting the quit flag if( mpPlayer ) { gst_element_set_state( mpPlayer, GST_STATE_NULL ); gst_object_unref( mpPlayer ); mpPlayer = NULL; } g_main_loop_unref( mpLoop ); mpLoop = NULL; g_source_destroy( pBusSource ); g_source_unref( pBusSource ); g_source_destroy( pIdleSource ); g_source_unref( pIdleSource ); g_main_context_unref( mpContext ); mpContext = NULL; } else { g_atomic_int_add( &mnInitialized, 1 ); g_cond_signal( mpCond ); } return( NULL ); } // ------------------------------------------------------------------------------ GstBusSyncReply Player::handleCreateWindow( GstBus* /* pBus */, GstMessage* pMsg ) { GstBusSyncReply eRet = GST_BUS_PASS; if( pMsg && gst_is_video_overlay_prepare_window_handle_message( pMsg ) && g_atomic_pointer_get( &mpPlayerWindow ) ) { OSL_TRACE( ">>> Got Request to create XOverlay" ); gst_video_overlay_set_window_handle( GST_VIDEO_OVERLAY( GST_MESSAGE_SRC( pMsg ) ), static_cast< Window* >( g_atomic_pointer_get( &mpPlayerWindow ) )->getXWindowHandle() ); gst_message_unref( pMsg ); eRet = GST_BUS_DROP; } return( eRet ); } } // namespace gst } // namespace avmedia