upnpsharing/upnpgstwrapper/src/upnpgstwrapper.cpp
author Sampo Huttunen <sampo.huttunen@nokia.com>
Wed, 24 Nov 2010 09:39:46 +0200
branchIOP_Improvements
changeset 45 a6c41ca11adf
parent 40 08b5eae9f9ff
permissions -rw-r--r--
Updated the SIS package, there was some BC issue with the earlier version. Also updated the platform UID to S^3 version.

/*
* 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