multimediacommsengine/mmceshared/src/mcecomavccodec.cpp
author William Roberts <williamr@symbian.org>
Mon, 08 Mar 2010 21:43:55 +0000
branchCompilerCompatibility
changeset 4 8b36925e8e80
parent 0 1bce908db942
permissions -rw-r--r--
Create CompilerCompatibility branch

/*
* Copyright (c) 2006 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:    
*
*/




#include "mcecomavccodec.h"

#ifdef MCE_COMMON_SERVER_SIDE

#include <mmcccodecinformationfactory.h>
#include <mmcccodecinformation.h>

#include "mcecommediastream.h"

#endif


// CONSTANTS
_LIT8( KMceAvcProfileLevelIdFmtp, "profile-level-id=" );
_LIT8( KMceAvcProfileLevel1, "42800A" );
const TInt KMceAvcProfileLevelIdValLen = 6;

_LIT8( KMceAvcPacketizationModeFmtp, "packetization-mode=" );
_LIT8( KMceAvcPacketizationModeSingleNal, "0" );
const TInt KMceAvcPacketizationModeValLen = 1;

#ifdef MCE_COMMON_SERVER_SIDE

_LIT8( KMceAvcProfileLevelOnly1, "0A" );
const TInt KMceAvcProfileLevelValLen = 2;
const TInt KMceAvcProfileLevelValOffset = 4;

_LIT8( KMceAvcProfileIopDefault, "80" );
const TInt KMceAvcProfileIopValLen = 2;
const TInt KMceAvcProfileIopValOffset = 2;

_LIT8( KMceAvcSpropParameterSetsFmtp, "sprop-parameter-sets=");
const TChar KMceAvcFmtpParameterSeparator = ';';
const TChar KMceAvcFtmpParameterSpace = ' ';

#endif

// ============================ MEMBER FUNCTIONS ===============================


// -----------------------------------------------------------------------------
// CMceComAvcCodec::NewL
// -----------------------------------------------------------------------------
//
CMceComAvcCodec* CMceComAvcCodec::NewL( TBuf8<KMceMaxSdpNameLength> aSdpName )
    {
    CMceComAvcCodec* self = NewLC( aSdpName );
    CleanupStack::Pop( self );
    return self;
    
    }

// -----------------------------------------------------------------------------
// CMceComAvcCodec::NewLC
// -----------------------------------------------------------------------------
//
CMceComAvcCodec* CMceComAvcCodec::NewLC( TBuf8<KMceMaxSdpNameLength> aSdpName )
    {
    CMceComAvcCodec* self = new (ELeave) CMceComAvcCodec();
    CleanupStack::PushL( self );
    self->ConstructL( aSdpName );
    return self;
    }


// -----------------------------------------------------------------------------
// CMceComAvcCodec::~CMceComAvcCodec
// -----------------------------------------------------------------------------
//
CMceComAvcCodec::~CMceComAvcCodec()
    {
    }
    

// -----------------------------------------------------------------------------
// CMceComAvcCodec::CMceComAvcCodec
// -----------------------------------------------------------------------------
//
CMceComAvcCodec::CMceComAvcCodec()
    :CMceComVideoCodec()
    {
    iCodecMode = KMceAvcModeSingleNal;
    iClockRate = KMceAvcDefaultFrameRate;
    iFrameRate = KMceAvcDefaultFrameRate;
    iResolutionWidth = KMceAvcDefaultFrameWidth;
    iResolutionHeight = KMceAvcDefaultFrameHeight;
    iBitrate = KMceAvcLevel1BitrateBps;
	iMaxBitRate = KMceAvcLevel1BitrateBps;
	iAllowedBitrates = KMceAvcDefaultAllowedBitrate;
    iFrameSize = KMceAvcDefaultFrameSize;
    // Defaults should be defined in MCE API
    const TUint8 KMceDefaultAvcPayloadType = 98;
	iPayloadType = KMceDefaultAvcPayloadType;
    
    iReceiveFrameRate = KMceAvcDefaultFrameRate;
    }


// -----------------------------------------------------------------------------
// CMceComAvcCodec::ConstructL
// -----------------------------------------------------------------------------
//
CMceComVideoCodec* CMceComAvcCodec::CloneL()
    {
    CMceComAvcCodec* copy = new (ELeave) CMceComAvcCodec();
    CleanupStack::PushL( copy );
    copy->ConstructL( *this );
    CleanupStack::Pop( copy );
    return copy;
    
    }


// -----------------------------------------------------------------------------
// CMceComAvcCodec::ConstructL
// -----------------------------------------------------------------------------
//
void CMceComAvcCodec::ConstructL( TBuf8<KMceMaxSdpNameLength> aSdpName )
    {
    CMceComVideoCodec::ConstructL( aSdpName );
    
    }


// -----------------------------------------------------------------------------
// CMceComAvcCodec::ConstructL
// -----------------------------------------------------------------------------
//
void CMceComAvcCodec::ConstructL( CMceComAvcCodec& aCodec )
    {
    CMceComVideoCodec::ConstructL( aCodec );
    }
    
// -----------------------------------------------------------------------------
// CMceComAvcCodec::SetBitrate
// -----------------------------------------------------------------------------
//
TInt CMceComAvcCodec::SetBitrate( TUint aBitrate )
    {
	return CMceComCodec::SetBitrate( aBitrate );
    }
    
// -----------------------------------------------------------------------------
// CMceComAvcCodec::SetAllowedBitrates
// -----------------------------------------------------------------------------
//
TInt CMceComAvcCodec::SetAllowedBitrates( TUint aBitrates )
    {
    TInt error = KErrNone;
    
    //Confirm that the bitrate mask is valid
    //I.e. after all the valid bitrates are set to zero the value should be zero
    if ( !aBitrates || 
        ( aBitrates >> 16 ) > 0 ) 
        {
        error = KErrArgument;
        }
    else
        {
        const TUint KMceOtherInfoMask = 0xFF00;
        
        if ( aBitrates == KMceAllowedAvcBitrateAll )
            {
            // Start using recommended high bitrate
            aBitrates = KMceAvcAllowedBitrateRecommendedHigh;
            }
            
        CMceComCodec::SetAllowedBitrates( KMceAllowedAvcBitrateAll );
        iAllowedBitrates &= aBitrates;
        TUint maxBitrate = GetMaxBitRate();
        CMceComVideoCodec::SetMaxBitrate( maxBitrate );
        if ( iBitrate > maxBitrate )
            {
            error = CMceComCodec::SetBitrate( maxBitrate );
            } 
            
        // Update also other avc codec information stored inside
        // allowed bitrates mask
        iAllowedBitrates |= ( aBitrates & KMceOtherInfoMask );
        }
    
    return error;
    }



// -----------------------------------------------------------------------------
// CMceComAvcCodec::GetMaxBitRate
// -----------------------------------------------------------------------------
//
TUint CMceComAvcCodec::GetMaxBitRate()
    {
    TUint maxBitrate( 0 );
    if( iAllowedBitrates & KMceAvcBitrateLevel1 )
        {
        maxBitrate = KMceAvcLevel1BitrateBps;
        }
    else if( iAllowedBitrates & KMceAvcBitrateLevel1b )
        {
        maxBitrate = KMceAvcLevel1bBitrateBps;
        }
    else if( iAllowedBitrates & KMceAvcBitrateLevel1_1 )
        {
        maxBitrate = KMceAvcLevel11BitrateBps;
        }
    else if( iAllowedBitrates & KMceAvcBitrateLevel1_2 )
        {
        maxBitrate = KMceAvcLevel12BitrateBps;
        }
    else if( iAllowedBitrates & KMceAvcBitrateLevel1_3 )
        {
        maxBitrate = KMceAvcLevel13BitrateBps;
        }
    else if( iAllowedBitrates & KMceAvcBitrateLevel2 )
        {
        maxBitrate = KMceAvcLevel2BitrateBps;
        }
    else
        {
        // NOP
        }
        
    return maxBitrate;
    }

// -----------------------------------------------------------------------------
// CMceComAvcCodec::SetMaxBitrate
// -----------------------------------------------------------------------------
//
TInt CMceComAvcCodec::SetMaxBitrate( TUint aMaxBitrate )
    {
    TInt status = KErrNone;
	switch( aMaxBitrate )
	    {
	    case KMceAvcLevel1BitrateBps:
	        {
	        status = SetAllowedBitrates( KMceAvcCodecProfileIdBaseline |
	                                     KMceAvcCodecProfileIopConstraintSet | 
	                                     KMceAvcBitrateLevel1 );
	        break;
	        }
	    case KMceAvcLevel1bBitrateBps:
	        {
	        status = SetAllowedBitrates( KMceAvcCodecProfileIdBaseline |
	                                     KMceAvcCodecProfileIopConstraintSet | 
	                                     KMceAvcBitrateLevel1b );
	        break;
	        }
	    case KMceAvcLevel11BitrateBps:
	        {
	        status = SetAllowedBitrates( KMceAvcCodecProfileIdBaseline |
	                                     KMceAvcCodecProfileIopConstraintSet | 
	                                     KMceAvcBitrateLevel1_1 );
	        break;
	        }
	    case KMceAvcLevel12BitrateBps:
	        {
	        status = SetAllowedBitrates( KMceAvcCodecProfileIdBaseline |
	                                     KMceAvcCodecProfileIopConstraintSet | 
	                                     KMceAvcBitrateLevel1_2 );
	        break;
	        }
	    case KMceAvcLevel13BitrateBps:
	        {
	        status = SetAllowedBitrates( KMceAvcCodecProfileIdBaseline |
	                                     KMceAvcCodecProfileIopConstraintSet | 
	                                     KMceAvcBitrateLevel1_3 );
	        break;
	        }
	    case KMceAvcLevel2BitrateBps:
	        {
	        status = SetAllowedBitrates( KMceAvcCodecProfileIdBaseline |
	                                     KMceAvcCodecProfileIopConstraintSet | 
	                                     KMceAvcBitrateLevel2 );
	        break;
	        }
        default:
            {
            status = KErrNotSupported;
            break;
            }
	    }

    return status;
    
    }

// -----------------------------------------------------------------------------
// CMceComAvcCodec::ResolveAllowedBitrates
// -----------------------------------------------------------------------------
//
TInt CMceComAvcCodec::ResolveAllowedBitrates( 
    TUint aBitrate, TUint& aAllowedBitrates )
    {
    TInt retVal( KErrNone );
    
    if ( aBitrate <= KMceAvcLevel1BitrateBps )
        {
        aAllowedBitrates =  KMceAvcCodecProfileIdBaseline | 
	                        KMceAvcCodecProfileIopConstraintSet | 
	                        KMceAvcBitrateLevel1;
        }
    else if ( aBitrate <= KMceAvcLevel1bBitrateBps )
        {
        aAllowedBitrates =  KMceAvcCodecProfileIdBaseline | 
	                        KMceAvcCodecProfileIopConstraintSet | 
	                        KMceAvcBitrateLevel1b;
        }
    else if ( aBitrate <= KMceAvcLevel11BitrateBps )
        {
        aAllowedBitrates =  KMceAvcCodecProfileIdBaseline | 
	                        KMceAvcCodecProfileIopConstraintSet | 
	                        KMceAvcBitrateLevel1_1;
        }
    else if ( aBitrate <= KMceAvcLevel12BitrateBps )
        {
        aAllowedBitrates =  KMceAvcCodecProfileIdBaseline | 
	                        KMceAvcCodecProfileIopConstraintSet | 
	                        KMceAvcBitrateLevel1_2;
        }
    else if ( aBitrate <= KMceAvcLevel13BitrateBps )
        {
        aAllowedBitrates =  KMceAvcCodecProfileIdBaseline | 
	                        KMceAvcCodecProfileIopConstraintSet | 
	                        KMceAvcBitrateLevel1_3;
        }
    else if ( aBitrate <= KMceAvcLevel2BitrateBps )
        {
        aAllowedBitrates =  KMceAvcCodecProfileIdBaseline | 
	                        KMceAvcCodecProfileIopConstraintSet | 
	                        KMceAvcBitrateLevel2;
        }    
    else 
        {
        retVal = KErrNotSupported;
        }
        
    return retVal;
    }

// -----------------------------------------------------------------------------
// CMceComAvcCodec::Decodes
// For AVC, fmtp value contains profile level and packetization mode 
// information which need to be taken in count (same codec cannot be
// used for different levels or packetization modes). If less strict matching
// is used, profile level is ignored but packetization mode is still checked
// -----------------------------------------------------------------------------
//
TBool CMceComAvcCodec::Decodes( 
    const TDesC8& aRtpmapValue, 
    const TDesC8& aFmtpValue,
    TBool aIsStrictMatch )
    {
    TBool decodes = CMceComCodec::Decodes( aRtpmapValue, aFmtpValue, aIsStrictMatch );
    if ( decodes )
        {
        TPtrC8 existingFmtpVal = FmtpAttr();
        
        TPtrC8 packetizationMode = ParsePacketizationMode( aFmtpValue );
        TPtrC8 existingPacketizationMode = ParsePacketizationMode( existingFmtpVal );
        decodes = ( packetizationMode.CompareF( existingPacketizationMode ) == 0 );

        if ( decodes && aIsStrictMatch )
            {
            TPtrC8 profileLevelId = ParseProfileLevelId( aFmtpValue );
                
            TPtrC8 existingProfileLevelId = ParseProfileLevelId( existingFmtpVal );
            decodes = ( profileLevelId.CompareF( existingProfileLevelId ) == 0 );
            }
        }
    return decodes;
    }

// -----------------------------------------------------------------------------
// CMceComAvcCodec::ParseProfileLevelId
// -----------------------------------------------------------------------------
//
TPtrC8 CMceComAvcCodec::ParseProfileLevelId( const TDesC8& aFmtpValue )
    {
    // Non-existing profile-level-id means level 1  
    TPtrC8 profileLevelIdVal = 
        GetFmtpAttributeValuePart( aFmtpValue, 
                                   KMceAvcProfileLevelIdFmtp(), 
                                   KMceAvcProfileLevelIdValLen, 
                                   KMceAvcProfileLevel1 );
                               
    return profileLevelIdVal;
    }

// -----------------------------------------------------------------------------
// CMceComAvcCodec::ParseProfileLevelIdValPart
// -----------------------------------------------------------------------------
//
TPtrC8 CMceComAvcCodec::ParseProfileLevelIdValPart( 
    const TDesC8& aFmtpValue,
    TInt aValPartOffset,
    TInt aValPartLen,
    const TDesC8& aDefaultVal )
    {
    TPtrC8 profileLevelVal( aDefaultVal );
    
    TPtrC8 profileLevelIdVal = ParseProfileLevelId( aFmtpValue );
    if ( profileLevelIdVal.Length() >= aValPartOffset + aValPartLen )
        {
        // Take just value part
        profileLevelVal.Set( profileLevelIdVal.Mid( aValPartOffset, aValPartLen ) );
        }

    return profileLevelVal;
    }

// -----------------------------------------------------------------------------
// CMceComAvcCodec::ReplaceProfileLevelIdValuePart
// -----------------------------------------------------------------------------
//
TInt CMceComAvcCodec::ReplaceProfileLevelIdValuePart( 
    TDes8& aModifiedFmtp, 
    const TDesC8& aReplacement,
    TInt aValPartOffset )
    {
    TInt err( KErrArgument );
    
    TInt startIndex = aModifiedFmtp.FindF( KMceAvcProfileLevelIdFmtp() );
    startIndex = ( startIndex != KErrNotFound ) ? 
        ( startIndex + KMceAvcProfileLevelIdFmtp().Length() ) : startIndex;
    if ( startIndex != KErrNotFound && 
       ( startIndex + aValPartOffset + aReplacement.Length() ) <= aModifiedFmtp.Length() )
        {
        aModifiedFmtp.Replace( 
            startIndex + aValPartOffset, 
            aReplacement.Length(), 
            aReplacement );
        err = KErrNone;
        }
    return err;
    }
    
// -----------------------------------------------------------------------------
// CMceComAvcCodec::ParsePacketizationMode
// -----------------------------------------------------------------------------
//
TPtrC8 CMceComAvcCodec::ParsePacketizationMode( const TDesC8& aFmtpValue )
    {
    // Non-existing packetization mode means single nal mode
    TPtrC8 packetizationModeVal =
        GetFmtpAttributeValuePart( aFmtpValue, 
                                   KMceAvcPacketizationModeFmtp(), 
                                   KMceAvcPacketizationModeValLen, 
                                   KMceAvcPacketizationModeSingleNal );
   
    return packetizationModeVal;
    }

// -----------------------------------------------------------------------------
// CMceComAvcCodec::GetFmtpAttributeValuePart
// -----------------------------------------------------------------------------
//
TPtrC8 CMceComAvcCodec::GetFmtpAttributeValuePart( 
    const TDesC8& aFmtpValue,
    const TDesC8& aFmtpAttributeName, 
    TInt aValLen, 
    const TDesC8& aDefaultVal )
    {
    TPtrC8 outputValuePart( aDefaultVal );
    
    TInt valPos = aFmtpValue.FindF( aFmtpAttributeName );
    valPos = ( valPos != KErrNotFound ) ? 
                ( valPos + aFmtpAttributeName.Length() ) : valPos;
    if ( valPos != KErrNotFound && 
       ( valPos + aValLen ) <= aFmtpValue.Length() )
        {
        outputValuePart.Set( aFmtpValue.Mid( valPos, aValLen ) );
        }
    return outputValuePart;
    }
    
#ifdef MCE_COMMON_SERVER_SIDE

// -----------------------------------------------------------------------------
// CMceComAvcCodec::SetFmtpProposalL
// -----------------------------------------------------------------------------
//
TBool CMceComAvcCodec::SetFmtpProposalL( const TDesC8& aFmtpProposal )
    {
    HBufC8* proposal = aFmtpProposal.AllocL();
    delete iFmtpProposal;
    iFmtpProposal = proposal;
    
    CMccCodecInformationFactory* factory = CMccCodecInformationFactory::NewL();
    CleanupStack::PushL( factory );
    CMccCodecInformation* codec = 
        factory->CreateCodecInformationL( iSdpName );
    CleanupStack::PushL( codec );
    
    TRAPD( err, codec->SetFmtpAttrL( *iFmtpProposal ) );
    if ( err == KErrNoMemory )
        {
        User::Leave( err );
        }
    
    CleanupStack::PopAndDestroy( codec );
    CleanupStack::PopAndDestroy( factory );
    
    return ( err == KErrNone );
    }

// -----------------------------------------------------------------------------
// CMceComAvcCodec::SetFmtpFromIncomingSdpL
// -----------------------------------------------------------------------------
//
void CMceComAvcCodec::SetFmtpFromIncomingSdpL( 
    const TDesC8& aFmtpAttribute, 
    TBool aRoleAnswerer,
    TBool aReset )
    {
    HBufC8* fmtp = aFmtpAttribute.AllocLC();
    TPtr8 ptrFmtp( fmtp->Des() );
    
    if ( !aRoleAnswerer )
        {
        // If no strict match, we are accepting fmtp even it is against specs.
        // In that case, replace level value with existing level
        TBool decodes = Decodes( iSdpName, aFmtpAttribute );
        if ( !decodes )
            {
            TPtrC8 existingLevel = 
                ParseProfileLevelIdValPart( FmtpAttr(), 
                                            KMceAvcProfileLevelValOffset, 
                                            KMceAvcProfileLevelValLen, 
                                            KMceAvcProfileLevelOnly1 );
            ReplaceProfileLevelIdValuePart( 
                    ptrFmtp, existingLevel, KMceAvcProfileLevelValOffset );
            }
            
        // In offerer side, tolerate also incorrect iop part usage
        // by using existing iop val
        TPtrC8 existingIop = 
            ParseProfileLevelIdValPart( FmtpAttr(), 
                                        KMceAvcProfileIopValOffset, 
                                        KMceAvcProfileIopValLen, 
                                        KMceAvcProfileIopDefault );
        ReplaceProfileLevelIdValuePart( 
                    ptrFmtp, existingIop, KMceAvcProfileIopValOffset );
        }
     
    if ( iStream && iStream->BoundStream() )
        {
        // In case of two-way video streams, sprop-parameter-set (configkey)
        // from incoming sdp is put to receive stream and it will not override
        // existing key in sendstream
        if ( IS_SENDSTREAM( iStream ) && IS_RECEIVESTREAM( &iStream->BoundStreamL() ) )
            {
            CMceComVideoCodec* boundCodec = 
                static_cast<CMceComVideoCodec*>(
                    iStream->BoundStreamL().FindCodecL( *this ) );

            HBufC8* spropParameterSetsVal = 
                StripFmtpParameterL( ptrFmtp, KMceAvcSpropParameterSetsFmtp() );
            CleanupStack::PushL( spropParameterSetsVal );
            if ( boundCodec && 
                 CMceComVideoCodec::IsConfigKeyValid( spropParameterSetsVal ) )
                {
                boundCodec->SetConfigKeyL( spropParameterSetsVal );
                }
            CleanupStack::PopAndDestroy( spropParameterSetsVal );    
            }
        }
    
    SetFmtpAttributeL( ptrFmtp, aReset );
        
    CleanupStack::PopAndDestroy( fmtp );
    }

// -----------------------------------------------------------------------------
// CMceComAvcCodec::FtmpForOutgoingSdpLC
// -----------------------------------------------------------------------------
//    
HBufC8* CMceComAvcCodec::FtmpForOutgoingSdpLC()
    {
    HBufC8* fmtp = CMceComCodec::FtmpForOutgoingSdpLC();
    if ( iStream && iStream->BoundStream() )
        {
        // In case of two-way video streams, sprop-parameter-set (configkey)
        // for outgoing sdp is taken from send stream
        if ( IS_RECEIVESTREAM( iStream ) && IS_SENDSTREAM( &iStream->BoundStreamL() ) )
            {
            TPtr8 ptrFmtp( fmtp->Des() );
            
            CMceComVideoCodec* boundCodec = 
                static_cast<CMceComVideoCodec*>( 
                    iStream->BoundStreamL().FindCodecL( *this ) );
            if ( boundCodec && boundCodec->IsConfigKeyValid() )
                {
                HBufC8* spropParameterSetsVal = 
                    StripFmtpParameterL( ptrFmtp, KMceAvcSpropParameterSetsFmtp() );
                delete spropParameterSetsVal;
                
                HBufC8* newFmtp = AddFmtpParameterL( ptrFmtp,  
                                                     KMceAvcSpropParameterSetsFmtp(), 
                                                     *boundCodec->iConfigKey );
                CleanupStack::PopAndDestroy( fmtp );
                fmtp = newFmtp;
                CleanupStack::PushL( fmtp );
                }
                
            }
        }
    return fmtp;
    }

// -----------------------------------------------------------------------------
// CMceComAvcCodec::StripFmtpParameterL
// -----------------------------------------------------------------------------
//
HBufC8* CMceComAvcCodec::StripFmtpParameterL( 
    TPtr8& aInputFmtp, 
    const TDesC8& aStrippedParameter )
    {
    HBufC8* strippedParameter = NULL;
    TInt startIndex = aInputFmtp.FindF( aStrippedParameter );
    if ( startIndex != KErrNotFound )
        {
        TPtrC8 parameterVal = aInputFmtp.Mid( startIndex + aStrippedParameter.Length() );
        TInt endIndex = parameterVal.Locate( KMceAvcFmtpParameterSeparator );
        if ( endIndex == KErrNotFound )
            {
            endIndex = parameterVal.Length();
            }    
        
        if ( endIndex > 0 )
            {
            strippedParameter = parameterVal.Mid( 0, endIndex ).AllocLC();
            
            // Remove stripped part from input fmtp
            TInt deleteLen( endIndex + aStrippedParameter.Length() + 1 );
            
            aInputFmtp.Delete( startIndex, deleteLen );
            aInputFmtp.Trim();
            
            CleanupStack::Pop( strippedParameter );
            }
        }
    return strippedParameter;
    }

// -----------------------------------------------------------------------------
// CMceComAvcCodec::AddFmtpParameterL
// -----------------------------------------------------------------------------
//
HBufC8* CMceComAvcCodec::AddFmtpParameterL( 
    const TDesC8& aInputFmtp, 
    const TDesC8& aAddedParameter, 
    const TDesC8& aAddedParameterVal )
    {
    const TInt KMceAvcAdditionalLen = 2;
    HBufC8* outputFmtp = HBufC8::NewL( aInputFmtp.Length() + 
                                       aAddedParameter.Length() + 
                                       aAddedParameterVal.Length() + 
                                       KMceAvcAdditionalLen );
    TPtr8 ptrOutputFmtp( outputFmtp->Des() );
    
    // Add original param
    ptrOutputFmtp.Append( aInputFmtp );
    
    // Add separators if needed
    TInt lastSeparatorIndex = aInputFmtp.LocateReverse( KMceAvcFmtpParameterSeparator );
    if ( aInputFmtp.Length() > 0 && lastSeparatorIndex < ( aInputFmtp.Length() - 2 ) )
        {
        ptrOutputFmtp.Append( KMceAvcFmtpParameterSeparator );
        }
    TInt lastSpaceIndex = aInputFmtp.LocateReverse( KMceAvcFtmpParameterSpace );
    if ( aInputFmtp.Length() > 0 && lastSpaceIndex < ( aInputFmtp.Length() - 1 ) )
        {
        ptrOutputFmtp.Append( KMceAvcFtmpParameterSpace );
        }
        
    // Add new parameter
    ptrOutputFmtp.Append( aAddedParameter );
    ptrOutputFmtp.Append( aAddedParameterVal );
    
    return outputFmtp;
    }
    
#endif
        
// End of File