/*
* Copyright (c) 2006-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:    MCP Symmetric subthread downlink stream
*
*/



// INCLUDE FILES
#include <mmf/server/mmfformat.h>
#include <mmf/server/mmfaudiooutput.h>
#include "mccsymsimpledlstream.h"
#include "mccdldatapath.h"
#include "mccjitterbuffer.h"
#include "mccuids.hrh"
#include "mccrtpmanager.h"
#include "mcccodecconfigurator.h"
#include "mccinternaldef.h"
#include "mccvideosink.h"
#include "mccsubcontrollerlogs.h"
#include "mccmultiplexer.h"
#include "mccresources.h"

// CONSTANTS

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

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

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

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

    return s;
    }

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::ConstructL()
    {
    iDatapath = CMccDlDataPath::NewL( this, iMccResources, Type() );
    }

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::~CMccSymSimpleDlStream
// Destructor
// -----------------------------------------------------------------------------
//
CMccSymSimpleDlStream::~CMccSymSimpleDlStream()
    {
    if( iSoundDevice )
        {
        iSoundDevice = NULL;
        }
    
    // Must do mux cleanup before decoder (mux sink) deletion
    MultiplexerCleanup();
        
    if( iFormatDecode )
        {
        iFormatDecode->SourceThreadLogoff();
        delete iFormatDecode;
        iFormatDecode = NULL;
        }
    }

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::SetPrioritySettingsL
// Sets the MMF priority settings for this stream
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::SetPrioritySettingsL( 
    const TMMFPrioritySettings& aPriority )
    {
    __ASSERT_ALWAYS( iDatasink, User::Leave( KErrNotReady ) ); 
   
    iDatasink->SetSinkPrioritySettings( aPriority );
    }

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::AddSinkAndSourceL
// From CMccSymStreamBase
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::AddSinkAndSourceL( 
    MDataSink* aDatasink, 
    MDataSource* aDatasource )
    {
    __ASSERT_ALWAYS( aDatasink && aDatasource, User::Leave( KErrArgument ) );

    iDatasink = aDatasink;
    iDatasource = aDatasource;    
    
    iMultiplexer = iMccResources->MultiplexerL( iDatasource );
    
    // Inform datapath about ids of associated sink and source
    iDatapath->SetAssociatedSink( iDatasink );
    iDatapath->SetAssociatedSource( iDatasource );
	        
	iDatapath->AddDataSinkL( aDatasink );

    User::LeaveIfError( iDatasink->SinkThreadLogon( *this ) );
	      
    iDatapath->AddDataSourceL( aDatasource );		    
    }

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

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

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::UpdateCodecInformationL
// From CMccSymStreamBase
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::UpdateCodecInformationL( TMccCodecInfo& aCodecInfo )
    {
    __ASSERT_ALWAYS( iFormatDecode, User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatasink, User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatasource, User::Leave( KErrArgument ) );

    HandleSpecialSinksAndSources();
    
    iCodecInfo.iIsUpdate = ETrue;
    
    // Configure the decoder
    MccCodecConfigurator::ConfigureCodecL( *iFormatDecode,
                                            *iDatasink, 
                                            aCodecInfo,
                                            *iRtpMediaClock,
                                            CurrentCodecState() );
    
    // Configure also the real source
    MccCodecConfigurator::ConfigureCodecL( *iDatasource,
                                            *iDatasink, 
                                            iCodecInfo,
                                            *iRtpMediaClock,
                                            CurrentCodecState() );
    }
    

// -----------------------------------------------------------------------------
// CMccSymDlStream::CreatePayloadFormatDecoderL
// Creates the payload format decoder
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::CreatePayloadFormatDecoderL()
    {
    __ASSERT_ALWAYS( iMultiplexer, User::Leave( KErrArgument ) );

    TUid formatuid;
    CMMFFormatDecode* tmp = NULL;
    
    formatuid.iUid = KImplUidAnyPayloadFormatDecode;
    
    // Multiplexer is the source of the decoder
    tmp = CMMFFormatDecode::NewL( formatuid, iMultiplexer );
    
	tmp->SetSourceDataTypeCode( iCodecInfo.iFourCC, Type() );

    if( iFormatDecode )
        {
        // Remove old one from multiplexer
        iMultiplexer->MultiplexerSourceThreadLogoff( iFormatDecode );
        
        iFormatDecode->SourceThreadLogoff();
        delete iFormatDecode;
        }
    
    iFormatDecode = tmp;
    User::LeaveIfError( iFormatDecode->SourceThreadLogon( *this ) );
    
    // Add decoder as sink of the multiplexer
    User::LeaveIfError( 
        iMultiplexer->MultiplexerSourceThreadLogon( *this, *iFormatDecode, ETrue, Type() ) );

    if ( KMccRtpSourceUid == iDatasource->DataSourceType() )
        {
        RArray<TUint> payloads;
        CleanupClosePushL( payloads );
        ParsePayloadTypesL( payloads, iCodecInfo );
        iMultiplexer->RegClientForPayloadsL( *iFormatDecode, payloads );
        CleanupStack::PopAndDestroy( &payloads );
        }
    }

// -----------------------------------------------------------------------------
// CMccSymDlStream::PrimeL
// Primes the audio stream ready
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::PrimeL( const TUint32 aEndpointId )
    {
    __ASSERT_ALWAYS( iRtpmanager, User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatapath, User::Leave( KErrArgument ) );

    TBool controlNetworkResources = SetStateL( EStatePrepared, aEndpointId );
    
     if ( controlNetworkResources && !LocalStream() )
        {
        iRtpmanager->CreateReceiveStreamL( *iDatasource, iCodecInfo );
        }
    
    LoadCodecL( iCodecInfo, KNullDesC8 );
    
    DoPrimeL();
    
    InformStreamStateChange( KMccStreamPrepared, aEndpointId );
    }

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::PlayL
// Starts audio streaming
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::PlayL( 
    const TUint32 aEndpointId, 
    TBool /*aStreamPaused*/, 
    TBool aEnableRtcp )
    {
    __ASSERT_ALWAYS( iRtpmanager, User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatapath, User::Leave( KErrArgument ) );
    
    TBool controlNetworkResources = SetStateL( EStateStreaming, aEndpointId );
    
    if ( !LocalStream() )
        {  
        if ( controlNetworkResources )
            {
            iRtpmanager->StartSessionL();
            }
            
        User::LeaveIfError( iRtpmanager->SetRTCPSendReceive( aEnableRtcp ) );
        }
    
    UpdateCodecLoadingPermission( ETrue );
    
    LoadCodecL( iCodecInfo, KNullDesC8 );
    
    DoPlayL();
    
    InformStreamStateChange( KMccStreamStarted, aEndpointId );
    }

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::PauseL
// Pauses audio streaming
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::PauseL( const TUint32 aEndpointId, TBool aEnableRtcp )
    {
    __ASSERT_ALWAYS( iRtpmanager, User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatapath, User::Leave( KErrArgument ) );
    
    SetStateL( EStatePaused, aEndpointId );
    
     if ( !LocalStream() )
        { 
        User::LeaveIfError( iRtpmanager->SetRTCPSendReceive( aEnableRtcp ) );
        }

    DoPauseL();
    
    InformStreamStateChange( KMccStreamPaused, aEndpointId );
    }

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::ResumeL
// Resumes paused audio streaming
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::ResumeL( const TUint32 aEndpointId, TBool aEnableRtcp )
    {
    __ASSERT_ALWAYS( iRtpmanager, User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatapath, User::Leave( KErrArgument ) );
    
    SetStateL( EStateStreaming, aEndpointId );
    
    if ( !LocalStream() )
        { 
        User::LeaveIfError( iRtpmanager->SetRTCPSendReceive( aEnableRtcp ) );
        }

    DoResumeL();
    
    InformStreamStateChange( KMccStreamResumed, aEndpointId );
    }

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::StopL
// Stops the audio streaming
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::StopL( const TUint32 aEndpointId )
    {
    __ASSERT_ALWAYS( iDatapath, User::Leave( KErrArgument ) );    
    
    SetStateL( EStateStopped, aEndpointId );
    
    DoStopL();
    
    InformStreamStateChange( KMccStreamStopped, aEndpointId );   
    }
    
// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::SetBalanceL
// Sets the current play balance
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::SetBalanceL( TInt aLeftBal, TInt aRightBal )
    {
    if( !iSoundDevice )
        {
        User::Leave( KErrNotReady );
        }
    else
        {
        iSoundDevice->SetPlayBalanceL( aLeftBal, aRightBal );
        }
    }

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::GetBalanceL
// Returns the current play balance
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::GetBalanceL( TInt& aLeftbal, TInt& aRightbal )
    {
    if( !iSoundDevice )
        {
        User::Leave( KErrNotReady );
        }
    else
        {
        iSoundDevice->GetPlayBalanceL( aLeftbal, aRightbal );
        }
    }
    
// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::GetSSRCL
// Returns SSRC value
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::GetSSRCL( TUint32& /*aSSRCValue*/ )
    {
    User::Leave ( KErrNotSupported );
    }


// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::ResetCountersL
// Resets Jitterbuffer
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::ResetCountersL( )
    {
    }

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::SendMediaSignalL
// Downlink can send RTCP media signals (only receiver reports)
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::SendMediaSignalL( const TMccEvent& aEvent )
    {
    __ASSERT_ALWAYS( !LocalStream(), User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatasource, User::Leave( KErrNotReady ) );
    
    CMccRtpDataSource* p = static_cast<CMccRtpDataSource*>( iDatasource );
    p->SendMediaSignallingL( aEvent );
    }
        
// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::SendRTCPReceiverReportL
// Sends a RTCP receiver report
// -----------------------------------------------------------------------------
//  
void CMccSymSimpleDlStream::SendRTCPReceiverReportL()
    {
    __ASSERT_ALWAYS( !LocalStream(), User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatasource, User::Leave( KErrArgument ) );

    CMccRtpDataSource* dSource = static_cast<CMccRtpDataSource*>( iDatasource );
    User::LeaveIfError( dSource->SendRTCPReceiverReport() );
    }

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::StartInactivityTimerL
// Starts inactivity timer for a stream
// -----------------------------------------------------------------------------
// 
void CMccSymSimpleDlStream::StartInactivityTimerL( TUint32 aTimeoutTime )
    {
    __ASSERT_ALWAYS( !LocalStream(), User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatasource, User::Leave( KErrArgument ) );
    
    CMccRtpDataSource* dSource = static_cast<CMccRtpDataSource*>( iDatasource );
    dSource->StartInactivityTimerL( aTimeoutTime );
    }

      
// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::StopInactivityTimerL
// Stops inactivity timer for a stream 
// -----------------------------------------------------------------------------
// 
void CMccSymSimpleDlStream::StopInactivityTimerL()
    {
    __ASSERT_ALWAYS( !LocalStream(), User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatasource, User::Leave( KErrArgument ) );
    
    CMccRtpDataSource* dSource = static_cast<CMccRtpDataSource*>( iDatasource );
    dSource->StopInactivityTimerL( );
    }

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::IsUplink
// -----------------------------------------------------------------------------
// 
TBool CMccSymSimpleDlStream::IsUplink()
    {
    return EFalse;
    }

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::StandByL
// -----------------------------------------------------------------------------
// 
void CMccSymSimpleDlStream::StandByL()
    {
    __ASSERT_ALWAYS( !LocalStream(), User::Leave( KErrArgument ) );
    __ASSERT_ALWAYS( iDatasource, User::Leave( KErrArgument ) );
    
    CMccRtpDataSource* dSource = static_cast<CMccRtpDataSource*>( iDatasource );
    dSource->StandBy( EForceStandby, iCodecInfo.iPayloadType );
    }

// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::JitterBufferError
// -----------------------------------------------------------------------------
// 
void CMccSymSimpleDlStream::JitterBufferError( TInt /*aError*/ )
	{
    }

// -----------------------------------------------------------------------------
// CMccSymDlStream::DynamicBufferChangeRequest
// -----------------------------------------------------------------------------
//
void CMccSymSimpleDlStream::DynamicBufferChangeRequest( TInt /* aBufferSize*/ )
    {
    
    }
    
// -----------------------------------------------------------------------------
// CMccSymSimpleDlStream::MultiplexerSink
// -----------------------------------------------------------------------------
//
MDataSink* CMccSymSimpleDlStream::MultiplexerSink()
    {
    return iFormatDecode;
    }

// ========================== OTHER EXPORTED FUNCTIONS =========================

//  End of File  
