diff options
Diffstat (limited to 'avmedia/source/gstreamer/gstplayer.cxx')
-rw-r--r-- | avmedia/source/gstreamer/gstplayer.cxx | 634 |
1 files changed, 634 insertions, 0 deletions
diff --git a/avmedia/source/gstreamer/gstplayer.cxx b/avmedia/source/gstreamer/gstplayer.cxx new file mode 100644 index 000000000000..1e2d0b70800e --- /dev/null +++ b/avmedia/source/gstreamer/gstplayer.cxx @@ -0,0 +1,634 @@ +/* -*- 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 2010 Novell, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include <math.h> + +#include <rtl/string.hxx> + +#include <vcl/syschild.hxx> +#include <vcl/sysdata.hxx> + + +#include "gstplayer.hxx" +#include "gstframegrabber.hxx" +#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" + +#if OSL_DEBUG_LEVEL > 2 +#define DBG OSL_TRACE +#else +#define DBG(...) +#endif + +using namespace ::com::sun::star; + +namespace avmedia { namespace gstreamer { + +// ---------------- +// - Player - +// ---------------- + +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() ) +{ + // Initialize GStreamer library + int argc = 1; + char name[] = "libreoffice"; + char *arguments[] = { name }; + char** argv = arguments; + GError* pError = NULL; + + mbInitialized = gst_init_check( &argc, &argv, &pError ); + + if (pError != NULL) + // TODO: thow an exception? + g_error_free (pError); +} + +// ------------------------------------------------------------------------------ + +Player::~Player() +{ + // Release the elements and pipeline + if( mbInitialized ) + { + if( mpPlaybin ) + { + gst_element_set_state( mpPlaybin, GST_STATE_NULL ); + gst_object_unref( GST_OBJECT( mpPlaybin ) ); + + mpPlaybin = NULL; + } + + if( mpXOverlay ) { + g_object_unref( G_OBJECT ( mpXOverlay ) ); + mpXOverlay = NULL; + } + } +} + +// ------------------------------------------------------------------------------ + +static gboolean gst_pipeline_bus_callback( GstBus *, GstMessage *message, gpointer data ) +{ + Player* pPlayer = (Player *) data; + + pPlayer->processMessage( message ); + + return TRUE; +} + +static GstBusSyncReply gst_pipeline_bus_sync_handler( GstBus *, GstMessage * message, gpointer data ) +{ + Player* pPlayer = (Player *) data; + + return pPlayer->processSyncMessage( message ); +} + +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; + + gst_message_parse_state_changed (message, NULL, &newstate, &pendingstate); + + 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; + } +} + +GstBusSyncReply Player::processSyncMessage( GstMessage *message ) +{ + DBG( "%p processSyncMessage: %s", this, GST_MESSAGE_TYPE_NAME( message ) ); + +#if OSL_DEBUG_LEVEL > 0 + if ( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_ERROR ) + { + GError* error; + gchar* error_debug; + + gst_message_parse_error( message, &error, &error_debug ); + OSL_TRACE("gstreamer error: '%s' debug: '%s'", error->message, error_debug); + } +#endif + + 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; + } + } + + if( GST_MESSAGE_TYPE( message ) == GST_MESSAGE_STATE_CHANGED ) { + if( message->src == GST_OBJECT( mpPlaybin ) ) { + GstState newstate, pendingstate; + + gst_message_parse_state_changed (message, NULL, &newstate, &pendingstate); + + DBG( "%p state change received, new state %d", this, newstate ); + if( newstate == GST_STATE_PAUSED && + pendingstate == GST_STATE_VOID_PENDING ) { + + DBG( "%p change to paused received", this ); + + if( mnDuration == 0) { + GstFormat format = GST_FORMAT_TIME; + gint64 gst_duration = 0L; + + if( gst_element_query_duration( mpPlaybin, &format, &gst_duration) && format == GST_FORMAT_TIME && gst_duration > 0L ) + mnDuration = gst_duration; + } + + if( mnWidth == 0 ) { + GList *pStreamInfo = NULL; + + g_object_get( G_OBJECT( mpPlaybin ), "stream-info", &pStreamInfo, NULL ); + + for ( ; pStreamInfo != NULL; pStreamInfo = pStreamInfo->next) { + GObject *pInfo = G_OBJECT( pStreamInfo->data ); + + if( !pInfo ) + continue; + + 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( !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 OSL_DEBUG_LEVEL > 2 + 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 OSL_DEBUG_LEVEL > 2 + sal_Bool aSuccess = +#endif + osl_setCondition( maSizeCondition ); + DBG( "%p set condition result: %d", this, aSuccess ); + } + } + + return GST_BUS_PASS; +} + +void Player::preparePlaybin( const ::rtl::OUString& rURL, bool bFakeVideo ) +{ + GstBus *pBus; + + if( mpPlaybin != NULL ) { + gst_element_set_state( mpPlaybin, GST_STATE_NULL ); + mbPlayPending = false; + g_object_unref( mpPlaybin ); + } + + mpPlaybin = gst_element_factory_make( "playbin", NULL ); + + if( bFakeVideo ) + g_object_set( G_OBJECT( mpPlaybin ), "video-sink", gst_element_factory_make( "fakesink", NULL ), NULL ); + + mbFakeVideo = bFakeVideo; + + rtl::OString ascURL = OUStringToOString( rURL, RTL_TEXTENCODING_ASCII_US ); + g_object_set( G_OBJECT( mpPlaybin ), "uri", ascURL.getStr() , NULL ); + + 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 ); +} + +bool Player::create( const ::rtl::OUString& rURL ) +{ + bool bRet = false; + + // create all the elements and link them + + DBG("create player, URL: %s", OUStringToOString( rURL, RTL_TEXTENCODING_UTF8 ).getStr()); + + if( mbInitialized ) + { + preparePlaybin( rURL, true ); + + gst_element_set_state( mpPlaybin, GST_STATE_PAUSED ); + mbPlayPending = false; + + bRet = true; + } + + if( bRet ) + maURL = rURL; + else + maURL = ::rtl::OUString(); + + return bRet; +} + +// ------------------------------------------------------------------------------ + +void SAL_CALL Player::start( ) + throw (uno::RuntimeException) +{ + // set the pipeline state to READY and run the loop + if( mbInitialized && NULL != mpPlaybin ) + { + gst_element_set_state( mpPlaybin, GST_STATE_PLAYING ); + mbPlayPending = true; + } +} + +// ------------------------------------------------------------------------------ + +void SAL_CALL Player::stop( ) + throw (uno::RuntimeException) +{ + // set the pipeline in PAUSED STATE + if( mpPlaybin ) + gst_element_set_state( mpPlaybin, GST_STATE_PAUSED ); + + mbPlayPending = false; + DBG( "stop %p", mpPlaybin ); +} + +// ------------------------------------------------------------------------------ + +sal_Bool SAL_CALL Player::isPlaying() + throw (uno::RuntimeException) +{ + bool bRet = mbPlayPending; + + // return whether the pipeline is in PLAYING STATE or not + if( !mbPlayPending && mbInitialized && mpPlaybin ) + { + bRet = GST_STATE_PLAYING == GST_STATE( mpPlaybin ); + } + + DBG( "isPlaying %d", bRet ); + + return bRet; +} + +// ------------------------------------------------------------------------------ + +double SAL_CALL Player::getDuration( ) + throw (uno::RuntimeException) +{ + // slideshow checks for non-zero duration, so cheat here + double duration = 0.01; + + if( mpPlaybin && mnDuration > 0 ) { + duration = mnDuration / 1E9; + } + + return duration; +} + +// ------------------------------------------------------------------------------ + +void SAL_CALL Player::setMediaTime( double fTime ) + throw (uno::RuntimeException) +{ + if( mpPlaybin ) { + gint64 gst_position = llround (fTime * 1E9); + + 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 ); + + DBG( "seek to: %"SAL_PRIdINT64" ns original: %lf s", gst_position, fTime ); + } +} + +// ------------------------------------------------------------------------------ + +double SAL_CALL Player::getMediaTime( ) + throw (uno::RuntimeException) +{ + 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; + } + + return position; +} + +// ------------------------------------------------------------------------------ + +void SAL_CALL Player::setStopTime( double /*fTime*/ ) + throw (uno::RuntimeException) +{ + // TODO implement +} + +// ------------------------------------------------------------------------------ + +double SAL_CALL Player::getStopTime( ) + throw (uno::RuntimeException) +{ + // Get the time at which to stop + + return 0; +} + +// ------------------------------------------------------------------------------ + +void SAL_CALL Player::setRate( double /*fRate*/ ) + throw (uno::RuntimeException) +{ + // TODO set the window rate +} + +// ------------------------------------------------------------------------------ + +double SAL_CALL Player::getRate( ) + throw (uno::RuntimeException) +{ + double rate = 0.0; + + // TODO get the window rate + if( mbInitialized ) + { + + } + + return rate; +} + +// ------------------------------------------------------------------------------ + +void SAL_CALL Player::setPlaybackLoop( sal_Bool bSet ) + throw (uno::RuntimeException) +{ + // TODO check how to do with GST + mbLooping = bSet; +} + +// ------------------------------------------------------------------------------ + +sal_Bool SAL_CALL Player::isPlaybackLoop( ) + throw (uno::RuntimeException) +{ + // TODO check how to do with GST + return mbLooping; +} + +// ------------------------------------------------------------------------------ + +void SAL_CALL Player::setMute( sal_Bool bSet ) + throw (uno::RuntimeException) +{ + 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 ) + { + double nVolume = mnUnmutedVolume; + if( bSet ) + { + nVolume = 0.0; + } + + g_object_set( G_OBJECT( mpPlaybin ), "volume", nVolume, NULL ); + + mbMuted = bSet; + } +} + +// ------------------------------------------------------------------------------ + +sal_Bool SAL_CALL Player::isMute( ) + throw (uno::RuntimeException) +{ + return mbMuted; +} + +// ------------------------------------------------------------------------------ + +void SAL_CALL Player::setVolumeDB( sal_Int16 nVolumeDB ) + throw (uno::RuntimeException) +{ + mnUnmutedVolume = pow( 10.0, nVolumeDB / 20.0 ); + + DBG( "set volume: %d gst volume: %lf", nVolumeDB, mnUnmutedVolume ); + + // change volume + if( !mbMuted && mpPlaybin ) + { + g_object_set( G_OBJECT( mpPlaybin ), "volume", (gdouble) mnUnmutedVolume, NULL ); + } +} + +// ------------------------------------------------------------------------------ + +sal_Int16 SAL_CALL Player::getVolumeDB( ) + throw (uno::RuntimeException) +{ + sal_Int16 nVolumeDB(0); + + if( mpPlaybin ) { + double nGstVolume = 0.0; + + g_object_get( G_OBJECT( mpPlaybin ), "volume", &nGstVolume, NULL ); + + nVolumeDB = (sal_Int16) ( 20.0*log10 ( nGstVolume ) ); + } + + return nVolumeDB; +} + +// ------------------------------------------------------------------------------ + +awt::Size SAL_CALL Player::getPreferredPlayerWindowSize( ) + throw (uno::RuntimeException) +{ + awt::Size aSize( 0, 0 ); + + DBG( "%p Player::getPreferredPlayerWindowSize, member %d x %d", this, mnWidth, mnHeight ); + + TimeValue aTimeout = { 10, 0 }; +#if OSL_DEBUG_LEVEL > 2 + oslConditionResult aResult = +#endif + osl_waitCondition( maSizeCondition, &aTimeout ); + + if( mbFakeVideo ) { + mbFakeVideo = sal_False; + + 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 ); + } + + DBG( "%p Player::getPreferredPlayerWindowSize after waitCondition %d, member %d x %d", this, aResult, mnWidth, mnHeight ); + + if( mnWidth != 0 && mnHeight != 0 ) { + aSize.Width = mnWidth; + aSize.Height = mnHeight; + } + + return aSize; +} + +// ------------------------------------------------------------------------------ + +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() ); + + DBG( "Player::createPlayerWindow %d %d length: %d", aSize.Width, aSize.Height, rArguments.getLength() ); + + if( aSize.Width > 0 && aSize.Height > 0 ) + { + ::avmedia::gstreamer::Window* pWindow = new ::avmedia::gstreamer::Window( mxMgr, *this ); + + xRet = pWindow; + + if( rArguments.getLength() > 2 ) + { + sal_IntPtr pIntPtr = 0; + rArguments[ 2 ] >>= pIntPtr; + SystemChildWindow *pParentWindow = reinterpret_cast< SystemChildWindow* >( pIntPtr ); + const SystemEnvData* pEnvData = pParentWindow ? pParentWindow->GetSystemData() : NULL; + OSL_ASSERT(pEnvData); + if (pEnvData) + mnWindowID = pEnvData->aWindow; + } + } + + return xRet; +} + +// ------------------------------------------------------------------------------ + +uno::Reference< media::XFrameGrabber > SAL_CALL Player::createFrameGrabber( ) + throw (uno::RuntimeException) +{ + uno::Reference< media::XFrameGrabber > xRet; + + return xRet; +} + +// ------------------------------------------------------------------------------ + +::rtl::OUString SAL_CALL Player::getImplementationName( ) + throw (uno::RuntimeException) +{ + return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( AVMEDIA_GST_PLAYER_IMPLEMENTATIONNAME ) ); +} + +// ------------------------------------------------------------------------------ + +sal_Bool SAL_CALL Player::supportsService( const ::rtl::OUString& ServiceName ) + throw (uno::RuntimeException) +{ + return ServiceName.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM ( AVMEDIA_GST_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_GST_PLAYER_SERVICENAME ) ); + + return aRet; +} + +} // namespace gstreamer +} // namespace avmedia + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |