/*
* Copyright (c) 2004-2008 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:    Mcc Symmetric subthread uplink stream
*
*/



// INCLUDE FILES
#include <mmf/server/mmfdatasource.h>
#include <mmf/server/mmfdatasink.h>
#include <mmf/server/mmfformat.h>
#include <mmf/server/mmfaudioinput.h>
#include "mccsymulstream.h"
#include "mcculdatapath.h"
#include "mcccodecconfigurator.h"
#include "mccrtpmanager.h"
#include "mccrtpdatasink.h"
#include "mmccinterface.h"
#include "mccsubcontrollerlogs.h"
#include "mccmultiplexer.h"
#include "mccresources.h"
#include "amrpayloadformatwrite.h" 
#include "mccinternaldef.h"

#include "mccredpayloadwrite.h"
#include "mccuids.hrh"

// EXTERNAL DATA STRUCTURES

// EXTERNAL FUNCTION PROTOTYPES  

// CONSTANTS

// MACROS

// LOCAL CONSTANTS AND MACROS

// MODULE DATA STRUCTURES

// LOCAL FUNCTION PROTOTYPES

// FORWARD DECLARATIONS

// ============================= LOCAL FUNCTIONS ===============================

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

// -----------------------------------------------------------------------------
// CMccSymUlStream::CMccSymUlStream
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CMccSymUlStream::CMccSymUlStream( 
    TUint32 aMccStreamId, 
    MAsyncEventHandler* aEventhandler, 
    MMccResources* aMccResources,
    CMccRtpManager* aManager, 
    TFourCC aFourCC,
    TInt aStreamType, 
    CMccRtpMediaClock& aClock ) : 
    CMccSymStreamBase( aMccStreamId, 
                       aEventhandler, 
                       aMccResources, 
                       aManager, 
                       aStreamType ), 
    iFormatEncode( NULL ), 
    iFourCC( aFourCC )
    {
    iRtpMediaClock = &aClock;
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CMccSymUlStream* CMccSymUlStream::NewLC( 
    TUint32 aMccStreamId, 
    MAsyncEventHandler* aEventhandler,
    MMccResources* aMccResources,
    CMccRtpManager* aManager, 
    TFourCC aFourCC,
    TInt aStreamType,
    CMccRtpMediaClock& aClock ) 
    {
    CMccSymUlStream* s = 
            new ( ELeave ) CMccSymUlStream( aMccStreamId, 
                                            aEventhandler, 
                                            aMccResources,
                                            aManager, 
                                            aFourCC, 
                                            aStreamType,
                                            aClock );
    CleanupStack::PushL( s );
    s->ConstructL();
    return s;
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::ConstructL()
    {
	__SUBCONTROLLER( "CMccSymUlStream::ConstructL" )
	__SUBCONTROLLER( "CMccSymUlStream::ConstructL, exit" )
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::~CMccSymUlStream
// Destructor
// -----------------------------------------------------------------------------
//
CMccSymUlStream::~CMccSymUlStream()
    {
	__SUBCONTROLLER( "CMccSymUlStream::~CMccSymUlStream" )
    
    if ( iSoundDevice )
        {
        iSoundDevice = NULL;
        }
    
    MultiplexerCleanup();
    
    // Sink may need update since encoder logoff is routed to it
    HandleSpecialSinksAndSources();
    
    if ( iFormatEncode )
        {
        iFormatEncode->SinkThreadLogoff();
        delete iFormatEncode;
        }
    
    delete iRedFormatEncode;
    
	__SUBCONTROLLER( "CMccSymUlStream::~CMccSymUlStream, exit"	)

    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::SetPrioritySettingsL
// Sets the MMF priority settings for this stream
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::SetPrioritySettingsL( 
        const TMMFPrioritySettings& aPriority )
    {
    __SUBCONTROLLER( "CMccSymUlStream::SetPrioritySettingsL start" );
    
    __ASSERT_ALWAYS( iDatasource, User::Leave( KErrNotReady ) );  
    __ASSERT_ALWAYS( iDatasink, User::Leave( KErrNotReady ) );  

    if ( iDatasink->DataSinkType() == KUidMccVideoSink )
        {
        iDatasink->SetSinkPrioritySettings( aPriority );
        }
        
    iDatasource->SetSourcePrioritySettings( aPriority );
    
    __SUBCONTROLLER( "CMccSymUlStream::SetPrioritySettingsL end" );
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::AddSinkAndSourceL
// From CMccSymStreamBase
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::AddSinkAndSourceL( MDataSink* aDatasink, 
        MDataSource* aDatasource )
    {
	__SUBCONTROLLER( "CMccSymUlStream::AddSinkAndSourceL" )
    __ASSERT_ALWAYS( aDatasink, User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( aDatasource, User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( !iDatapath, User::Leave( KErrAlreadyExists ) );
    
	__SUBCONTROLLER( "CMccSymUlStream::AddSinkAndSourceL, adding" )
    iDatasink = aDatasink;
    iDatasource = aDatasource;
    
    iMultiplexer = iMccResources->MultiplexerL( iDatasource );

    iDatapath = CMccUlDataPath::NewL( this, 
                                      iMccResources, 
                                      Type() );
                                      
    iDatapath->AddDataSourceL( iMultiplexer );   
    
    // Add datapath as the sink of the multiplexer, logon is routed to the
    // real datasource
    User::LeaveIfError( 
        iMultiplexer->MultiplexerSourceThreadLogon( *this, *iDatapath, EFalse, Type() ) );
    
    // Source logon must have been completed before fetching sounddevice
    if ( iDatasource->DataSourceType() == KUidMmfAudioInput )
    	{
		__SUBCONTROLLER( "CMccSymUlStream::AddSinkAndSourceL, KUidMmfAudioInput" )
		
        iSoundDevice = &( static_cast<MMMFAudioInput*>( iDatasource )->SoundDevice() );   	
    	}
    		
    // Inform datapath about ids of associated sink and source
    iDatapath->SetAssociatedSink( iDatasink );
    iDatapath->SetAssociatedSource( iDatasource );
    
	__SUBCONTROLLER( "CMccSymUlStream::AddSinkAndSourceL, exit" )
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::LoadCodecL
// From CMccSymStreamBase
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::LoadCodecL( const TMccCodecInfo& aCodecInfo,
                                  const TDesC8& aFmtp )
    {
	__SUBCONTROLLER_INT1( "CMccSymUlStream::LoadCodecL, codec state:", 
                          CurrentCodecState() )
    
    __ASSERT_ALWAYS( CurrentCodecState() == EStateCodecNone || 
                     iCodecInfo.iFourCC == aCodecInfo.iFourCC,
                     User::Leave( KErrArgument ) );
        
    iCodecInfo = aCodecInfo;
    
    if ( iDatasource && iDatasource->DataSourceType() == KUidMccFileSource )
        {
        iCodecInfo.iSourceDefinedTimeStamps = ETrue;
        }
    
    if ( CurrentCodecState() == EStateCodecNone )
        {
        delete iFmtpAttr;
        iFmtpAttr = NULL;

    	iFmtpAttr = HBufC8::NewL( aFmtp.Length() );
        iFmtpAttr->Des().Copy( aFmtp );
        
        iCodecInfo.iFmtpLength = iFmtpAttr->Length();
        
        CreatePayloadFormatEncoderL();
        
       	if ( iDatasink->DataSinkType() == KMccRtpSinkUid )
       	    {
            CMccRtpDataSink* dataSink = static_cast<CMccRtpDataSink*>( iDatasink );        
            dataSink->SetMediaClock( *iRtpMediaClock ); 
       	    }
        
        // Just negotiate the codec, load it later
        static_cast<CMccUlDataPath*>( iDatapath )->NegotiateL( *iFormatEncode );
        
        SetCodecState( EStateCodecNegotiated );        	
        }
    else if ( CurrentCodecState() == EStateCodecNegotiated )
        {
        // Codec is not fully usable yet, do just formatter initialization
        MccCodecConfigurator::ConfigureCodecL( *iDatasource, 
                                                *iFormatEncode, 
                                                iCodecInfo,
                                                *iRtpMediaClock,
                                                CurrentCodecState(),
                                                iDatasink );  
                                                
        SetCodecState( EStateCodecPrepared );
        }
    else if ( CurrentCodecState() == EStateCodecPrepared )
        {
        if ( CodecLoadingAllowed() )
            {
            // Load the codec
            static_cast<CMccUlDataPath*>( iDatapath )->LoadL( *iFormatEncode );
            
            // Set state before updating as the state affects to update functionality
            SetCodecState( EStateCodecLoaded );
                                               
            UpdateCodecInformationL( iCodecInfo );
            }
        }
    else if ( CurrentCodecState() == EStateCodecLoaded ||
              CurrentCodecState() == EStateCodecLoadedAndUpdating )
        {
        if ( iDatasink->DataSinkType() == KMccRtpSinkUid )
       	    {
            CMccRtpDataSink* dataSink = 
                static_cast<CMccRtpDataSink*>( iDatasink );
            
            // For updating keep alive parameters
            TMccCodecInfoBuffer infoBuffer( iCodecInfo );     
            dataSink->ConfigureL( infoBuffer );
       	    }
        // Update codec info
        SetCodecState( EStateCodecLoadedAndUpdating );
    	UpdateCodecInformationL( iCodecInfo );
        }
    else
        {
        User::Leave( KErrNotReady );
        }
        
    __SUBCONTROLLER_INT1( "CMccSymUlStream::LoadCodecL, exit, new state:", 
                          CurrentCodecState() )
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::UpdateCodecInformationL
// From CMccSymStreamBase
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::UpdateCodecInformationL( TMccCodecInfo& /*aCodecInfo*/ )
    {
	__SUBCONTROLLER( "CMccSymUlStream::UpdateCodecInformationL" )
    User::LeaveIfNull( iDatasource );
    User::LeaveIfNull( iFormatEncode );
    
    HandleSpecialSinksAndSources();
                                            
    iCodecInfo.iIsUpdate = ETrue; 
    
    MccCodecConfigurator::ConfigureCodecL( *iDatasource, 
                                            *iFormatEncode, 
                                            iCodecInfo,
                                            *iRtpMediaClock,
                                            CurrentCodecState(),
                                            iDatasink );                                             
                                                  
	__SUBCONTROLLER( "CMccSymUlStream::UpdateCodecInformationL, exit" )
    }
    
// -----------------------------------------------------------------------------
// CMccSymUlStream::CreatePayloadFormatEncoderL
// Creates a payload format encoder for UL stream
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::CreatePayloadFormatEncoderL()
    {
	__SUBCONTROLLER( "CMccSymUlStream::CreatePayloadFormatEncoderL" )
	__ASSERT_ALWAYS( NULL == iFormatEncode, User::Leave( KErrAlreadyExists ) );
	__ASSERT_ALWAYS( iDatasink, User::Leave( KErrNotReady ) );
    
    TUid formatuid;
    formatuid.iUid = iCodecInfo.iPayloadFormatEncoder;
    
    if ( EGenRedUsed == iCodecInfo.iAlgoUsed )
        {
        __ASSERT_ALWAYS( KMccPayloadTypeMax != iCodecInfo.iRedundantPayload,
            User::Leave( KErrArgument ) );
        
        iRedFormatEncode = CMMFFormatEncode::NewL( 
            TUid::Uid( KImplUidRedPayloadFormatEncode ), iDatasink );
        
        iFormatEncode = CMMFFormatEncode::NewL( formatuid, iRedFormatEncode );
        }
    else
        {
        iFormatEncode = CMMFFormatEncode::NewL( formatuid, iDatasink );
        }
    
    User::LeaveIfError( iFormatEncode->SetSinkDataTypeCode( 
        iCodecInfo.iFourCC, iCodecInfo.iType ) );
    User::LeaveIfError( iFormatEncode->SinkThreadLogon( *this ) );
	__SUBCONTROLLER( "CMccSymUlStream::CreatePayloadFormatEncoderL, exit" )
    }
    
// -----------------------------------------------------------------------------
// CMccSymUlStream::PrimeL
// Primes the audio stream ready
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::PrimeL( const TUint32 aEndpointId )
    {
	__SUBCONTROLLER( "CMccSymUlStream::PrimeL" )
	
    TBool controlNetworkResources = SetStateL( EStatePrepared, aEndpointId );  
    
    if ( controlNetworkResources && !LocalStream() )
        {
        iRtpmanager->CreateTransmitStreamL( *iDatasink, iCodecInfo );
        }

    LoadCodecL( iCodecInfo, KNullDesC8 );
        
    DoPrimeL();
    
    InformStreamStateChange( KMccStreamPrepared, aEndpointId );

	__SUBCONTROLLER( "CMccSymUlStream::PrimeL, exit" )
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::PlayL
// Starts audio streaming
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::PlayL( TUint32 aEndpointId,
							 TBool /*aStreamPaused*/, 
                             TBool aEnableRtcp )
    {
	__SUBCONTROLLER( "CMccSymUlStream::PlayL" )
	
    TBool controlNetworkResources = SetStateL( EStateStreaming, aEndpointId );
    
    if ( !LocalStream() )
        {
        if ( controlNetworkResources )
            {
            iRtpmanager->StartSessionL();
            }
            
        User::LeaveIfError( iRtpmanager->SetRTCPSendReceive( aEnableRtcp ) );
        }
    
    UpdateCodecLoadingPermission( IsCodecOperationAllowed() );
    
    LoadCodecL( iCodecInfo, KNullDesC8 );
    
    DoPlayL();
    
    InformStreamStateChange( KMccStreamStarted, aEndpointId );
    
	__SUBCONTROLLER( "CMccSymUlStream::PlayL, exit" )
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::PauseL
// Pauses audio streaming
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::PauseL( const TUint32 aEndpointId, 
							  TBool aEnableRtcp )
    {
    __SUBCONTROLLER( "CMccSymUlStream::PauseL" )
    
    SetStateL( EStatePaused, aEndpointId );
    
    if ( !LocalStream() )
        { 
        User::LeaveIfError( iRtpmanager->SetRTCPSendReceive( aEnableRtcp ) );
        }
    
    DoPauseL();

    InformStreamStateChange( KMccStreamPaused, aEndpointId );
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::ResumeL
// Resumes paused audio streaming
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::ResumeL( const TUint32 aEndpointId,
							   TBool aEnableRtcp )
    {
    __SUBCONTROLLER( "CMccSymUlStream::ResumeL" )
    
    SetStateL( EStateStreaming, aEndpointId );
    
    if ( !LocalStream() )
        { 
        User::LeaveIfError( iRtpmanager->SetRTCPSendReceive( aEnableRtcp ) );
        }
    
    DoResumeL();

    InformStreamStateChange( KMccStreamResumed, aEndpointId );
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::StopL
// Stops the audio streaming
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::StopL( const TUint32 aEndpointId )
    {
    __SUBCONTROLLER( "CMccSymUlStream::StopL" )
    
    SetStateL( EStateStopped, aEndpointId );
    
    DoStopL();
    
    InformStreamStateChange( KMccStreamStopped, aEndpointId );
	}

// -----------------------------------------------------------------------------
// CMccSymUlStream::SetBalanceL
// Sets the current record balance
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::SetBalanceL( TInt aLeftBal, TInt aRightBal )
    {
    if( !iSoundDevice )
        {
        User::Leave( KErrNotReady );
        }
    else
        {
        iSoundDevice->SetRecordBalanceL( aLeftBal, aRightBal );
        }
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::GetBalanceL
// Returns the current record balance
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::GetBalanceL( TInt& aLeftbal, TInt& aRightbal )
    {
    if( !iSoundDevice )
        {
        User::Leave( KErrNotReady );
        }
    else
        {
        iSoundDevice->GetRecordBalanceL( aLeftbal, aRightbal );
        }
    }
    
// -----------------------------------------------------------------------------
// CMccSymUlStream::GetSSRCL
// Returns SSRC value
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::GetSSRCL( TUint32& aSSRCValue )
    {
    __ASSERT_ALWAYS( !LocalStream(), User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatasink, User::Leave( KErrNotReady ) );

    CMccRtpDataSink* sink = static_cast<CMccRtpDataSink*>( iDatasink );
    aSSRCValue = sink->GetSSRC();
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::ResetCountersL
// 
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::ResetCountersL( )
    {
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::SendMediaSignalL
// Uplink can send RTCP media signals
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::SendMediaSignalL( const TMccEvent& aEvent )
    {
    __ASSERT_ALWAYS( !LocalStream(), User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatasink, User::Leave( KErrNotReady ) );
    
    CMccRtpDataSink* p = static_cast<CMccRtpDataSink*>( iDatasink );
    p->SendMediaSignallingL( aEvent );
    }
        
// -----------------------------------------------------------------------------
// CMccSymUlStream::DoChangeBitrateL
// Uplink can change bitrate during AMR streaming
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::DoChangeBitrateL( TInt aBitrate )
    {    
    // This function is trapped inside ChangeBitrate() function
    __SUBCONTROLLER_INT1( "CMccSymUlStream::DoChangeBitrateL CURRENT:", iCodecInfo.iBitrate );
    __SUBCONTROLLER_INT1( "CMccSymUlStream::DoChangeBitrateL NEW:", aBitrate );
    
    // If using AMR codec and bitrate has changed for what it was set
    // then change it.
    const TUint32 fourCC = iCodecInfo.iFourCC.FourCC();
    
    if ( ( KMccFourCCIdAMRNB == fourCC || KMccFourCCIdAMRWB == fourCC ) &&
           iSoundDevice && iFormatEncode )
        {
        // Change the bitrate only if it needs to be changed
        if( iCodecInfo.iBitrate != aBitrate )
            {
            CAmrPayloadFormatWrite* write =
                static_cast<CAmrPayloadFormatWrite*>( iFormatEncode );
                
            if ( write->IsBitrateChangeValid( aBitrate ) )
                {
                MccCodecConfigurator::ChangeBitrateL( 
                    iCodecInfo, aBitrate, *iSoundDevice );
                iCodecInfo.iBitrate = aBitrate;    
                }
            }
        }
    else
        {
        // Other codec than AMR is in use and we cannot change the bitrate
        __SUBCONTROLLER( "CMccSymUlStream::DoChangeBitrateL KErrNotSupported" );
        User::Leave( KErrNotSupported );
        }
    }
    
// -----------------------------------------------------------------------------    
// CMccSymUlStream::SendRTCPSenderReportL
// Sends a RTCP sender report
// -----------------------------------------------------------------------------
// 
void CMccSymUlStream::SendRTCPSenderReportL()
    {
    __ASSERT_ALWAYS( !LocalStream(), User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatasink, User::Leave( KErrNotReady ) );
    
    CMccRtpDataSink* dSink = static_cast<CMccRtpDataSink*>( iDatasink );
    User::LeaveIfError( dSink->SendRTCPSenderReport() );
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::SendRTCPDataL
// Sends Non-RTCP data
// -----------------------------------------------------------------------------
// 
void CMccSymUlStream::SendRTCPDataL( const TDesC8& aData )
    {
    __ASSERT_ALWAYS( !LocalStream(), User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatasink, User::Leave( KErrNotReady ) );
    
    CMccRtpDataSink* dSink = static_cast<CMccRtpDataSink*>( iDatasink );
    dSink->SendRTCPDataL( aData );
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::IsUplink
// -----------------------------------------------------------------------------
// 
TBool CMccSymUlStream::IsUplink()
    {
    return ETrue;
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::StandByL
// -----------------------------------------------------------------------------
// 
void CMccSymUlStream::StandByL()
    {
    }

// -----------------------------------------------------------------------------
// CMccSymUlStream::MultiplexerSink
// -----------------------------------------------------------------------------
//
MDataSink* CMccSymUlStream::MultiplexerSink()
    {
    return iDatapath;
    }

// -----------------------------------------------------------------------------
// CMccSymDlStream::BindContextIntoStreamL
// -----------------------------------------------------------------------------
//
void CMccSymUlStream::BindContextIntoStreamL( TUint32 aEndpointId,
                                              const TMccCryptoContext& aContextParams )
    {
     __ASSERT_ALWAYS( iDatasink, User::Leave( KErrNotReady ) );
     
	if ( iDatasink->DataSinkType() == KMccRtpSinkUid &&
	     aEndpointId == MCC_ENDPOINT_ID( iDatasink ) )
	    {
        CMccRtpDataSink* dataSink = static_cast<CMccRtpDataSink*>( iDatasink );
        dataSink->ApplyCryptoContextL( aContextParams );
	    }
    else
	    {
	    User::Leave( KErrNotFound );
	    }
    }
        
// ========================== OTHER EXPORTED FUNCTIONS =========================

//  End of File  
