upnpavcontroller/upnpavcontrollerhelper/src/upnptranscodehelper.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) 2009 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 transcode helper
*
*/
#include <bautils.h> //hack

#include <upnpitem.h>
#include <upnpstring.h>
#include <upnpdlnaprotocolinfo.h>

#include <upnpgstwrapper.h>
#include <upnprenderercfg.h>
#include <upnprenderercfgparser.h>

#include "upnpitemutility.h"
#include "upnpavdevice.h"

#include "upnptranscodehelper.h"

_LIT( KComponentLogfile, "upnptranscodehelper.txt");
#include "upnplog.h"

#define __FUNC_LOG __LOG8_1( "%s", __PRETTY_FUNCTION__ );

_LIT( KRendererCfgFile, "C:\\data\\UpnpCfgs.xml" );

_LIT( KTranscodedFlag, "?transcoded=true" );

_LIT8( KSymbianDirDelimiter, "\\" );
_LIT8( KLinuxDirDelimiter, "/" );
_LIT8( KProtocolInfo, "protocolInfo" );
_LIT8( KRes, "res" );

// These are for Transport Stream muxer checking (from pipeline)
_LIT8( KMpegTsMux, "mpegtsmux" );
_LIT8( KFFMuxMpegTs, "ffmux_mpegts" );

// These are for Transport Stream support checking
_LIT8( KTsProtocolInfoIdentifier, "_TS_" );
_LIT8( KAllMpegMimeTypes, "video/mpeg:*" );

// This is for MP4 support checking 
_LIT8( KMp4MimeType, "video/mp4" );

// The pipeline is currently hardcoded (except input file)
_LIT8( KDefaultPipelineFmt, 
"mpegtsmux output-buf-size=0x7FCCC name=mux ! appsink max-buffers=10 name=sinkbuffer filesrc \
location=%S ! qtdemux name=demux ! queue max-size-bytes=0xAAAA ! mux. demux. ! \
queue max-size-bytes=0xAAAA ! mux.");

const TInt KLocationFmtTagLen = 2;

CUpnpTranscodeHelper* CUpnpTranscodeHelper::NewL()
    {
    __FUNC_LOG;
    
    CUpnpTranscodeHelper* self = new ( ELeave ) CUpnpTranscodeHelper();
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

CUpnpTranscodeHelper::~CUpnpTranscodeHelper()
    {
    __FUNC_LOG;
    
    iGstWrapper->RemoveObserver( *this );
    iGstWrapper->Close();
    iFs.Close();
    }

void CUpnpTranscodeHelper::PreDefinedCfgL( const CUpnpAVDevice& aRenderer, 
        CUpnpItem& aUpnpItem, HBufC8*& aPipeline,
        HBufC8*& aProtocolInfo )
    {
    __FUNC_LOG;
    
    //fetch mimetype and filename from original resource
    RUPnPElementsArray resources;
    CleanupClosePushL( resources );
    UPnPItemUtility::GetResElements( aUpnpItem, resources );
    TInt resourcesCount = resources.Count();
    if ( resourcesCount == 0 )
        {
        User::Leave( KErrNotFound );
        }         

    const CUpnpAttribute& originalProtocolInfoDesc = 
             UPnPItemUtility::FindAttributeByNameL( 
             *resources[0], KProtocolInfo ); 
    
    CUpnpDlnaProtocolInfo* originalProtocolInfo = 
            CUpnpDlnaProtocolInfo::NewL( originalProtocolInfoDesc.Value() );
    CleanupStack::PushL( originalProtocolInfo );            
    
    //create config for renderer
    CUpnpRendererCfg* config = CUpnpRendererCfg::NewLC( KRendererCfgFile, 
            aRenderer.ModelName(), originalProtocolInfo->ThirdField() );    
    
    CUpnpRendererCfgParser* parser = CUpnpRendererCfgParser::NewLC();   
        
    parser->ParseRendererCfgL( *config );
    
    HBufC8* srcFileName8 = PathToLinuxSyntaxL( resources[0]->FilePath() );
    CleanupStack::PushL( srcFileName8 );
    
    HBufC8* pipeline = HBufC8::NewLC( config->Pipeline().Length() + 
            srcFileName8->Length() - KLocationFmtTagLen );    
    pipeline->Des().AppendFormat( config->Pipeline(), srcFileName8 );
      
    HBufC8* protocolInfo = config->ProtocolInfo().AllocL();

    RFile file;
    User::LeaveIfError( file.Open(iFs,resources[0]->FilePath(),EFileRead) );
    CleanupClosePushL(file);
    TInt size(KErrNotFound);
    User::LeaveIfError( file.Size(size) );
    iGstWrapper->SetTranscodedFileSize( config->SizeMultiplier()*size );
    CleanupStack::PopAndDestroy(&file);
    
    CleanupStack::Pop( pipeline );
    CleanupStack::PopAndDestroy( srcFileName8 );
    CleanupStack::PopAndDestroy( parser );
    CleanupStack::PopAndDestroy( config );
    CleanupStack::PopAndDestroy( originalProtocolInfo );
    CleanupStack::PopAndDestroy( &resources );
    
    aPipeline = pipeline;
    aProtocolInfo = protocolInfo;
    }

HBufC8* CUpnpTranscodeHelper::PipelineL( CUpnpItem& aUpnpItem, 
        const CDesC8Array& aRendererProtocolInfos )
    {
    __FUNC_LOG;
    
    HBufC8* pipeline = NULL;
    TBool protocolMatch = EFalse;
    TContainerType srcContainerType = EUnknownContainer;
    TContainerType destContainerType = EUnknownContainer;

    //Get resources from the current upnp item
    RUPnPElementsArray resources;
    CleanupClosePushL( resources );
    UPnPItemUtility::GetResElements( aUpnpItem, resources );
    TInt resourcesCount = resources.Count();
    if ( resourcesCount == 0 )
        {
        User::Leave( KErrNotFound );
        }    

    //Check if suitable source container is found from 1st resource
    const CUpnpAttribute& originalProtocolInfo = 
            UPnPItemUtility::FindAttributeByNameL( 
            *resources[0], KProtocolInfo ); 
            
    if( originalProtocolInfo.Value().Find( KMp4MimeType ) )
        {
        srcContainerType = EMp4Container; 
        }
    else
        {
        User::Leave( KErrNotSupported );
        }        

    //Loop through all protocol infos from renderer and try to find suitable        
    TInt rendererProtocolInfoCount = aRendererProtocolInfos.Count(); 
    for( TInt i = 0; i < rendererProtocolInfoCount && !protocolMatch; i++ )
        {
        for( TInt j = 0; j < resourcesCount && !protocolMatch; j++ )
            {
            const TDesC8& protocolInfo = 
            UPnPItemUtility::FindAttributeByNameL( 
                    *resources[j], KProtocolInfo ).Value();        

            const TDesC8& rendererProtocolInfo = 
            aRendererProtocolInfos.MdcaPoint(i);

            if( rendererProtocolInfo.Find( protocolInfo ) != KErrNotFound )
                {                                
                protocolMatch = ETrue;
                //breaks both 'for' loops here    
                }

            //Check if protocol info is supported 
            if( destContainerType == EUnknownContainer &&
                ( rendererProtocolInfo.Find( 
                      KTsProtocolInfoIdentifier ) != KErrNotFound || 
                  rendererProtocolInfo.Find(
                      KAllMpegMimeTypes ) != KErrNotFound ) ) 
                {
                destContainerType = ETsContainer; 
                }
            //TODO: add here other supported containers (muxers) as well

            }        
        }

    if( !protocolMatch )
        {               
        HBufC8* filePath = PathToLinuxSyntaxL( resources[0]->FilePath() );     
        CleanupStack::PushL( filePath );            
        pipeline = GeneratePipelineL( *filePath, destContainerType );        
        CleanupStack::PopAndDestroy( filePath );                
        }

    CleanupStack::PopAndDestroy( &resources );

    return pipeline;
    }

void CUpnpTranscodeHelper::ReplaceResourceL( CUpnpItem& aUpnpItem, 
                                             const TDesC8& aProtocolInfo )
    {
    RUPnPElementsArray& elements = const_cast<RUPnPElementsArray&>(
                    aUpnpItem.GetElements());

    for( TInt resIndex(0); resIndex < elements.Count(); ++resIndex )
        {
        if( elements[resIndex]->Name() == KRes )
            {
            HBufC* tmp = HBufC::NewLC( 
                    elements[resIndex]->FilePath().Length() +
                    KTranscodedFlag().Length() );
            tmp->Des().Append( elements[resIndex]->FilePath() );
            tmp->Des().Append( KTranscodedFlag() );
            elements.Remove( resIndex );
            aUpnpItem.AddResourceL( *tmp, aProtocolInfo );
            CleanupStack::PopAndDestroy( tmp );
            break;
            }
        }
    }

void CUpnpTranscodeHelper::TranscodeL( const TDesC8& aPipeline )
    {
    __FUNC_LOG;
    
    //iGstWrapper->StartL( aPipeline ); 
    iGstWrapper->SetPipelineL( aPipeline );
    
    //should we wait here until we get pipeline playing state notification
    //or error ;)
    }


void CUpnpTranscodeHelper::AddValidResElementL( 
        CUpnpItem& aUpnpItem, const TDesC8& aPipeline, 
        const CDesC8Array& aRendererProtocolInfos )
    {
    __FUNC_LOG;
    
    TBool suitableFound = EFalse;
    const TDesC8* protocolInfoIdentifier = NULL;

    //TODO: Add also other supported containers (muxers)
    if( aPipeline.Find( KMpegTsMux ) != KErrNotFound || 
        aPipeline.Find( KFFMuxMpegTs ) != KErrNotFound )        
        {
        protocolInfoIdentifier = &KTsProtocolInfoIdentifier(); 
        }
    else
        {
        User::Leave( KErrNotSupported );
        }

    TInt rendererProtocolInfoCount = aRendererProtocolInfos.Count();

    if( rendererProtocolInfoCount == 0 )
        {
        User::Leave(KErrNotFound);
        }

    //Get resources from the current upnp item
    RUPnPElementsArray resources;
    CleanupClosePushL( resources );
    UPnPItemUtility::GetResElements( aUpnpItem, resources );
    TInt resourcesCount = resources.Count();
    if ( resourcesCount == 0 )
        {
        User::Leave( KErrNotFound );
        }  
    
    for( TInt i = 0; i < rendererProtocolInfoCount && !suitableFound; i++ )
        {           
        TPtrC8 rendererProtocolInfo = aRendererProtocolInfos.MdcaPoint(i);
        if( rendererProtocolInfo.Find( *protocolInfoIdentifier ) )
            {
            //TODO: Add only suitable ones -- now adds all protocol infos
            //      from renderer, which contains _TS_ sub-string
            const TDesC& originalFilePath = resources[0]->FilePath();
            HBufC* transcodedFilePath = HBufC::NewL( 
                    originalFilePath.Length() + KTranscodedFlag().Length() );
            CleanupStack::PushL( transcodedFilePath );
            
            
            transcodedFilePath->Des().Append( originalFilePath );
            transcodedFilePath->Des().Append( KTranscodedFlag );
        
            aUpnpItem.AddResourceL( *transcodedFilePath, 
                    rendererProtocolInfo );
            
            CleanupStack::PopAndDestroy( transcodedFilePath );
            
            suitableFound = ETrue;
            }                
        }

    CleanupStack::PopAndDestroy( &resources );
    
    if( !suitableFound )
        {
        User::Leave( KErrNotFound );
        }    
    else
        {
        // delete orig.
        RUPnPElementsArray& elements = const_cast<RUPnPElementsArray&>(
                aUpnpItem.GetElements());

        for( TInt resIndex(0); resIndex < elements.Count(); ++resIndex )
            {
            if( elements[resIndex]->Name() == KRes )
                {
                elements.Remove(resIndex);
                break;
                }
            }
        }
    }

CUpnpTranscodeHelper::CUpnpTranscodeHelper()
    {
    __FUNC_LOG;    
    }

void CUpnpTranscodeHelper::ConstructL()
    {
    __FUNC_LOG;    
    
    iGstWrapper = CUpnpGstWrapper::GetInstanceL();    
    iGstWrapper->SetObserverL( *this );
    User::LeaveIfError( iFs.Connect() );
    }

HBufC8* CUpnpTranscodeHelper::GeneratePipelineL( 
        const TDesC8& aOriginalFilePath, 
        TContainerType aDestContainerType )
    {    
    __FUNC_LOG;
    
    if( aDestContainerType == EUnknownContainer )
        {
        User::Leave( KErrNotSupported );
        }    

    HBufC8* pipeline = NULL;     
    
    switch( aDestContainerType )
        {
        case ETsContainer:
            {
            //TODO ??
            pipeline = HBufC8::NewL( KDefaultPipelineFmt().Length() - 
                    KLocationFmtTagLen + aOriginalFilePath.Length() );            
            pipeline->Des().AppendFormat( KDefaultPipelineFmt, 
                    &aOriginalFilePath );
            break;
            }
        default:
            {
            __ASSERT_ALWAYS( 0, User::Invariant() );
            }
        }
    
    return pipeline; 
    }

HBufC8* CUpnpTranscodeHelper::PathToLinuxSyntaxL( 
        const TDesC& aOriginalPath )
    {
    __FUNC_LOG;
    
    HBufC8* newFilePath = UpnpString::FromUnicodeL( aOriginalPath );
     
    TInt index = 0;
    while( index >= 0 )
        {
        index = newFilePath->Find( KSymbianDirDelimiter );
        if( index >= 0 )
            {
            newFilePath->Des().Replace( index, 
                    KSymbianDirDelimiter().Length(), KLinuxDirDelimiter );
            }
        }    
    
    return newFilePath;
    }

void CUpnpTranscodeHelper::HandleGstEvent( Gst::TEvent aEvent )
    {
    __FUNC_LOG;
    
    if( aEvent == Gst::EError )
        {
        __LOG1( "CUpnpTranscodeHelper::HandleGstEvent ErrorMsg: %S",
                iGstWrapper->ErrorMsg() );
        __ASSERT( EFalse, __FILE__, __LINE__ );
        }
    }


// end of file