/*
* 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 FILES
#include <mmcccodecinformationfactory.h>
#include <mmcccodecinformation.h>
#include "mccfilesink.h"
#include "mmccinterfacedef.h"
#include "mmcccodecinformation.h"
#include "mccfilesinklogs.h"
#include "mccinternalevents.h"
#include "mccinternaldef.h"
#include "mccresources.h"

// CONSTANTS

const TInt KMccMaxNumTimestamps = 5;

const TInt KMccTimestampDifferenceMultiplier = 10;

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

// -----------------------------------------------------------------------------
// CMccFileSink::NewSinkL
// -----------------------------------------------------------------------------
//
MDataSink* CMccFileSink::NewSinkL( TUid /*aImplementationUid*/, 
                                   const TDesC8& /*aInitData*/ )
    {
    __FILESINK_CONTROLL( "CMccFileSink::NewSinkL" )
    CMccFileSink* self = new ( ELeave ) CMccFileSink();
    return static_cast<MDataSink*>( self );
    }

// -----------------------------------------------------------------------------
// CMccFileSink::ConstructSinkL
// -----------------------------------------------------------------------------
//
void CMccFileSink::ConstructSinkL( const TDesC8& aInitData )
    {
    __FILESINK_CONTROLL( "CMccFileSink::ConstructSinkL" )

	iFileComposer = CCamC3GPDataSink::NewL( this );
    iFileComposer->SetSizeLimit( iMaxFileSize );
    
    TPckgBuf<TFileName> initData;
    initData.Copy( aInitData );
    iFileName = initData();
    
    }
    
// -----------------------------------------------------------------------------
// CMccFileSink::CMccFileSink
// -----------------------------------------------------------------------------
//
CMccFileSink::CMccFileSink() : 
    CMccDataSink( KMccFileSinkUid )
    {
    iVideoCodec.iFourCC = TFourCC( KMccFourCCIdH263 );
    // FJKI-7J58CB no use case for audio in file, hence removing the audio track capability 
    //             per request from customer
    // iAudioFourCC = TFourCC( KMccFourCCIdAMRNB );
    iAudioFourCC = TFourCC( KMMFFourCCCodeNULL );
    iFileName = KNullDesC;
    iMaxFileSize = 0; 
    iT1 = 0;
    iT2 = 0;
    iPausedDuration = 0;
    iPreviousTimestamp = 0;
    iAddToTimestamp = 0;
    iSizeLimitReached = EFalse;
    iNotifySizeLimitReached = EFalse;
    SetActiveUserIndex( 0 );
    }
        
// -----------------------------------------------------------------------------
// CMccFileSink::~CMccFileSink
// -----------------------------------------------------------------------------
//
CMccFileSink::~CMccFileSink()
    {
    __FILESINK_CONTROLL( "CMccFileSink::~CMccFileSink" )
    // Cleanup and other ending operations...
    if ( iCurrentState == ERecording || iCurrentState == EPaused )
        {
        TRAP_IGNORE( CMccFileSink::SinkStopL() );
        }	
	delete iFileComposer;
	iTimestamps.Close();
	
	iUsers.ResetAndDestroy();
    }

// -----------------------------------------------------------------------------
// CMccFileSink::SetCurrentUser
// -----------------------------------------------------------------------------
//
void CMccFileSink::SetCurrentUser( MAsyncEventHandler* aEventHandler )
    {
    iAsyncEventHandler = aEventHandler;
    }

// -----------------------------------------------------------------------------
// CMccFileSink::SinkPrimeL
// -----------------------------------------------------------------------------
//
void CMccFileSink::SinkPrimeL()
	{
	__FILESINK_CONTROLL( "CMccFileSink::SinkPrimeL" )

    DoSinkPrimeL();	
	}
	
// -----------------------------------------------------------------------------
// CMccFileSink::SinkPlayL()
// -----------------------------------------------------------------------------
//
void CMccFileSink::SinkPlayL()
    {
    __FILESINK_CONTROLL( "CMccFileSink::SinkPlayL" )  
    
    DoSinkPlayL();
    }

// -----------------------------------------------------------------------------
// CMccFileSink::SinkPauseL()
// 
// Pauses streaming by cancelling timers
// -----------------------------------------------------------------------------
//
void CMccFileSink::SinkPauseL()
    {
    __FILESINK_CONTROLL( "CMccFileSink::SinkPauseL" )

    DoSinkPauseL();
	}

// -----------------------------------------------------------------------------
// CMccFileSink::SinkStopL()
// 
// Stops streaming
// -----------------------------------------------------------------------------
//
void CMccFileSink::SinkStopL()
    {
    __FILESINK_CONTROLL( "CMccFileSink::SinkStopL" )
    
    DoSinkStopL();		
    }

// -----------------------------------------------------------------------------
// CMccFileSink::SinkDataTypeCode
// -----------------------------------------------------------------------------
//
TFourCC CMccFileSink::SinkDataTypeCode( TMediaId aMediaId )
	{
    __FILESINK_CONTROLL( "CMccFileSink::SinkDataTypeCode" )

    if ( KUidMediaTypeVideo == aMediaId.iMediaType )
        {
        return iVideoCodec.iFourCC;
        }
    else if ( KUidMediaTypeAudio == aMediaId.iMediaType  )
        {
        return iAudioFourCC;
        }
    else
        {
        return TFourCC( KMMFFourCCCodeNULL );
        }
	}
	
// -----------------------------------------------------------------------------
// CMccFileSink::SetSinkDataTypeCode
// -----------------------------------------------------------------------------
//
TInt CMccFileSink::SetSinkDataTypeCode( TFourCC aCodec, 
                            TMediaId aMediaId )
	{
    __FILESINK_CONTROLL( "CMccFileSink::SetSinkDataTypeCode" )

    TInt retVal = KErrNone;
    if ( KUidMediaTypeVideo == aMediaId.iMediaType &&
    	aCodec == iVideoCodec.iFourCC )
        {
        retVal = KErrNone;
        }
    else if ( KUidMediaTypeAudio == aMediaId.iMediaType &&
    	aCodec == iAudioFourCC )
        {
        retVal = KErrNone;
        }
    else
        {
        retVal = KErrNotSupported;
        }

	return retVal;
	}	

// -----------------------------------------------------------------------------
// CMccFileSink::BufferEmptiedL
// -----------------------------------------------------------------------------
//
void CMccFileSink::BufferEmptiedL( CMMFBuffer* /*aBuffer*/ )
	{
    __FILESINK_MEDIA( "CMccFileSink::BufferEmptiedL" )
    User::Leave( KErrNotSupported );
	}
	
// -----------------------------------------------------------------------------
// CMccFileSink::CanCreateSinkBuffer
// -----------------------------------------------------------------------------
//
TBool CMccFileSink::CanCreateSinkBuffer()
	{
    __FILESINK_CONTROLL( "CMccFileSink::CanCreateSinkBuffer, EFalse" )
	return EFalse;
	}	

// -----------------------------------------------------------------------------
// CMccFileSink::CreateSinkBufferL
// -----------------------------------------------------------------------------
//
CMMFBuffer* CMccFileSink::CreateSinkBufferL( 
	TMediaId /*aMediaId*/, 
    TBool& /*aReference*/ )
	{
	__FILESINK_CONTROLL( "CMccFileSink::CreateSinkBufferL, return NULL" )
    CMMFBuffer* buffer = NULL;
	User::Leave( KErrNotSupported );	
	return buffer;	
	}
	
// -----------------------------------------------------------------------------
// CMccFileSink::SinkThreadLogon
// -----------------------------------------------------------------------------
//
TInt CMccFileSink::SinkThreadLogon( MAsyncEventHandler& aEventHandler )
	{
    __FILESINK_CONTROLL( "CMccFileSink::SinkThreadLogon" )

    TInt err( KErrNone );
    TMccFileSinkUser* userEntry = 
        MccUserArray<TMccFileSinkUser>::FindUserEntryForCurrent( iUsers, &aEventHandler );
    if ( !userEntry )
        {
        TRAP( err, AddUserL( &aEventHandler ) );
        }
    
    __FILESINK_CONTROLL_INT1( 
        "CMccFileSink::SinkThreadLogon, exit with err:", err )
        
    return err;
	}
	
// -----------------------------------------------------------------------------
// CMccFileSink::SinkThreadLogoff
// -----------------------------------------------------------------------------
//
void CMccFileSink::SinkThreadLogoff()
	{
	__FILESINK_CONTROLL( "CMccFileSink::SinkThreadLogoff" )

    // If multiple different codecs are using the filesink and active user
    // is removed, reset active user information so that new active user
    // can be seleceted once someone else tries to use the sink
    if ( IsActiveUser( iAsyncEventHandler ) )
        {
        SetActiveUserIndex( KErrNotFound );
        }
         
    MccUserArray<TMccFileSinkUser>::RemoveCurrentUser( iUsers, iAsyncEventHandler );
    
    if ( iUsers.Count() > 0 )
        {
        SetCurrentUser( iUsers[ 0 ]->iEventHandler );
        }
    else
        {
        SetCurrentUser( NULL );
        }
	}

// -----------------------------------------------------------------------------
// CMccFileSink::EmptyBufferL
// -----------------------------------------------------------------------------
//
void CMccFileSink::EmptyBufferL( CMMFBuffer* aBuffer,
                  MDataSource* aProvider,
                  TMediaId aMediaId )
	{
	__FILESINK_MEDIA( "CMccFileSink::EmptyBufferL" )
	
	__ASSERT_ALWAYS( aBuffer && aProvider, User::Leave( KErrArgument ) );
	
	
    if ( iCurrentState != ERecording )
        {
        __FILESINK_MEDIA( "CMccFileSink::EmptyBufferL, IGNORED" )
        
        // Do not let the datapath to halt
        aProvider->BufferEmptiedL( aBuffer );
        return;
        }

    if ( iNotifySizeLimitReached )
        {
        __FILESINK_MEDIA( 
            "CMccFileSink::EmptyBufferL, IGNORED (notify size limit reached)" )
        MfcoSizeLimitReachedL();
        
        // Do not let the datapath to halt
        aProvider->BufferEmptiedL( aBuffer );
        return;
        }
        
	CMMFDataBuffer* buffer = static_cast<CMMFDataBuffer*>(aBuffer);
	CCMRMediaBuffer* outBuffer = NULL; 
	                  
	TTimeIntervalMicroSeconds timeToPlay = TimeToPlayL( buffer->TimeToPlay() );
    	    	
   	__FILESINK_MEDIA_INT1( "CMccFileSink::EmptyBufferL, timeToPlay = ", 
	                       timeToPlay.Int64() )
	
	TBool dropBuffer( EFalse );                      
	if ( aMediaId.iMediaType == KUidMediaTypeAudio )
		{
    	__FILESINK_MEDIA( "CMccFileSink::EmptyBufferL, audio" )
    	    
    	outBuffer = new ( ELeave ) CCMRMediaBuffer(
        	buffer->Data(), CCMRMediaBuffer::EAudioAMRNB, buffer->BufferSize(),
            ETrue,  timeToPlay ); 
        CleanupStack::PushL( outBuffer );  
		}
	else if ( aMediaId.iMediaType == KUidMediaTypeVideo )
		{
    	__FILESINK_MEDIA( "CMccFileSink::EmptyBufferL, video" )    
        
        TFourCC providerDataType = UpdateActiveUserL( aMediaId, *aProvider );
        
        CCMRMediaBuffer::TBufferType bufType = 
            ResolveBufferType( *buffer, providerDataType );	
        
        dropBuffer = CheckWritingPermission( *buffer, bufType );
        
    	outBuffer = new ( ELeave ) CCMRMediaBuffer(
        	buffer->Data(), bufType, buffer->BufferSize(),
            ETrue,  timeToPlay );    
            
        CleanupStack::PushL( outBuffer );   
		}
	else
		{
    	__FILESINK_MEDIA( "CMccFileSink::EmptyBufferL, unknown media" ) 
		User::Leave( KErrNotSupported );	
		}
    
    if ( !dropBuffer )
        {
    	iFileComposer->WriteBufferL( outBuffer );
        }
        
	CleanupStack::PopAndDestroy( outBuffer );
	outBuffer= NULL;
	aProvider->BufferEmptiedL( aBuffer );
	}	

// -----------------------------------------------------------------------------
// CMccFileSink::BufferFilledL
// -----------------------------------------------------------------------------
//
void CMccFileSink::BufferFilledL( CMMFBuffer* /*aBuffer*/ )
	{
	__FILESINK_MEDIA( "CMccFileSink::BufferFilledL" )
	}	                  

// -----------------------------------------------------------------------------
// CMccFileSink::MfcoDiskFullL
// -----------------------------------------------------------------------------
//
void CMccFileSink::MfcoDiskFullL()
	{
	__FILESINK_MEDIA( "CMccFileSink::MfcoDiskFullL" )
	
	// After size limit has been reached, there is no way to really continue
	// recording to the same file, writebuffer returns without error but
	// has no effect. Pause the sink anyway but fail if resume is tried.
	// Creating new filewriter would be only way to recover from this but
	// that would overwrite old recording.
	iSizeLimitReached = ETrue;
	
	SendStreamEventToClient( KMccResourceNotAvailable, KErrNoMemory, ETrue );
	
    TRAP_IGNORE( AutomaticPauseL() );
    
    iNotifySizeLimitReached = EFalse;
	}	                  

// -----------------------------------------------------------------------------
// CMccFileSink::MfcoSizeLimitReachedL
// -----------------------------------------------------------------------------
//
void CMccFileSink::MfcoSizeLimitReachedL()
	{
	__FILESINK_MEDIA( "CMccFileSink::MfcoSizeLimitReachedL" )
	
	iSizeLimitReached = ETrue;
	
	SendStreamEventToClient( KMccResourceNotAvailable, KErrNone, ETrue );

    TRAP_IGNORE( AutomaticPauseL() );
    
    iNotifySizeLimitReached = EFalse;
	}	                  

// -----------------------------------------------------------------------------
// CMccFileSink::SetVideoCodecL
// -----------------------------------------------------------------------------
//
void CMccFileSink::SetVideoCodecL( const TMccCodecInfo& aVideoCodec )
    {
   	__FILESINK_CONTROLL_STR8( 
	    "CMccFileSink::SetVideoCodecL, sdpname:", aVideoCodec.iSdpName )
    
    TMccFileSinkUser* user = 
        MccUserArray<TMccFileSinkUser>::FindUserEntryForCurrent( iUsers, iAsyncEventHandler );
    if ( user )
        {
        user->iCodecInfo = aVideoCodec;
        }
    }

// -----------------------------------------------------------------------------
// CMccFileSink::SetAudioCodecL
// -----------------------------------------------------------------------------
//
void CMccFileSink::SetAudioCodecL( const TMccCodecInfo& /*aAudioCodec*/ )
    {
    __FILESINK_MEDIA( "CMccFileSink::SetAudioCodecL" )
    }
    
// -----------------------------------------------------------------------------
// CMccFileSink::RecordTimeAvailableL
// -----------------------------------------------------------------------------
//
void CMccFileSink::RecordTimeAvailableL( TTimeIntervalMicroSeconds& aTime )
	{ 
	__FILESINK_CONTROLL( "CMccFileSink::RecordTimeAvailableL" )
	aTime = iFileComposer->GetRemainingTimeL();
	}

// -----------------------------------------------------------------------------
// CMccFileSink::SetFileNameL
// -----------------------------------------------------------------------------
//
void CMccFileSink::SetFileNameL( const TFileName aFileName )
	{
	__FILESINK_CONTROLL( "CMccFileSink::SetFileNameL" )
	iFileName = aFileName;
	}
	
// -----------------------------------------------------------------------------
// CMccFileSink::SendStreamEventToClient
// -----------------------------------------------------------------------------
//	
void CMccFileSink::SendStreamEventToClient( 
    TMccEventType aEventType, 
    TInt aError,
    TBool aToAllClients )
	{
	__FILESINK_CONTROLL_INT2( 
	    "CMccFileSink::SendStreamEventToClient, type", aEventType, 
	    " to all:", aToAllClients )
	
    TMccEvent event( 0, 
                     0, 
                     0, 
                     MCC_ENDPOINT_ID( static_cast<MDataSink*>( this ) ), 
                     KMccEventCategoryStream, 
                     aEventType, 
                     aError, 
                     KNullDesC8 );

    if ( aToAllClients )
        {
        for ( TInt i = 0; i < iUsers.Count(); i++ )
            {
            FinalizeSendEvent( iUsers[ i ]->iEventHandler, event );
            }
        }
    else
        {
        FinalizeSendEvent( iAsyncEventHandler, event );
        }
	}
	
// -----------------------------------------------------------------------------
// CMccFileSink::TimeToPlayL
// -----------------------------------------------------------------------------
//	   	
TTimeIntervalMicroSeconds CMccFileSink::TimeToPlayL( 
    TTimeIntervalMicroSeconds aCurrentTimestamp )
    { 
    CalculateAverageTimestampDifferenceL( aCurrentTimestamp );

    TTimeIntervalMicroSeconds timeToPlay( 0 );
    
    if ( aCurrentTimestamp >= iPausedDuration )
        {
        __FILESINK_CONTROLL("CMccFileSink::TimeToPlay aCurrentTimestamp \
>= iPausedDuration" )

        timeToPlay = aCurrentTimestamp.Int64() - iPausedDuration.Int64();    
        }
    else
        {
        __FILESINK_CONTROLL("CMccFileSink::TimeToPlay aCurrentTimestamp \
< iPausedDuration" )
        timeToPlay = aCurrentTimestamp; 
        }
   
    timeToPlay = ( timeToPlay.Int64() + iAddToTimestamp );
    
    __FILESINK_CONTROLL_INT1("CMccFileSink::TimeToPlay, \
timeToPlay=", timeToPlay.Int64() )  

    iPreviousTimestamp = aCurrentTimestamp;
  
    return timeToPlay;
    }

// -----------------------------------------------------------------------------
// CMccFileSink::CalculateAverageTimestampDifferenceL
// -----------------------------------------------------------------------------
//  
void CMccFileSink::CalculateAverageTimestampDifferenceL( 
    const TTimeIntervalMicroSeconds& aCurrentTimestamp )
    {
    TInt64 averageTimeStampDifference = 0;
    if ( iTimestamps.Count() == KMccMaxNumTimestamps )
       {
       
       for ( TInt i = iTimestamps.Count() - 1; i > 0; i-- )
               {
               averageTimeStampDifference += ( iTimestamps[ i ] - iTimestamps[ i - 1 ] );
               }
               
       averageTimeStampDifference = averageTimeStampDifference / ( KMccMaxNumTimestamps - 1 );
       }
    
    if ( aCurrentTimestamp > iPreviousTimestamp )
       {
       if ( iTimestamps.Count() >= KMccMaxNumTimestamps )
           {
           iTimestamps.Remove( 0 );
           }
       iTimestamps.AppendL( aCurrentTimestamp.Int64() );
       }
    else
       {
       TInt64 currDifference = iPreviousTimestamp.Int64() - aCurrentTimestamp.Int64();
       if ( averageTimeStampDifference != 0 && 
            currDifference > ( averageTimeStampDifference * KMccTimestampDifferenceMultiplier ) )
           {
           iAddToTimestamp += ( currDifference + averageTimeStampDifference );
           iTimestamps.Reset();
		   iPausedDuration = 0;
           
           __FILESINK_CONTROLL_INT1("CMccFileSink::TimeToPlay, iAddToTimestamp=", iAddToTimestamp )  
           }
       }
    }

// -----------------------------------------------------------------------------
// CMccFileSink::ResetTimers
// -----------------------------------------------------------------------------
//	  
void CMccFileSink::ResetTimers()
    {
    iT1 = 0;
    iT2 = 0; 
    iPausedDuration = 0;  
    iPreviousTimestamp = 0;
    iAddToTimestamp = 0;
    iTimestamps.Reset();
    }

// -----------------------------------------------------------------------------
// CMccFileSink::SetPausedDuration
// -----------------------------------------------------------------------------
//	
void CMccFileSink::SetPausedDuration( TTime aT1, TTime aT2 )
    {
    __FILESINK_CONTROLL( "CMccFileSink::SetPausedDuration" )
    __FILESINK_CONTROLL_INT2( 
        "CMccFileSink::SetPausedDuration, aT1=", aT1.Int64(),
        "aT2=", aT2.Int64() )

    if ( aT1 != 0 && aT1 < aT2 )
        {
        __FILESINK_CONTROLL_INT1("CMccFileSink::SetPausedDuration, \
before iPausedDuration=", iPausedDuration.Int64() ) 

        iPausedDuration = ( iPausedDuration.Int64() + 
                            aT2.MicroSecondsFrom( aT1 ).Int64() );
                            
        __FILESINK_CONTROLL_INT1("CMccFileSink::SetPausedDuration, \
after iPausedDuration=", iPausedDuration.Int64() ) 
        }
    }

// -----------------------------------------------------------------------------
// CMccFileSink::AutomaticPauseL
// -----------------------------------------------------------------------------
//
void CMccFileSink::AutomaticPauseL()
    {
    __FILESINK_CONTROLL( "CMccFileSink::AutomaticPauseL" )
    
    for ( TInt i = 0; i < iUsers.Count(); i++ )
        {
        TMccEvent* controlEvent = new ( ELeave ) TMccEvent;
        CleanupStack::PushL( controlEvent );
        
        controlEvent->iEndpointId = MCC_ENDPOINT_ID( static_cast<MDataSink*>( this ) );
    	controlEvent->iEventCategory = KMccEventCategoryStreamControl;
    	controlEvent->iEventType = KMccStreamPaused;
    	controlEvent->iEventNumData = KMccAutomaticEvent;

        User::LeaveIfError( 
            FinalizeSendEvent( iUsers[ i ]->iEventHandler, *controlEvent ) );
        
        CleanupStack::PopAndDestroy( controlEvent );
        }
    
    __FILESINK_CONTROLL( "CMccFileSink::AutomaticPauseL, exit" )
    }  
	
// -----------------------------------------------------------------------------
// CMccFileSink::FinalizeSendEvent
// -----------------------------------------------------------------------------
//	
TInt CMccFileSink::FinalizeSendEvent( 
    MAsyncEventHandler* aEventHandler, 
    TMccEvent& aEvent )
	{
	TInt err( KErrNone );
	if ( aEventHandler )
	    {
	    TMccInternalEvent internalEvent( KMccFileSinkUid, 
		                                 EMccInternalEventNone,
		                                 aEvent );
		                         
		err = aEventHandler->SendEventToClient( internalEvent );
	    }
	else
		{
		__FILESINK_CONTROLL( "CMccFileSink::FinalizeSend, aEventHandler=NULL" )
		err = KErrNotReady;
		}
    return err;
	}

// ---------------------------------------------------------------------------
// CMccFileSink::GetCodecTypeStringLC
// ---------------------------------------------------------------------------
//
HBufC8* CMccFileSink::GetCodecTypeStringLC( const TMccCodecInfo& aCodecInfo )    
    {
    __FILESINK_CONTROLL( "CMccFileSink::GetCodecTypeStringLC" )
    
    CMccCodecInformationFactory* factory = CMccCodecInformationFactory::NewL();
    CleanupStack::PushL( factory );
    CMccCodecInformation* codec = 
        factory->CreateCodecInformationL( aCodecInfo.iSdpName );
    CleanupStack::PushL( codec );
    codec->SetValues( aCodecInfo );
    HBufC8* fmtp = codec->GetFmtpL().AllocL();
    CleanupStack::PopAndDestroy( codec );
    CleanupStack::PopAndDestroy( factory );
    CleanupStack::PushL( fmtp );
    
    _LIT8( KMccCodecTypeFormat, "video/%S; %S" );
    HBufC8* codecType = HBufC8::NewL( 
        KMccCodecTypeFormat().Length() + fmtp->Length() + KMaxSdpNameLength );
        
    // Disabling PC-lint warnings 1025 and 64, which seems to be false warnings
    /*lint -e1025 -e64*/  
    codecType->Des().AppendFormat( KMccCodecTypeFormat(), &aCodecInfo.iSdpName, &*fmtp ); 
    CleanupStack::PopAndDestroy( fmtp );
    CleanupStack::PushL( codecType );
     
    __FILESINK_CONTROLL_STR8( "type string:", *codecType )
    
    return codecType;
    }

// ---------------------------------------------------------------------------
// CMccFileSink::ResolveBufferType
// Dec spec info cannot be given multiple times for file writer so ignore
// all later dec spec info buffers. H264 bytestream buffers needs to be
// ignored until dec spec info buffer has been written.
// ---------------------------------------------------------------------------
//
CCMRMediaBuffer::TBufferType CMccFileSink::ResolveBufferType( 
    CMMFDataBuffer& aBuffer,
    TFourCC aDataType )
    { 
    CCMRMediaBuffer::TBufferType type = CCMRMediaBuffer::EVideoH263;
    if ( aDataType == KMccFourCCIdAVC )
        {
        if ( TMccCodecInfo::IsAvcPpsOrSpsData( aBuffer.Data() ) )
            {
            __FILESINK_CONTROLL( "pps or sps" )
            type = CCMRMediaBuffer::EVideoH264BytestreamDecSpecInfo;
            }
        else
            {
            type = CCMRMediaBuffer::EVideoH264Bytestream;
            }
        }
    
    __FILESINK_CONTROLL_INT1( "CMccFileSink::ResolveBufferType, type:", type )
    
    return type;
    }

// ---------------------------------------------------------------------------
// CMccFileSink::CheckWritingPermissionL
// 1) H264 dec spec info can be written only once to the file.
// 2) Normal frames should not be written until IFrame has been written to file
// as it will cause bad quality for the clip. 
// 3) H264 bytestream cannot be written before dec spec info has been written.
// ---------------------------------------------------------------------------
//
TBool CMccFileSink::CheckWritingPermission( 
    CMMFDataBuffer& aBuffer,
    const CCMRMediaBuffer::TBufferType& aBufferType )
    { 
    TBool ignoreBuffer( EFalse );

    if ( aBufferType == CCMRMediaBuffer::EVideoH264BytestreamDecSpecInfo )
        {
        if ( !iDecSpecInfoProvided )
            {
            iDecSpecInfoProvided = ETrue;
            ignoreBuffer = EFalse;
            }
        else
            {
            __FILESINK_CONTROLL( "Ignore as dec spec info already provided!" )
            ignoreBuffer = ETrue;
            }
        }
    else if ( iMccResources && !iKeyFrameProvided )
        {
        TBool keyFrame = iMccResources->IsKeyFrame( 
                MCC_ENDPOINT_ID( static_cast<MDataSink*>( this ) ), aBuffer );
        if ( keyFrame )
            {
            __FILESINK_CONTROLL_INT1( "Key frame match for timestamp:", 
                                      aBuffer.TimeToPlay().Int64() )
            iKeyFrameProvided = ETrue;
            }
        else
            {
            __FILESINK_CONTROLL( "Ignore as key frame not yet provided!" )
            ignoreBuffer = ETrue;
            }
        }
    else if ( aBufferType == CCMRMediaBuffer::EVideoH264Bytestream )
        {
        if ( !iDecSpecInfoProvided )
            {
            __FILESINK_CONTROLL( "Ignore as dec spec info not yet provided!" )
            ignoreBuffer = ETrue;
            }
        }
    else
        {
        // NOP
        }
    
    __FILESINK_CONTROLL_INT1( "CMccFileSink::CheckWritingPermission, ignore:", 
                              ignoreBuffer )
    
    return ignoreBuffer;
    }

// ---------------------------------------------------------------------------
// CMccFileSink::SetStateL
// ---------------------------------------------------------------------------
//
TBool CMccFileSink::SetStateL( TFileSinkState aState )
    {
    TBool controlSink( iCurrentState != aState );
    TBool transitionOk( iCurrentState == aState );
    switch ( aState )
        {
        case EReady:
            { 
            transitionOk = ETrue;
            iCurrentState = aState;
            break;
            }
        case EPaused:
            {
            if ( iCurrentState == ERecording )
                {
                transitionOk = ETrue;
                iCurrentState = aState;
                }
            break;
            }
        case ERecording:
            {
            if ( iCurrentState == EReady || iCurrentState == EPaused )
                {
                transitionOk = ETrue;
                iCurrentState = aState;
                }
            break;
            }
        case EStopped:
            {
            // State is not changed to stopped if there's several users
            transitionOk = ETrue;
            const TInt KMccFileSinkMultipleUsers = 2;
            if ( iUsers.Count() < KMccFileSinkMultipleUsers )
                {
                iCurrentState = aState;
                }
            break;
            }
        default:
            {
            break;       
            }
        }

    __ASSERT_ALWAYS( transitionOk, User::Leave( KErrNotReady ) );
    
    return controlSink;    
    }

// -----------------------------------------------------------------------------
// CMccFileSink::DoSinkPrimeL
// -----------------------------------------------------------------------------
//
void CMccFileSink::DoSinkPrimeL( TBool aSendEvent )
	{
	__FILESINK_CONTROLL( "CMccFileSink::DoSinkPrimeL" )

    // If disk is full at beginning, ignore error at this stage and send disk
    // full event when first buffer is tried to be written to file.
    //
    if ( SetStateL( EReady ) )
        {
        HBufC8* codecType = GetCodecTypeStringLC( ActiveUserL().iCodecInfo );
    	TRAPD( err, iFileComposer->OpenFileL( iFileName, 
    	                                      iAudioFourCC, 
    	                                      *codecType ) );  

        CleanupStack::PopAndDestroy( codecType );
    	if ( err == KErrDiskFull )
    	    {
    	    __FILESINK_CONTROLL( "CMccFileSink::SinkPrimeL, disk full" )
    	    iNotifySizeLimitReached = ETrue;
    	    err = KErrNone;
    	    }
    	User::LeaveIfError( err );
    	
    	iDecSpecInfoProvided = EFalse;
        }

    if ( aSendEvent )
        {
    	SendStreamEventToClient( KMccStreamPrepared );	
        }
	}

// -----------------------------------------------------------------------------
// CMccFileSink::DoSinkPlayL
// -----------------------------------------------------------------------------
//
void CMccFileSink::DoSinkPlayL( TBool aSendEvent )
    { 
    __ASSERT_ALWAYS( !iSizeLimitReached, User::Leave( KErrDiskFull ) );
    
    TMccEventType eventType = 
        iCurrentState == EPaused ? KMccStreamResumed : KMccStreamStarted;
    TFileSinkState oldState = iCurrentState;
    
    if ( SetStateL( ERecording ) )
        {    
        // First frame written to file should be keyframe
        iKeyFrameProvided = EFalse;
        
    	if ( oldState == EPaused )
            {
            iT2.HomeTime();
            __FILESINK_CONTROLL_INT1( "CMccFileSink::SinkPlayL, iT2=", iT2.Int64() )
            SetPausedDuration( iT1, iT2 );
            }
        }
	
	if ( aSendEvent )
	    {
        SendStreamEventToClient( eventType );
	    }
    }

// -----------------------------------------------------------------------------
// CMccFileSink::DoSinkPauseL
// -----------------------------------------------------------------------------
//
void CMccFileSink::DoSinkPauseL( TBool aSendEvent )
    { 
    TFileSinkState oldState = iCurrentState;
    
    if ( SetStateL( EPaused ) )
        {  
        if ( oldState == ERecording )
            {
            iT1.HomeTime();  
            __FILESINK_CONTROLL_INT1( "CMccFileSink::SinkPauseL, iT1=", iT1.Int64() )
            }   
        }
    
	if ( aSendEvent )
	    {
	    SendStreamEventToClient( KMccStreamPaused );
	    }
    }
    
// -----------------------------------------------------------------------------
// CMccFileSink::DoSinkStopL
// -----------------------------------------------------------------------------
//
void CMccFileSink::DoSinkStopL( TBool aSendEvent )
    {
    __FILESINK_CONTROLL( "CMccFileSink::DoSinkStopL" )
    
     if ( SetStateL( EStopped ) )
        {  
    	iFileComposer->SinkStopL();
        
        ResetTimers();
        }

    if ( aSendEvent )
        {
	    SendStreamEventToClient( KMccStreamStopped );	
        }
    }	

// -----------------------------------------------------------------------------
// CMccFileSink::DoCodecChangeL
// -----------------------------------------------------------------------------
//
void CMccFileSink::DoCodecChangeL()
    {
    __FILESINK_CONTROLL( "CMccFileSink::DoCodecChangeL" )
    
    TFileSinkState oldState = iCurrentState;
    
    DoSinkStopL( EFalse );
    DoSinkPrimeL( EFalse );
    
    if ( oldState == ERecording || oldState == EPaused )
        {
        DoSinkPlayL( EFalse );
        if ( oldState == EPaused )
            {
            DoSinkPauseL( EFalse );
            }
        }
    
    __FILESINK_CONTROLL( "CMccFileSink::DoCodecChangeL, exit" )
    }

// -----------------------------------------------------------------------------
// CMccFileSink::AddUserL()
// -----------------------------------------------------------------------------
// 
void CMccFileSink::AddUserL( MAsyncEventHandler* aEventHandler )
    {
    TMccFileSinkUser* user = new ( ELeave ) TMccFileSinkUser( aEventHandler );
    CleanupStack::PushL( user );
    iUsers.AppendL( user );
    CleanupStack::Pop( user );
    }

// -----------------------------------------------------------------------------
// CMccFileSink::ActiveUserL()
// -----------------------------------------------------------------------------
//
TMccFileSinkUser& CMccFileSink::ActiveUserL()
    {
    __ASSERT_ALWAYS( iUsers.Count() > 0, User::Leave( KErrNotReady ) );
    TInt index( 0 );
    if ( iActiveUserIndex != KErrNotFound && iActiveUserIndex < iUsers.Count() )
        {
        index = iActiveUserIndex;
        }
    return *iUsers[ index ];
    }

// -----------------------------------------------------------------------------
// CMccFileSink::IsActiveUser()
// -----------------------------------------------------------------------------
//
TBool CMccFileSink::IsActiveUser( MAsyncEventHandler* aUser )
    {
    TInt index = 
        MccUserArray<TMccFileSinkUser>::FindUserEntryIndexForCurrent( iUsers, aUser );
    return ( index != KErrNotFound && index == iActiveUserIndex );
    }

// -----------------------------------------------------------------------------
// CMccFileSink::UpdateActiveUserL()
// -----------------------------------------------------------------------------
//
TFourCC CMccFileSink::UpdateActiveUserL( TMediaId aMediaId, MDataSource& aDataProvider )
    {
    TBool doCodecChange( EFalse );
    
    TFourCC providerDataType = aDataProvider.SourceDataTypeCode( aMediaId );
    
    if ( iActiveUserIndex == KErrNotFound )
        {
        SetActiveUserIndex( 0 );
        doCodecChange = ETrue;
        
        __FILESINK_CONTROLL_INT1( 
            "CMccFileSink::UpdateActiveUserL, new active user index:", iActiveUserIndex )
        }
    else
        {
        if ( ActiveUserL().iCodecInfo.iFourCC != providerDataType )
            {    
            __FILESINK_CONTROLL_INT1( 
                "CMccFileSink::UpdateActiveUserL, provider fourcc:", 
                providerDataType.FourCC() )
            
            __FILESINK_CONTROLL_INT1( 
                "CMccFileSink::UpdateActiveUserL, active user fourcc:", 
                ActiveUserL().iCodecInfo.iFourCC.FourCC() )
            
            for ( TInt i = 0; i < iUsers.Count() && !doCodecChange; i++ )
                {
                if ( iUsers[ i ]->iCodecInfo.iFourCC == providerDataType )
                    {
                    SetActiveUserIndex( i );
                    doCodecChange = ETrue;
                    }
                }
            }
        }
        
    if ( doCodecChange )
        {
        SetCurrentUser( ActiveUserL().iEventHandler );
        DoCodecChangeL();
        }
        
    return providerDataType;
    }

// -----------------------------------------------------------------------------
// CMccFileSink::SetActiveUserIndex()
// -----------------------------------------------------------------------------
//
void CMccFileSink::SetActiveUserIndex( TInt aIndex )
    {
    __FILESINK_CONTROLL_INT1( "CMccFileSink::SetActiveUserIndex, index:", aIndex )
            
    iActiveUserIndex = aIndex;
    }
    
#ifndef EKA2
EXPORT_C TInt E32Dll( TDllReason )
    {
    return KErrNone;
    }
#endif

