diff options
Diffstat (limited to 'avmedia/source/gstreamer/gstplayer.cxx')
-rwxr-xr-x[-rw-r--r--] | avmedia/source/gstreamer/gstplayer.cxx | 1088 |
1 files changed, 388 insertions, 700 deletions
diff --git a/avmedia/source/gstreamer/gstplayer.cxx b/avmedia/source/gstreamer/gstplayer.cxx index f082cfc5c513..f6ea7295a540 100644..100755 --- a/avmedia/source/gstreamer/gstplayer.cxx +++ b/avmedia/source/gstreamer/gstplayer.cxx @@ -1,8 +1,9 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * - * Copyright 2000, 2010 Oracle and/or its affiliates. + * Copyright 2010 Novell, Inc. * * OpenOffice.org - a multi-platform office productivity suite * @@ -25,914 +26,601 @@ * ************************************************************************/ +#include <math.h> + +#ifndef __RTL_USTRING_ +#include <rtl/string.hxx> +#endif + #include "gstplayer.hxx" -#include "gstwindow.hxx" #include "gstframegrabber.hxx" -#include <stdio.h> -#include <unistd.h> -#include <math.h> -#include <string> -#include <gst/gstelement.h> +#include "gstwindow.hxx" + #include <gst/interfaces/xoverlay.h> +#define AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME "com.sun.star.comp.avmedia.Player_GStreamer" +#define AVMEDIA_GST_PLAYER_SERVICENAME "com.sun.star.media.Player_GStreamer" -// maximum timeout time in nanoseconds -#define GST_MAX_TIMEOUT (2500 * GST_MSECOND) +#if DEBUG +#define DBG OSL_TRACE +#else +#define DBG(...) +#endif 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; +namespace avmedia { namespace gstreamer { // ---------------- -// - GstBusSource - +// - Player - // ---------------- -struct GstBusSource : public GSource +Player::Player( const uno::Reference< lang::XMultiServiceFactory >& rxMgr ) : + mxMgr( rxMgr ), + mpPlaybin( NULL ), + mbFakeVideo (sal_False ), + mnUnmutedVolume( 0 ), + mbPlayPending ( false ), + mbMuted( false ), + mbLooping( false ), + mbInitialized( false ), + mnWindowID( 0 ), + mpXOverlay( NULL ), + mnDuration( 0 ), + mnWidth( 0 ), + mnHeight( 0 ), + maSizeCondition( osl_createCondition() ) { - GstBus* mpBus; + // Initialize GStreamer library + int argc = 1; + char name[] = "libreoffice"; + char *arguments[] = { name }; + char** argv = arguments; + GError* pError = NULL; - GstBusSource() : - mpBus( NULL ) - {} + mbInitialized = gst_init_check( &argc, &argv, &pError ); - ~GstBusSource() - {} -}; + if (pError != NULL) + // TODO: thow an exception? + g_error_free (pError); +} -// --------------- -// - Player - -// --------------- -Player::Player( GString* pURI ) : - 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 ) +Player::~Player() +{ + // Release the elements and pipeline + if( mbInitialized ) { - gst_init( NULL, NULL ); - bGstInitialized = true; - } + if( mpPlaybin ) + { + gst_element_set_state( mpPlaybin, GST_STATE_NULL ); + gst_object_unref( GST_OBJECT( mpPlaybin ) ); - if( pURI ) - { - OSL_TRACE( ">>> --------------------------------" ); - OSL_TRACE( ">>> Creating Player object with URL: %s", pURI->str ); + mpPlaybin = NULL; + } - mpThread = g_thread_create( Player::implThreadFunc, this, true, NULL ); + if( mpXOverlay ) { + g_object_unref( G_OBJECT ( mpXOverlay ) ); + mpXOverlay = NULL; + } } } // ------------------------------------------------------------------------------ -Player::~Player() +static gboolean gst_pipeline_bus_callback( GstBus *, GstMessage *message, gpointer data ) { - if( g_atomic_pointer_get( &mpPlayer ) ) - { - implQuitThread(); - } + Player* pPlayer = (Player *) data; - // cleanup - g_cond_free( mpCond ); - g_mutex_free( mpMutex ); - g_string_free( mpURI, false ); + pPlayer->processMessage( message ); + + return TRUE; } -// ------------------------------------------------------------------------------ -Player* Player::create( const ::rtl::OUString& rURL ) +static GstBusSyncReply gst_pipeline_bus_sync_handler( GstBus *, GstMessage * message, gpointer data ) { - Player* pPlayer = NULL; + Player* pPlayer = (Player *) data; - if( rURL.getLength() ) - { - // safely initialize GLib threading framework - try - { - if( !g_thread_supported() ) - { - g_thread_init( NULL ); - } - } - catch( ... ) - {} + return pPlayer->processSyncMessage( message ); +} - 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 ); - } +void Player::processMessage( GstMessage *message ) +{ + switch( GST_MESSAGE_TYPE( message ) ) { + case GST_MESSAGE_EOS: + gst_element_set_state( mpPlaybin, GST_STATE_READY ); + mbPlayPending = false; + if (mbLooping) + start(); + break; + case GST_MESSAGE_STATE_CHANGED: + if( message->src == GST_OBJECT( mpPlaybin ) ) { + GstState newstate, pendingstate; - g_mutex_unlock( pPlayer->mpMutex ); - } + gst_message_parse_state_changed (message, NULL, &newstate, &pendingstate); - // check if player pipeline could be initialized - if( !pPlayer->mpPlayer ) - { - delete pPlayer; - pPlayer = NULL; - } - } - else - { - g_string_free( pURI, false ); - } - } + if( newstate == GST_STATE_PAUSED && + pendingstate == GST_STATE_VOID_PENDING && + mpXOverlay ) + gst_x_overlay_expose( mpXOverlay ); + + if (mbPlayPending) + mbPlayPending = ((newstate == GST_STATE_READY) || (newstate == GST_STATE_PAUSED)); } + default: + break; } - - return( pPlayer ); } -// ------------------------------------------------------------------------------ -void SAL_CALL Player::start() - throw( uno::RuntimeException ) +GstBusSyncReply Player::processSyncMessage( GstMessage *message ) { - if( implInitPlayer() && !isPlaying() ) - { - gst_element_set_state( mpPlayer, GST_STATE_PLAYING ); + DBG( "%p processSyncMessage: %s", this, GST_MESSAGE_TYPE_NAME( message ) ); + +#if DEBUG + if ( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR ) { + GError* error; + gchar* error_debug; + + gst_message_parse_error( message, &error, &error_debug ); + DBG("error: '%s' debug: '%s'", error->message, error_debug); } -} +#endif -// ------------------------------------------------------------------------------ -void SAL_CALL Player::stop() - throw( uno::RuntimeException ) -{ - if( implInitPlayer() && isPlaying() ) - { - gst_element_set_state( mpPlayer, GST_STATE_PAUSED ); + if (message->structure) { + if( !strcmp( gst_structure_get_name( message->structure ), "prepare-xwindow-id" ) && mnWindowID != 0 ) { + if( mpXOverlay ) + g_object_unref( G_OBJECT ( mpXOverlay ) ); + mpXOverlay = GST_X_OVERLAY( GST_MESSAGE_SRC( message ) ); + g_object_ref( G_OBJECT ( mpXOverlay ) ); + gst_x_overlay_set_xwindow_id( mpXOverlay, mnWindowID ); + return GST_BUS_DROP; + } } -} -// ------------------------------------------------------------------------------ -sal_Bool SAL_CALL Player::isPlaying() - throw( uno::RuntimeException ) -{ - GstState aState = GST_STATE_NULL; + if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_STATE_CHANGED ) { + if( message->src == GST_OBJECT( mpPlaybin ) ) { + GstState newstate, pendingstate; - if( mpPlayer ) - { - gst_element_get_state( mpPlayer, &aState, NULL, GST_MAX_TIMEOUT ); - } + gst_message_parse_state_changed (message, NULL, &newstate, &pendingstate); - return( GST_STATE_PLAYING == aState ); -} + DBG( "%p state change received, new state %d", this, newstate ); + if( newstate == GST_STATE_PAUSED && + pendingstate == GST_STATE_VOID_PENDING ) { -// ------------------------------------------------------------------------------ -double SAL_CALL Player::getDuration() - throw( uno::RuntimeException ) -{ - gint64 nDuration = 0; + DBG( "%p change to paused received", this ); - if( implInitPlayer() ) - { - GstFormat aFormat = GST_FORMAT_TIME; + if( mnDuration == 0) { + GstFormat format = GST_FORMAT_TIME; + gint64 gst_duration = 0L; - if( !gst_element_query_duration( mpPlayer, &aFormat, &nDuration ) || - ( GST_FORMAT_TIME != aFormat ) || - ( nDuration < 0 ) ) - { - nDuration = 0; - } - } + if( gst_element_query_duration( mpPlaybin, &format, &gst_duration) && format == GST_FORMAT_TIME && gst_duration > 0L ) + mnDuration = gst_duration; + } - return( static_cast< double >( nDuration ) / NANO_TIME_FACTOR ); -} + if( mnWidth == 0 ) { + GList *pStreamInfo = NULL; -// ------------------------------------------------------------------------------ -void SAL_CALL Player::setMediaTime( double fTime ) - throw( uno::RuntimeException ) -{ - if( implInitPlayer() ) - { - fTime = ::std::min( ::std::max( fTime, 0.0 ), getDuration() ); + g_object_get( G_OBJECT( mpPlaybin ), "stream-info", &pStreamInfo, NULL ); - 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 ) ); - } -} + for ( ; pStreamInfo != NULL; pStreamInfo = pStreamInfo->next) { + GObject *pInfo = G_OBJECT( pStreamInfo->data ); -// ------------------------------------------------------------------------------ -double SAL_CALL Player::getMediaTime() - throw( uno::RuntimeException ) -{ - double fRet = 0.0; + if( !pInfo ) + continue; - if( implInitPlayer() ) - { - GstFormat aFormat = GST_FORMAT_TIME; - gint64 nCurTime = 0; + int nType; + g_object_get( pInfo, "type", &nType, NULL ); + GEnumValue *pValue = g_enum_get_value( G_PARAM_SPEC_ENUM( g_object_class_find_property( G_OBJECT_GET_CLASS( pInfo ), "type" ) )->enum_class, + nType ); - if( gst_element_query_position( mpPlayer, &aFormat, &nCurTime ) && - ( GST_FORMAT_TIME == aFormat ) && - ( nCurTime >= 0 ) ) - { - fRet = static_cast< double >( nCurTime ) / NANO_TIME_FACTOR; + if( !g_strcasecmp( pValue->value_nick, "video" ) ) { + GstStructure *pStructure; + GstPad *pPad; + + g_object_get( pInfo, "object", &pPad, NULL ); + pStructure = gst_caps_get_structure( GST_PAD_CAPS( pPad ), 0 ); + if( pStructure ) { + gst_structure_get_int( pStructure, "width", &mnWidth ); + gst_structure_get_int( pStructure, "height", &mnHeight ); + DBG( "queried size: %d x %d", mnWidth, mnHeight ); + } + } + } + +#if DEBUG + sal_Bool aSuccess = +#endif + osl_setCondition( maSizeCondition ); + DBG( "%p set condition result: %d", this, aSuccess ); + } + } + } + } else if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR ) { + if( mnWidth == 0 ) { + // an error occurred, set condition so that OOo thread doesn't wait for us +#if DEBUG + sal_Bool aSuccess = +#endif + osl_setCondition( maSizeCondition ); + DBG( "%p set condition result: %d", this, aSuccess ); } } - return( fRet ); + return GST_BUS_PASS; } -// ------------------------------------------------------------------------------ -void SAL_CALL Player::setStopTime( double /* fTime */ ) - throw( uno::RuntimeException ) +void Player::preparePlaybin( const ::rtl::OUString& rURL, bool bFakeVideo ) { - OSL_TRACE( "GStreamer method avmedia::gst::Player::setStopTime needs to be implemented" ); + GstBus *pBus; - /* 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() ) - { - } + if( mpPlaybin != NULL ) { + gst_element_set_state( mpPlaybin, GST_STATE_NULL ); + mbPlayPending = false; + g_object_unref( mpPlaybin ); + } - */ -} + mpPlaybin = gst_element_factory_make( "playbin", NULL ); -// ------------------------------------------------------------------------------ -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 - */ + if( bFakeVideo ) + g_object_set( G_OBJECT( mpPlaybin ), "video-sink", gst_element_factory_make( "fakesink", NULL ), NULL ); - return( getDuration() ); -} + mbFakeVideo = bFakeVideo; -// ------------------------------------------------------------------------------ -void SAL_CALL Player::setRate( double /* fRate */ ) - throw( uno::RuntimeException ) -{ - OSL_TRACE( "GStreamer method avmedia::gst::Player::setRate needs to be implemented" ); + rtl::OString ascURL = OUStringToOString( rURL, RTL_TEXTENCODING_ASCII_US ); + g_object_set( G_OBJECT( mpPlaybin ), "uri", ascURL.getStr() , NULL ); - /* 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 - */ + pBus = gst_element_get_bus( mpPlaybin ); + gst_bus_add_watch( pBus, gst_pipeline_bus_callback, this ); + DBG( "%p set sync handler", this ); + gst_bus_set_sync_handler( pBus, gst_pipeline_bus_sync_handler, this ); + g_object_unref( pBus ); } -// ------------------------------------------------------------------------------ -double SAL_CALL Player::getRate() - throw( uno::RuntimeException ) +bool Player::create( const ::rtl::OUString& rURL ) { - /* - 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 ); -} + bool bRet = false; -// ------------------------------------------------------------------------------ -void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet ) - throw( uno::RuntimeException ) -{ - if( bSet && !isPlaybackLoop() ) - g_atomic_int_inc( &mnLooping ); - else if( !bSet && isPlaybackLoop() ) - g_atomic_int_dec_and_test( &mnLooping ); -} + // create all the elements and link them -// ------------------------------------------------------------------------------ -sal_Bool SAL_CALL Player::isPlaybackLoop() - throw( uno::RuntimeException ) -{ - return( g_atomic_int_get( &mnLooping ) > 0 ); -} + DBG("create player, URL: %s", OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 ).getStr()); -// ------------------------------------------------------------------------------ -void SAL_CALL Player::setMute( sal_Bool bSet ) - throw( uno::RuntimeException ) -{ - if( implInitPlayer() && ( bSet != isMute() ) ) + if( mbInitialized ) { - if( bSet ) - { - g_object_set( mpPlayer, "volume", 0.0, NULL ); - } - else - { - setVolumeDB( mnVolumeDB ); - } - } -} + preparePlaybin( rURL, true ); -// ------------------------------------------------------------------------------ -sal_Bool SAL_CALL Player::isMute() - throw( uno::RuntimeException ) -{ - gdouble fGstVolume = 1.0; + gst_element_set_state( mpPlaybin, GST_STATE_PAUSED ); + mbPlayPending = false; - if( implInitPlayer() ) - { - g_object_get( mpPlayer, "volume", &fGstVolume, NULL ); + bRet = true; } - return( 0.0 == fGstVolume ); + + if( bRet ) + maURL = rURL; + else + maURL = ::rtl::OUString(); + + return bRet; } // ------------------------------------------------------------------------------ -void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB ) - throw( uno::RuntimeException ) + +void SAL_CALL Player::start( ) + throw (uno::RuntimeException) { - if( implInitPlayer() ) + // set the pipeline state to READY and run the loop + if( mbInitialized && NULL != mpPlaybin ) { - 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 ); + gst_element_set_state( mpPlaybin, GST_STATE_PLAYING ); + mbPlayPending = true; } } // ------------------------------------------------------------------------------ -sal_Int16 SAL_CALL Player::getVolumeDB() - throw( uno::RuntimeException ) + +void SAL_CALL Player::stop( ) + throw (uno::RuntimeException) { - return( static_cast< sal_Int16 >( g_atomic_int_get( &mnVolumeDB ) ) ); + // set the pipeline in PAUSED STATE + if( mpPlaybin ) + gst_element_set_state( mpPlaybin, GST_STATE_PAUSED ); + + mbPlayPending = false; + DBG( "stop %p", mpPlaybin ); } // ------------------------------------------------------------------------------ -awt::Size SAL_CALL Player::getPreferredPlayerWindowSize() - throw( uno::RuntimeException ) + +sal_Bool SAL_CALL Player::isPlaying() + throw (uno::RuntimeException) { - awt::Size aSize( 0, 0 ); + bool bRet = mbPlayPending; - if( implInitPlayer() && ( g_atomic_int_get( &mnIsVideoSource ) > 0 ) ) + // return whether the pipeline is in PLAYING STATE or not + if( !mbPlayPending && mbInitialized && mpPlaybin ) { - 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; - } + bRet = GST_STATE_PLAYING == GST_STATE( mpPlaybin ); } - OSL_TRACE( ">>> Requested preferred video size is: %d x %d pixel", aSize.Width, aSize.Height ); + DBG( "isPlaying %d", bRet ); - return( aSize ); + return bRet; } // ------------------------------------------------------------------------------ -uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( - const uno::Sequence< uno::Any >& rArguments ) - throw( uno::RuntimeException ) -{ - 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; +double SAL_CALL Player::getDuration( ) + throw (uno::RuntimeException) +{ + // slideshow checks for non-zero duration, so cheat here + double duration = 0.01; - if( !pPlayerWindow->create( rArguments ) ) - { - 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_element_set_state( mpPlayer, aOldState ); - } - } + if( mpPlaybin && mnDuration > 0 ) { + duration = mnDuration / 1E9; } - return( xRet ); + return duration; } // ------------------------------------------------------------------------------ -uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber() - throw( ::com::sun::star::uno::RuntimeException ) + +void SAL_CALL Player::setMediaTime( double fTime ) + throw (uno::RuntimeException) { - FrameGrabber* pFrameGrabber = NULL; - const awt::Size aPrefSize( getPreferredPlayerWindowSize() ); + if( mpPlaybin ) { + gint64 gst_position = llround (fTime * 1E9); - if( ( aPrefSize.Width > 0 ) && ( aPrefSize.Height > 0 ) ) - { - pFrameGrabber = FrameGrabber::create( mpURI ); - } + gst_element_seek( mpPlaybin, 1.0, + GST_FORMAT_TIME, + GST_SEEK_FLAG_FLUSH, + GST_SEEK_TYPE_SET, gst_position, + GST_SEEK_TYPE_NONE, 0 ); + if( !isPlaying() ) + gst_element_set_state( mpPlaybin, GST_STATE_PAUSED ); - return( pFrameGrabber ); + DBG( "seek to: %lld ns original: %lf s", gst_position, fTime ); + } } // ------------------------------------------------------------------------------ -void SAL_CALL Player::dispose() - throw( uno::RuntimeException ) + +double SAL_CALL Player::getMediaTime( ) + throw (uno::RuntimeException) { - if( mpPlayer ) - { - stop(); - implQuitThread(); + double position = 0.0; + + if( mpPlaybin ) { + // get current position in the stream + GstFormat format = GST_FORMAT_TIME; + gint64 gst_position; + if( gst_element_query_position( mpPlaybin, &format, &gst_position ) && format == GST_FORMAT_TIME && gst_position > 0L ) + position = gst_position / 1E9; } - OSL_ASSERT( NULL == mpPlayer ); + return position; } // ------------------------------------------------------------------------------ -void SAL_CALL Player::addEventListener( const uno::Reference< lang::XEventListener >& /*rxListener*/ ) - throw( uno::RuntimeException ) -{} - -// ------------------------------------------------------------------------------ -void SAL_CALL Player::removeEventListener( const uno::Reference< lang::XEventListener >& /*rxListener*/ ) - throw( uno::RuntimeException ) -{} -// ------------------------------------------------------------------------------ -::rtl::OUString SAL_CALL Player::getImplementationName() - throw( uno::RuntimeException ) +void SAL_CALL Player::setStopTime( double /*fTime*/ ) + throw (uno::RuntimeException) { - return( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_GSTREAMER_PLAYER_IMPLEMENTATIONNAME ) ) ); + // TODO implement } // ------------------------------------------------------------------------------ -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 ) +double SAL_CALL Player::getStopTime( ) + throw (uno::RuntimeException) { - uno::Sequence< ::rtl::OUString > aRet( 1 ); - aRet[ 0 ] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_GSTREAMER_PLAYER_SERVICENAME ) ); + // Get the time at which to stop - return( aRet ); + return 0; } // ------------------------------------------------------------------------------ -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; - } +void SAL_CALL Player::setRate( double /*fRate*/ ) + throw (uno::RuntimeException) +{ + // TODO set the window rate } // ------------------------------------------------------------------------------ -bool Player::implInitPlayer() + +double SAL_CALL Player::getRate( ) + throw (uno::RuntimeException) { - bool bRet = false; + double rate = 0.0; - if( mpPlayer && (mnInitFail < 3) ) + // TODO get the window rate + if( mbInitialized ) { - 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 ); + return rate; } // ------------------------------------------------------------------------------ -gboolean Player::implBusPrepare( GSource* pSource, - gint* pTimeout ) -{ - if( pTimeout ) - { - *pTimeout = 0; - } - - return( implBusCheck( pSource ) ); -} -// ------------------------------------------------------------------------------ -gboolean Player::implBusCheck( GSource* pSource ) +void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet ) + throw (uno::RuntimeException) { - GstBusSource* pBusSource = static_cast< GstBusSource* >( pSource ); - - return( pBusSource && - GST_IS_BUS( pBusSource->mpBus ) && - gst_bus_have_pending( GST_BUS_CAST( pBusSource->mpBus ) ) ); + // TODO check how to do with GST + mbLooping = bSet; } // ------------------------------------------------------------------------------ -gboolean Player::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 ); -} -// ------------------------------------------------------------------------------ -void Player::implBusFinalize( GSource* pSource ) +sal_Bool SAL_CALL Player::isPlaybackLoop( ) + throw (uno::RuntimeException) { - GstBusSource* pBusSource = static_cast< GstBusSource* >( pSource ); - - if( pBusSource && pBusSource->mpBus ) - { - gst_object_unref( pBusSource->mpBus ); - pBusSource->mpBus = NULL; - } + // TODO check how to do with GST + return mbLooping; } // ------------------------------------------------------------------------------ -gboolean Player::busCallback( GstBus* /*pBus*/, - GstMessage* pMsg ) + +void SAL_CALL Player::setMute( sal_Bool bSet ) + throw (uno::RuntimeException) { - if( pMsg && mpLoop ) + DBG( "set mute: %d muted: %d unmuted volume: %lf", bSet, mbMuted, mnUnmutedVolume ); + + // change the volume to 0 or the unmuted volume + if( mpPlaybin && mbMuted != bSet ) { - switch( GST_MESSAGE_TYPE( pMsg ) ) + double nVolume = mnUnmutedVolume; + if( bSet ) { - 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 ); + nVolume = 0.0; + } - g_free( pDebug ); - g_error_free( pErr ); - } - break; + g_object_set( G_OBJECT( mpPlaybin ), "volume", nVolume, NULL ); - default: - { - break; - } - } + mbMuted = bSet; } - - return( true ); } // ------------------------------------------------------------------------------ -gboolean Player::implIdleFunc( gpointer pData ) -{ - return( pData ? static_cast< Player* >( pData )->idle() : true ); -} -// ------------------------------------------------------------------------------ -gpointer Player::implThreadFunc( gpointer pData ) +sal_Bool SAL_CALL Player::isMute( ) + throw (uno::RuntimeException) { - return( pData ? static_cast< Player* >( pData )->run() : NULL ); + return mbMuted; } // ------------------------------------------------------------------------------ -GstBusSyncReply Player::implHandleCreateWindowFunc( GstBus* pBus, - GstMessage* pMsg, - gpointer pData ) -{ - return( pData ? static_cast< Player* >( pData )->handleCreateWindow( pBus, pMsg ) : GST_BUS_PASS ); -} -// ------------------------------------------------------------------------------ -void Player::implHandleNewElementFunc( GstBin* /* pBin */, - GstElement* pElement, - gpointer pData ) +void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB ) + throw (uno::RuntimeException) { - if( pElement ) - { -#ifdef DEBUG - gchar* pElementName = gst_element_get_name( pElement ); + mnUnmutedVolume = pow( 10.0, nVolumeDB / 20.0 ); - 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 ); - } + DBG( "set volume: %d gst volume: %lf", nVolumeDB, mnUnmutedVolume ); - // watch for all pads that are going to be added to this element; - g_signal_connect( pElement, "pad-added", - G_CALLBACK( Player::implHandleNewPadFunc ), pData ); - } + // change volume + if( !mbMuted && mpPlaybin ) + { + g_object_set( G_OBJECT( mpPlaybin ), "volume", (gdouble) mnUnmutedVolume, NULL ); + } } // ------------------------------------------------------------------------------ -void Player::implHandleNewPadFunc( GstElement* pElement, - GstPad* pPad, - gpointer pData ) + +sal_Int16 SAL_CALL Player::getVolumeDB( ) + throw (uno::RuntimeException) { - Player* pPlayer = static_cast< Player* >( pData ); + sal_Int16 nVolumeDB(0); - if( pPlayer && pElement && pPad ) - { -#ifdef DEBUG - gchar* pElementName = gst_element_get_name( pElement ); - gchar* pPadName = gst_pad_get_name( pPad ); + if( mpPlaybin ) { + double nGstVolume = 0.0; - OSL_TRACE( ">>> Element %s has pad: %s", pElementName, pPadName ); + g_object_get( G_OBJECT( mpPlaybin ), "volume", &nGstVolume, NULL ); - g_free( pPadName ); - g_free( pElementName ); -#endif + nVolumeDB = (sal_Int16) ( 20.0*log10 ( nGstVolume ) ); + } - GstCaps* pCaps = gst_pad_get_caps( pPad ); + return nVolumeDB; +} - // 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 ); +awt::Size SAL_CALL Player::getPreferredPlayerWindowSize( ) + throw (uno::RuntimeException) +{ + awt::Size aSize( 0, 0 ); -#ifdef DEBUG - OSL_TRACE( "\t>>> Pad has structure: %s", pStructName ); + DBG( "%p Player::getPreferredPlayerWindowSize, member %d x %d", this, mnWidth, mnHeight ); - 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 ) ); - } + TimeValue aTimeout = { 10, 0 }; +#if DEBUG + oslConditionResult aResult = #endif + osl_waitCondition( maSizeCondition, &aTimeout ); - // 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 ) ); - } - } - } - } - } + if( mbFakeVideo ) { + mbFakeVideo = sal_False; - gst_caps_unref( pCaps ); - } + g_object_set( G_OBJECT( mpPlaybin ), "video-sink", NULL, NULL ); + gst_element_set_state( mpPlaybin, GST_STATE_READY ); + gst_element_set_state( mpPlaybin, GST_STATE_PAUSED ); } -} -// ------------------------------------------------------------------------------ -gboolean Player::idle() -{ - // test if main loop should quit and set flag mnQuit to 1 - bool bQuit = g_atomic_int_compare_and_exchange( &mnQuit, 1, 1 ); + DBG( "%p Player::getPreferredPlayerWindowSize after waitCondition %d, member %d x %d", this, aResult, mnWidth, mnHeight ); - if( bQuit ) - { - // set mnQuit back to 0 to avoid mutiple g_main_loop_quit calls - // in case Player::idle() is called again later; - // the flag should have been set only once within Ctor called from - // the application thread - g_atomic_int_dec_and_test( &mnQuit ); - g_main_loop_quit( mpLoop ); + if( mnWidth != 0 && mnHeight != 0 ) { + aSize.Width = mnWidth; + aSize.Height = mnHeight; } - // don't eat up all cpu time - usleep( 1000 ); - - return( true ); + return aSize; } // ------------------------------------------------------------------------------ -gpointer Player::run() + +uno::Reference< ::media::XPlayerWindow > SAL_CALL Player::createPlayerWindow( const uno::Sequence< uno::Any >& rArguments ) + throw (uno::RuntimeException) { - static GSourceFuncs aSourceFuncs = - { - Player::implBusPrepare, - Player::implBusCheck, - Player::implBusDispatch, - Player::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, Player::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, - Player::implHandleCreateWindowFunc, this ); - - // 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 ); - - // 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; - } + uno::Reference< ::media::XPlayerWindow > xRet; + awt::Size aSize( getPreferredPlayerWindowSize() ); - g_atomic_int_add( &mnInitialized, 1 ); - g_cond_signal( mpCond ); + DBG( "Player::createPlayerWindow %d %d length: %d", aSize.Width, aSize.Height, rArguments.getLength() ); - // run the main loop - g_main_loop_run( mpLoop ); + if( aSize.Width > 0 && aSize.Height > 0 ) + { + ::avmedia::gstreamer::Window* pWindow = new ::avmedia::gstreamer::Window( mxMgr, *this ); - // 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; - } + xRet = pWindow; - g_main_loop_unref( mpLoop ); - mpLoop = NULL; + if( rArguments.getLength() > 2 ) { + rArguments[ 2 ] >>= mnWindowID; + DBG( "window ID: %ld", mnWindowID ); + } + } - g_source_destroy( pBusSource ); - g_source_unref( pBusSource ); + return xRet; +} - 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 ); - } +uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber( ) + throw (uno::RuntimeException) +{ + uno::Reference< media::XFrameGrabber > xRet; - return( NULL ); + return xRet; } // ------------------------------------------------------------------------------ -GstBusSyncReply Player::handleCreateWindow( GstBus* /* pBus */, - GstMessage* pMsg ) + +::rtl::OUString SAL_CALL Player::getImplementationName( ) + throw (uno::RuntimeException) { - GstBusSyncReply eRet = GST_BUS_PASS; + return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME ) ); +} - if( pMsg && - ( GST_MESSAGE_TYPE( pMsg ) == GST_MESSAGE_ELEMENT ) && - gst_structure_has_name( pMsg->structure, "prepare-xwindow-id" ) && - g_atomic_pointer_get( &mpPlayerWindow ) ) - { - OSL_TRACE( ">>> Got Request to create XOverlay" ); +// ------------------------------------------------------------------------------ - gst_x_overlay_set_xwindow_id( GST_X_OVERLAY( GST_MESSAGE_SRC( pMsg ) ), - static_cast< Window* >( g_atomic_pointer_get( - &mpPlayerWindow ) )->getXWindowHandle() ); +sal_Bool SAL_CALL Player::supportsService( const ::rtl::OUString& ServiceName ) + throw (uno::RuntimeException) +{ + return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( AVMEDIA_GST_PLAYER_SERVICENAME ) ); +} - gst_message_unref( pMsg ); - eRet = GST_BUS_DROP; - } +// ------------------------------------------------------------------------------ - return( eRet ); +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_GST_PLAYER_SERVICENAME ) ); + + return aRet; } -} // namespace gst + +} // namespace gstreamer } // namespace avmedia + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |