upnpsharing/upnpgstwrapper/src/upnpgstwrapper.cpp
branchIOP_Improvements
changeset 40 08b5eae9f9ff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/upnpsharing/upnpgstwrapper/src/upnpgstwrapper.cpp	Wed Nov 03 11:45:09 2010 +0200
@@ -0,0 +1,955 @@
+/*
+* Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  Implementation of UPnP GStreamer wrapper
+*
+*/
+
+// INCLUDES
+#include <bautils.h>
+
+#include <gst/app/gstappsink.h>
+
+#include <gst/app/gstappsink.h>
+#include <gst/app/gstappbuffer.h>
+
+#include <gst/gst.h>
+#include <gst/gstinfo.h>
+
+#include "upnpgstwrapper.h"
+
+_LIT( KComponentLogfile, "upnpgstwrapper.txt");
+#include "upnplog.h"
+
+#define __FUNC_LOG __LOG8_1( "%s", __PRETTY_FUNCTION__ );
+
+_LIT( KAlreadyInUseErrorMsg, "GST already in use (call Stop first)" );
+_LIT( KUnknownErrorErrorMsg, "Unknown error" );
+_LIT( KOutOfMemoryErrorMsg, "Out of memory" );
+
+const char* const KMimeTypeVideo = "video/";
+const char* const KMimeTypeAudio = "audio/";
+const char* const KDemuxerName = "demux";
+
+class CUpnpGstWrapperPimpl : CBase
+    {
+    
+public: // construction / destruction
+       
+    /**
+     * Static NewL
+     *
+     * @param none
+     * @return instance to CUpnpGstWrapperPimpl
+     */
+    static CUpnpGstWrapperPimpl* NewL();    
+    
+    /**
+     * Destructor
+     */
+    ~CUpnpGstWrapperPimpl();
+    
+private:
+    
+    /**
+     * Constructor
+     */
+    CUpnpGstWrapperPimpl();
+    
+    /**
+     * 2nd phrase Constructor
+     */
+    void ConstructL();
+
+public:
+    
+    inline void SetPipeline( GstElement* aPipeline );
+    
+    inline GstElement* Pipeline(); 
+    
+    inline void SetDemuxer( GstElement* aDemuxer );
+    
+    inline GstElement* Demuxer();
+    
+    inline void InitAppSinkL();
+    
+    inline TBool PullBufferL( TPtr8& aPointer,TInt aMaxBytesLength, RBuf8* aBuf );
+    
+    inline TInt TranscodedBytes();
+    
+public: //callbacks
+    
+    /**
+     * A function to receive any message occured within the pipeline
+     * Note that the function will be called in the same thread context as
+     * the posting object.
+     * 
+     * @param GstBus* a bus to create the watch for
+     * @param GstMessage* a message created within the pipeline
+     * @param gponter the user data that has been given, 
+     *        when registering the handler
+     * @return GstBusSyncReply indicates whether the message is also added
+     *         into async queue (after this callback) or dropped
+     */
+    static GstBusSyncReply SyncBusCallback( GstBus* aBus, GstMessage* aMsg, 
+            gpointer aData );
+    
+    /**
+     * A function to receive callback from demuxer whenever source pad is
+     * added into it
+     * 
+     * @param GstElement* an element (demuxer) where the source pad was added 
+     * @param GstPad* a souce pad that was added
+     * @param gponter the user data that has been given, 
+     *        when registering the handler
+     */
+    static void DemuxPadAddedCallback( GstElement* aElement, GstPad* aPad,
+            gpointer aData );
+    
+    /**
+     * Internal log handler.
+     * Implements GStreamer's log handler callback.
+     * Ouputs GStreamer debug traces if DEBUG_ENABLE flag is enabled in
+     * GStreamer.
+     */    
+    static void LogCallback( GstDebugCategory *category, GstDebugLevel level,
+            const gchar *file, const gchar *function, gint line, 
+            GObject *object, GstDebugMessage *message, gpointer data );
+
+private:
+    
+    GstElement*                            iPipeline;
+    GstElement*                            iDemuxer;        
+    
+    TInt                                   iBytesOffSet;
+    
+    GstElement*                            iAppsink;
+
+    GstElement*                            iElement;
+    
+    GstBuffer*                             iGstBuffer;
+    
+    };
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapperPimpl::NewL
+//---------------------------------------------------------------------------
+CUpnpGstWrapperPimpl* CUpnpGstWrapperPimpl::NewL()
+    {    
+    __FUNC_LOG;
+    
+    CUpnpGstWrapperPimpl* self = new( ELeave ) CUpnpGstWrapperPimpl();
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );  
+    return self;
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapperPimpl::~CUpnpGstWrapperPimpl
+//---------------------------------------------------------------------------
+CUpnpGstWrapperPimpl::~CUpnpGstWrapperPimpl()
+    {  
+    __FUNC_LOG;
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapperPimpl::CUpnpGstWrapperPimpl
+//---------------------------------------------------------------------------
+CUpnpGstWrapperPimpl::CUpnpGstWrapperPimpl()
+    {    
+    __FUNC_LOG;
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapperPimpl::ConstructL
+//---------------------------------------------------------------------------
+void CUpnpGstWrapperPimpl::ConstructL()
+    {    
+    __FUNC_LOG;
+    }
+
+void CUpnpGstWrapperPimpl::SetPipeline( GstElement* aPipeline )
+    {
+    __FUNC_LOG;
+    
+    iPipeline = aPipeline;
+    }
+
+GstElement* CUpnpGstWrapperPimpl::Pipeline()
+    {
+    __FUNC_LOG;
+    return iPipeline;
+    }
+
+void CUpnpGstWrapperPimpl::SetDemuxer( GstElement* aDemuxer )
+    {    
+    __FUNC_LOG;
+    
+    iDemuxer = aDemuxer;
+    }
+
+GstElement* CUpnpGstWrapperPimpl::Demuxer()
+    {
+    __FUNC_LOG;
+    
+    return iDemuxer;
+    }
+
+void CUpnpGstWrapperPimpl::InitAppSinkL()
+    {
+    iAppsink = gst_bin_get_by_name (
+            GST_BIN ( iPipeline ), "sinkbuffer");
+  
+    if( !iAppsink )
+        User::Leave( KErrNotFound);
+    
+    g_object_set (
+             G_OBJECT (iAppsink), "emit-signals", TRUE, "sync", FALSE, NULL);       
+    gst_object_unref (iAppsink); 
+    }
+    
+TBool CUpnpGstWrapperPimpl::PullBufferL( TPtr8& aPointer,TInt aMaxBytesLength, RBuf8* aBuf )
+    {
+    guint size;     
+     
+    if( iGstBuffer )
+        {
+        delete [] GST_BUFFER_DATA(iGstBuffer);
+        GST_BUFFER_DATA(iGstBuffer) = NULL;
+        }
+    
+    iGstBuffer = gst_app_sink_pull_buffer( GST_APP_SINK ( iAppsink  ));
+    
+    if( iGstBuffer )
+        {
+        size = GST_BUFFER_SIZE( iGstBuffer );
+
+        iBytesOffSet += size;
+        
+        if( aBuf && ( size + aBuf->Length() ) > aBuf->MaxLength() )
+            {
+            HBufC8* tmp(NULL);
+            TInt len(aBuf->Length());
+            if( len > 0 )
+                {
+                tmp = aBuf->AllocLC();
+                }
+            aBuf->Close();
+            aBuf->CreateL(size+len);
+            if( tmp )
+                {
+                aBuf->Append(*tmp);
+                CleanupStack::PopAndDestroy(tmp);
+                }
+            
+            aBuf->Append( (TUint8*)GST_BUFFER_DATA(iGstBuffer),size );
+            }
+        else
+            {                       
+            __ASSERT( (aMaxBytesLength >= size), __FILE__, __LINE__ );
+            aPointer.Set((TUint8*)GST_BUFFER_DATA(iGstBuffer),size,aMaxBytesLength);
+            }
+        }
+    return ( iGstBuffer ) ? EFalse : ETrue;
+    }
+
+TInt CUpnpGstWrapperPimpl::TranscodedBytes()
+    {
+    return iBytesOffSet;
+    }
+    
+// --------------------------------------------------------------------------
+// CUpnpGstWrapperPimpl::SyncBusCallback
+//---------------------------------------------------------------------------
+GstBusSyncReply CUpnpGstWrapperPimpl::SyncBusCallback( GstBus* /*aBus*/, 
+        GstMessage* aMsg, gpointer aData )
+    {
+    __FUNC_LOG;
+    
+    GstMessageType messageType = GST_MESSAGE_TYPE( aMsg );    
+    
+    if( messageType == GST_MESSAGE_EOS ||
+        messageType == GST_MESSAGE_ERROR )
+        {
+        __ASSERT( aData, __FILE__, __LINE__ );      
+        
+        CUpnpGstWrapper* parent = static_cast<CUpnpGstWrapper*>( aData );
+        Gst::TEvent event = Gst::EUnknown;
+    
+        parent->iMutex.Wait();
+        
+        switch( messageType ) 
+            {
+            case GST_MESSAGE_EOS:
+                {        
+                event = Gst::EEOS;  
+                break;
+                }
+            case GST_MESSAGE_ERROR: 
+                {
+                GError* err = NULL;
+                
+                char* debug;
+                gst_message_parse_error( aMsg, &err, &debug );
+                g_free( debug );   
+                
+                if( err )
+                    {
+                    TRAP_IGNORE( parent->SetErrorMsg( 
+                            parent->ConvertCharToDescL(
+                            err->message ) ) );
+                    g_error_free( err );
+                    err = NULL;
+                    }
+                event = Gst::EError;        
+                break;
+                }
+            default:
+                {
+                __ASSERT( EFalse, __FILE__, __LINE__ );
+                }
+            }
+
+        parent->iEventQueue.Append( event );        
+        
+        __ASSERT( parent->IsActive(), __FILE__, __LINE__ );
+        if( parent->iStatus == KRequestPending )
+            {
+            TRequestStatus* status = &parent->iStatus;
+            RThread thread;
+            TInt err = thread.Open( parent->iClientThreadId );
+            __ASSERT( !err, __FILE__, __LINE__ );                        
+                
+            thread.RequestComplete( status, KErrNone );            
+            thread.Close();
+            }
+        
+        parent->iMutex.Signal();
+
+        gst_message_unref( aMsg );
+        }
+
+    return GST_BUS_DROP; //do not add anything into async queue
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapperPimpl::DemuxPadAddedCallback
+//---------------------------------------------------------------------------
+void CUpnpGstWrapperPimpl::DemuxPadAddedCallback( GstElement* /*aElement*/, 
+        GstPad* aPad, gpointer aData )
+    {
+    __FUNC_LOG;    
+    __ASSERT( aData, __FILE__, __LINE__ );       
+    
+    CUpnpGstWrapper* parent = static_cast<CUpnpGstWrapper*>( aData );
+    
+    GstCaps* caps = gst_pad_get_caps( aPad );
+    char* str = gst_caps_to_string( caps );  
+    Gst::TEvent event = Gst::EUnknown;
+    
+    parent->iMutex.Wait();
+    
+    if( g_str_has_prefix( str, KMimeTypeVideo ) )
+        {
+        delete parent->iVideoInfo; parent->iVideoInfo = NULL;
+        TRAPD( err, parent->iVideoInfo = parent->ConvertCharToDescL( str ) );
+        if( !err )
+            {
+            event = Gst::EVideoStreamInfoAvailable;
+            }
+        else
+            {            
+            event = Gst::EError;
+            TRAP_IGNORE( parent->SetErrorMsgL( KOutOfMemoryErrorMsg ) );
+            }
+        }
+    else if( g_str_has_prefix( str, KMimeTypeAudio ) )
+        {
+        delete parent->iAudioInfo; parent->iAudioInfo = NULL;
+        TRAPD( err, parent->iAudioInfo = parent->ConvertCharToDescL( str ) );
+        if( !err )
+            {
+            event = Gst::EAudioStreamInfoAvailable;
+            }
+        else
+            {            
+            event = Gst::EError;
+            TRAP_IGNORE( parent->SetErrorMsgL( KOutOfMemoryErrorMsg ) );
+            }        
+        }
+    else
+        {
+        __ASSERT( EFalse, __FILE__, __LINE__ );
+        }
+    
+    parent->iEventQueue.Append( event );        
+    
+    __ASSERT( parent->IsActive(), __FILE__, __LINE__ );
+    if( parent->iStatus == KRequestPending )
+        {
+        TRequestStatus* status = &parent->iStatus;
+        RThread thread;
+        TInt err = thread.Open( parent->iClientThreadId );
+        __ASSERT( !err, __FILE__, __LINE__ );                        
+            
+        thread.RequestComplete( status, KErrNone );            
+        thread.Close();
+        }
+    
+    parent->iMutex.Signal();
+    
+    g_free(str);
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapperPimpl::LogCallback
+//---------------------------------------------------------------------------
+void CUpnpGstWrapperPimpl::LogCallback( GstDebugCategory* category, 
+        GstDebugLevel level, const char* file, const char* function, 
+        gint line, GObject* /*object*/, GstDebugMessage* message, 
+        gpointer /*data*/ )
+    {  
+    if (level > gst_debug_category_get_threshold (category))
+        {
+        return;    
+        }    
+    
+#ifndef __UPNP_LOG_FILE    
+    const char* const KGstLogFmt = "%s %20s %s:%d:%s %s\n";
+    RDebug::Printf( KGstLogFmt,
+            gst_debug_level_get_name (level),
+            gst_debug_category_get_name (category), file, line, function,
+            gst_debug_message_get (message) );
+#else
+    _LIT8( KGstLogFmtDesc, "%s %20s %s:%d:%s %s\n" );
+    RFileLogger::WriteFormat( KLogDir, KComponentLogfile,
+            EFileLoggingModeAppend, KGstLogFmtDesc, 
+            gst_debug_level_get_name (level),
+            gst_debug_category_get_name (category), file, line, function,
+            gst_debug_message_get (message) );
+#endif    
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::GetInstanceL
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+EXPORT_C CUpnpGstWrapper* CUpnpGstWrapper::GetInstanceL()
+    {
+    CUpnpGstWrapper* singleton = static_cast<CUpnpGstWrapper*>( Dll::Tls() );
+    if ( singleton == 0 )
+        {
+        // singleton must be created first
+        singleton = new ( ELeave ) CUpnpGstWrapper();
+        CleanupStack::PushL( singleton );
+        singleton->ConstructL();
+        User::LeaveIfError( Dll::SetTls( singleton ) );
+        CleanupStack::Pop( singleton );
+        }    
+    singleton->iSingletonRefCount++;
+
+    return singleton;
+    }
+	
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::Close
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+EXPORT_C void CUpnpGstWrapper::Close()
+    {
+    if(--iSingletonRefCount == 0)
+        {
+        Dll::FreeTls();
+        delete this;
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::~CUpnpGstWrapper
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+EXPORT_C CUpnpGstWrapper::~CUpnpGstWrapper()
+    {    
+    __FUNC_LOG;
+
+    Stop();
+
+    Cancel();
+    
+    gst_deinit();
+    
+    delete iErrorMsg;
+    delete iVideoInfo;
+    delete iAudioInfo;
+    
+    iEventQueue.Close();    
+    iMutex.Close();
+    iObservers.Close();
+    
+    delete iPimpl;
+    delete iPipeline;
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::CUpnpGstWrapper
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+CUpnpGstWrapper::CUpnpGstWrapper()
+    : CActive( EPriorityStandard )
+    , iDuration( KErrNotFound )
+    {    
+    __FUNC_LOG;    
+    
+    CActiveScheduler::Add( this );
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::ConstructL
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+void CUpnpGstWrapper::ConstructL()
+    {
+    __FUNC_LOG;      
+    
+    iPimpl = CUpnpGstWrapperPimpl::NewL();
+    
+    iMutex.CreateLocal();
+    
+    RThread thread;
+    iClientThreadId = thread.Id(); 
+    
+    /*
+     * Install our own log handler
+     * FIXME: After this there is two log handlers:
+     *        
+     *          Default: Uses glib's g_printerr() that prints 
+     *                   RDebug::RawPrints ONLY in full udeb mode
+     *          Our own: See behaviour in LogCallback function below
+     *          
+     *        We should somehow get rid of the default one...
+     */                    
+    gst_debug_add_log_function( CUpnpGstWrapperPimpl::LogCallback, this );
+    
+    __LOG( "Initializing GStreamer..." );
+    gst_init( NULL, NULL );    
+    __LOG( "GStreamer initialization done!" );     
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::SetObserverL
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+EXPORT_C void CUpnpGstWrapper::SetObserverL( 
+        MUpnpGstWrapperObserver& aObserver )
+    {
+    if( iObservers.Find( &aObserver ) < 0 )
+        {
+        iObservers.AppendL( &aObserver );
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::RemoveObserverL
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+EXPORT_C void CUpnpGstWrapper::RemoveObserver( 
+        MUpnpGstWrapperObserver& aObserver )
+    {
+    TInt index = iObservers.Find( &aObserver );
+    if( index >= 0 )
+        {
+        iObservers.Remove( index );
+        }    
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::StartL
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+EXPORT_C void CUpnpGstWrapper::StartL()
+    {
+    __FUNC_LOG;
+    
+    if( !iPipeline )
+        {
+        SetErrorMsgL( KAlreadyInUseErrorMsg );        
+        User::Leave( KErrNotReady );  
+        }
+
+    //Create pipeline according to the input description
+    char* pipelineStr = ConvertDescToCharL( *iPipeline );
+
+    GError* err = NULL;
+    /**
+     * Create a new pipeline based on command line syntax. 
+     * Please note that you might get a return value that is not NULL even 
+     * though the error is set. In this case there was a recoverable 
+     * parsing error and you can try to play the pipeline
+     */
+    __LOG( "Parsing GStreamer pipeline..." );
+    GstElement* pipeline = gst_parse_launch( pipelineStr, &err );
+    __LOG( "GStreamer pipeline parsing done!" );
+    User::Free( pipelineStr );
+    
+    if( err )
+        {    
+        SetErrorMsg( ConvertCharToDescL( err->message ) );
+        g_error_free( err );
+        err = NULL;
+        User::Leave( KErrGeneral );
+        }
+    
+    iPimpl->SetPipeline( pipeline );
+	
+    //TODO: Hardcoded demuxer name 'KDemuxerName -> should be fetched somehow 
+    //dynamically ??
+    GstElement* demuxer = gst_bin_get_by_name( GST_BIN( pipeline ), 
+            KDemuxerName );
+         
+    //If a demuxer is found, create 'pad-added' callback; else do nothing 
+    if( demuxer )
+        {
+        g_signal_connect( demuxer, "pad-added", 
+                G_CALLBACK( CUpnpGstWrapperPimpl::DemuxPadAddedCallback ), 
+                this );
+        iPimpl->SetDemuxer( demuxer );
+        }     
+
+    GstBus* bus = NULL;
+    bus = gst_pipeline_get_bus( GST_PIPELINE( pipeline ) );
+    if( !bus )
+        {
+        //unknown error
+        User::Leave(KErrGeneral);	
+        }	
+    
+    if( !IsActive() )
+        {
+        iStatus = KRequestPending;
+        SetActive();     
+        }
+    
+    gst_bus_set_sync_handler( bus, CUpnpGstWrapperPimpl::SyncBusCallback, this );
+    
+    gst_object_unref( bus );
+
+    gst_element_set_state( pipeline, GST_STATE_PLAYING );       
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::Stop
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+EXPORT_C void CUpnpGstWrapper::Stop()
+    {    
+    __FUNC_LOG;
+    GstElement* demuxer = iPimpl->Demuxer();
+    if( demuxer )
+        {
+        gst_object_unref( GST_OBJECT( demuxer ) );
+        iPimpl->SetDemuxer( NULL );
+        }    
+    
+    GstElement* pipeline = iPimpl->Pipeline();
+    if( pipeline )
+        {
+        gst_element_set_state( pipeline, GST_STATE_NULL );
+        gst_object_unref( GST_OBJECT( pipeline ) );
+        iPimpl->SetPipeline( NULL );
+        }
+    //Cancel();    
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::ErrorMsg
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+EXPORT_C const TDesC& CUpnpGstWrapper::ErrorMsg()
+    {
+    __FUNC_LOG;
+    
+    if( iErrorMsg )
+        {
+        return *iErrorMsg;
+        }
+    else
+        {
+        return KUnknownErrorErrorMsg();
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::FeatureListL
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+EXPORT_C CDesCArray* CUpnpGstWrapper::FeatureListL()
+    {
+    __FUNC_LOG;
+    
+    CDesCArray* array = new (ELeave) CDesCArrayFlat( 5 );
+    CleanupStack::PushL( array );
+
+    GList* features = gst_registry_get_feature_list( 
+            gst_registry_get_default(), GST_TYPE_ELEMENT_FACTORY );
+    while( features )
+        {
+        GstPluginFeature* feature = (GstPluginFeature*)features->data;        
+        HBufC* tempBuf = ConvertCharToDescL( gst_plugin_feature_get_name( 
+                feature ) );
+        CleanupStack::PushL( tempBuf );
+        array->AppendL( *tempBuf );
+        CleanupStack::PopAndDestroy( tempBuf );
+        features = features->next;
+        }
+
+    CleanupStack::Pop( array );
+    return array;
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::VideoInfo
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+EXPORT_C const TDesC& CUpnpGstWrapper::VideoInfo()
+    {
+    __FUNC_LOG;
+    
+    if( iVideoInfo )
+        {
+        return *iVideoInfo;
+        }
+    else
+        {
+        return KNullDesC;
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::AudioInfo
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+EXPORT_C const TDesC& CUpnpGstWrapper::AudioInfo()
+    {
+    __FUNC_LOG;
+    
+    if( iAudioInfo )
+        {
+        return *iAudioInfo;
+        }
+    else
+        {
+        return KNullDesC;
+        }
+    }
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::Position
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+EXPORT_C TInt64 CUpnpGstWrapper::Position()
+    {
+    __FUNC_LOG;
+    
+    GstFormat fmt = GST_FORMAT_TIME;
+    TInt64 position = KErrNotFound;
+    
+    //fetch position from pipeline (not from the demuxer)
+    GstElement* pipeline = iPimpl->Pipeline();
+    
+    if( pipeline )
+        {
+        gst_element_query_position( pipeline, &fmt, &position );
+        }
+    
+    return position;
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::Duration
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+EXPORT_C TInt64 CUpnpGstWrapper::Duration()
+    {
+    __FUNC_LOG;
+     
+     if( iDuration < 0 )
+         {
+         GstFormat fmt = GST_FORMAT_TIME;
+         if( iPimpl->Demuxer() )
+             {
+             //fetch duration from demuxer
+             gst_element_query_duration( iPimpl->Demuxer(), &fmt, &iDuration );
+             } 
+         else if( iPimpl->Pipeline() )
+             {
+             //backup plan: not so good choice as demuxer
+             gst_element_query_position( iPimpl->Pipeline(), &fmt, &iDuration );
+             }
+         }
+     
+     return iDuration;
+    }
+
+EXPORT_C TAny* CUpnpGstWrapper::PipelinePtr()
+    {
+    return iPimpl->Pipeline();
+    }
+
+EXPORT_C void CUpnpGstWrapper::InitAppsinkL()
+    {
+    iPimpl->InitAppSinkL();
+    }
+   
+EXPORT_C TBool CUpnpGstWrapper::PullBufferL( TPtr8& aPointer,TInt aMaxBytesLength, RBuf8* aBuf )
+    {
+    return iPimpl->PullBufferL(aPointer,aMaxBytesLength,aBuf);
+    }
+   
+EXPORT_C TInt CUpnpGstWrapper::TranscodedBytes()
+    {
+    return iPimpl->TranscodedBytes();
+    }
+       
+EXPORT_C void CUpnpGstWrapper::SetTranscodedFileSize( TInt aSize )
+    {
+    iFileSize = aSize;
+    }
+
+EXPORT_C TInt CUpnpGstWrapper::TranscodedFileSize()
+    {
+    return iFileSize;
+    }
+
+EXPORT_C void CUpnpGstWrapper::SetPipelineL( const TDesC8& aPipeline )
+    {
+    delete iPipeline;
+    iPipeline = NULL;
+    iPipeline = aPipeline.AllocL();
+    }
+     
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::SetErrorMsgL
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+void CUpnpGstWrapper::SetErrorMsgL( const TDesC& aErrorMsg )
+    {
+    __FUNC_LOG;     
+    
+    delete iErrorMsg; iErrorMsg = NULL;
+    iErrorMsg = aErrorMsg.AllocL();
+    
+    __LOG1( "CUpnpGstWrapper::SetErrorMsgL Error desc: %S", iErrorMsg );
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::SetErrorMsg
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+void CUpnpGstWrapper::SetErrorMsg( HBufC* const aErrorMsg )
+    {
+    delete iErrorMsg; iErrorMsg = NULL;
+    iErrorMsg = aErrorMsg;
+    
+    __LOG1( "CUpnpGstWrapper::SetErrorMsg Error desc: %S", iErrorMsg );
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::ConvertCharToDescL
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+HBufC* CUpnpGstWrapper::ConvertCharToDescL( const char* aString )
+    {
+    __FUNC_LOG;
+    
+    HBufC* desc = NULL;
+    TPtrC8 tempPtr8( (TUint8*)(aString) );
+    desc = HBufC::NewL( tempPtr8.Length() );
+    desc->Des().Copy( tempPtr8 );
+    
+    return desc; 
+    }
+
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::ConvertDescToCharL
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+char* CUpnpGstWrapper::ConvertDescToCharL( const TDesC8& aDescriptor )
+    {
+    __FUNC_LOG;
+    
+    TUint length = aDescriptor.Length();
+    char* string = (char*)User::AllocL( length + 1 );
+    Mem::Copy((char*)string, aDescriptor.Ptr(), length );
+    string[length] = 0; //null terminated
+    return string;
+    }
+
+void CUpnpGstWrapper::SendEventToObsevers( Gst::TEvent aEvent )
+    {
+    __FUNC_LOG;
+    
+    TInt observerCount = iObservers.Count();
+    for( TInt i = 0; i < observerCount; i++ )
+        {
+        iObservers[i]->HandleGstEvent( aEvent );
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::RunL
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+void CUpnpGstWrapper::RunL()
+    {    
+    __FUNC_LOG;
+    if( iStatus.Int() >= KErrNone )
+        {
+        TBool completed = EFalse;   
+        
+        iMutex.Wait();
+        
+        TInt eventQueueCount = iEventQueue.Count();
+        for( TInt i = 0; i < eventQueueCount; i++ )
+            {
+            Gst::TEvent event = iEventQueue[i];
+            SendEventToObsevers( event );
+            if( event == Gst::EEOS || event == Gst::EError )
+                {
+                __LOG1( "CUpnpGstWrapper::RunL Completed: %d (2=Error, 3=EOS)",
+                        event);
+                completed = ETrue;
+                }
+            }
+        iEventQueue.Reset();
+        
+        if( !completed )
+            {        
+            iStatus = KRequestPending;
+            SetActive();
+            }        
+        iMutex.Signal();
+        }
+    }
+
+// --------------------------------------------------------------------------
+// CUpnpGstWrapper::DoCancel
+// See upnpgstwrapper.h
+//---------------------------------------------------------------------------
+void CUpnpGstWrapper::DoCancel()
+    {  
+    __FUNC_LOG;
+    }
+
+// end of file