diff -r 000000000000 -r 1bce908db942 multimediacommscontroller/mmccfilesourcesink/src/mccfilesink.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/multimediacommscontroller/mmccfilesourcesink/src/mccfilesink.cpp Tue Feb 02 01:04:58 2010 +0200 @@ -0,0 +1,1104 @@ +/* +* 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 +#include +#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( self ); + } + +// ----------------------------------------------------------------------------- +// CMccFileSink::ConstructSinkL +// ----------------------------------------------------------------------------- +// +void CMccFileSink::ConstructSinkL( const TDesC8& aInitData ) + { + __FILESINK_CONTROLL( "CMccFileSink::ConstructSinkL" ) + + iFileComposer = CCamC3GPDataSink::NewL( this ); + iFileComposer->SetSizeLimit( iMaxFileSize ); + + TPckgBuf 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::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::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(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::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( 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(); + + __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( 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( 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::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 +