diff -r 826cea16efd9 -r 13a33d82ad98 dvrengine/CommonRecordingEngine/src/CCREngine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dvrengine/CommonRecordingEngine/src/CCREngine.cpp Wed Sep 01 12:20:37 2010 +0100 @@ -0,0 +1,1592 @@ +/* +* Copyright (c) 2007 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "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: Engine part of the engine. In practice keeps count on* +*/ + + + + +// INCLUDE FILES +#include +#include "VideoServiceUtilsConf.hrh" +#include "CCREngine.h" +#include "CCRStreamingSession.h" +#include "CCRSession.h" +#include "CCRClientInformer.h" +#include "CCRConnection.h" +#ifdef RD_IPTV_FEA_RTP_CLIP_SUPPORT +#include +#include +#endif // RD_IPTV_FEA_RTP_CLIP_SUPPORT +#include +#include +#include +#include +#include "videoserviceutilsLogger.h" + +// CONSTANTS +const TUint KCRELoopbackPortStart( 1106 ); + +// ============================ MEMBER FUNCTIONS =============================== + +// ----------------------------------------------------------------------------- +// CCREngine::NewL +// Two-phased constructor. +// ----------------------------------------------------------------------------- +// +CCREngine* CCREngine::NewL( void ) + { + CCREngine* self = new( ELeave ) CCREngine(); + + // Use CleanupClosePushL to make sure the Close function defined in CObject + // base class gets called. CObject will delete itself once its reference count + // reaches zero. Using CleanupStack::PushL here results in E32USER-CBase panic 33 + // if there is a leave somewhere in construction + CleanupClosePushL( *self ); + + self->ConstructL(); + CleanupStack::Pop( self ); + return self; + } + +// ----------------------------------------------------------------------------- +// CCREngine::CCREngine +// C++ default constructor can NOT contain any code, that might leave. +// ----------------------------------------------------------------------------- +// +CCREngine::CCREngine( void ) : iLoopbackPort( KCRELoopbackPortStart ) + { + // None + } + +// ----------------------------------------------------------------------------- +// CCREngine::ConstructL +// Symbian 2nd phase constructor can leave. +// ----------------------------------------------------------------------------- +// +void CCREngine::ConstructL() + { + LOG( "CCREngine::ConstructL()" ); + + // Note, quite high priority + iCleanUp = new ( ELeave ) CAsyncCallBack( CActive::EPriorityStandard ); + } + +// ----------------------------------------------------------------------------- +// CCREngine::~CCREngine +// Destructor. +// ----------------------------------------------------------------------------- +// +CCREngine::~CCREngine() + { + LOG( "CCREngine::~CCREngine()" ); + + delete iCleanUp; + iSessions.ResetAndDestroy(); + iSessionsToDelete.Reset(); + delete iConnection; +#ifdef RD_IPTV_FEA_RTP_CLIP_SUPPORT + delete iClipHandler; +#endif // RD_IPTV_FEA_RTP_CLIP_SUPPORT + iSockServer.Close(); + delete iInformer; + delete iQueueName; + } + +// ----------------------------------------------------------------------------- +// Method for actual message handling. +// +// ----------------------------------------------------------------------------- +// +void CCREngine::GeneralServiceL( const RMessage2& aMessage ) + { +#if defined( LIVE_TV_RDEBUG_TRACE ) || defined( LIVE_TV_FILE_TRACE ) + if ( aMessage.Function() != ECRGetPosition ) + { + LOG1( "CCREngine::GeneralService(), aMessage: %d", aMessage.Function() ); + } +#endif // LIVE_TV_RDEBUG_TRACE || LIVE_TV_FILE_TRACE + + // Verify cababilities + VerifyCapabilitiesL( aMessage ); + + // Handle message + TInt err( KErrNone ); + switch( aMessage.Function() ) + { + case ECRSetIap: + { + TPckgBuf pckg; + Read( aMessage, 0, pckg ); + err = SetConnectionId( pckg() ); + aMessage.Complete( err ); + } + break; + + case ECRCancelSetIap: + aMessage.Complete( CancelSetConnectionId() ); + break; + + case ECRPlayRtspUrl: + { + TPckgBuf pckg0( 0 ); + TPckgBuf pckg1; + Read( aMessage, 1, pckg1 ); + TRAP( err, pckg0() = PlayRtspUrlL( pckg1() ) ); + if ( !err ) + { + Write( aMessage, 0, pckg0 ); + } + aMessage.Complete( err ); + } + break; + + case ECRPlayDvbhLive: + { + TPckgBuf pckg0( 0 ); + TPckgBuf pckg1; + Read( aMessage, 1, pckg1 ); + TRAP( err, pckg0() = PlayDvbhLiveL( pckg1() ) ); + if ( !err ) + { + Write( aMessage, 0, pckg0 ); + } + aMessage.Complete( err ); + } + break; + + case ECRChangeService: + { + TPckgBuf pckg0( 0 ); + TPckgBuf pckg1; + Read( aMessage, 0, pckg0 ); + Read( aMessage, 1, pckg1 ); + TRAP( err, pckg0() = ChangeDvbhServiceL( pckg0(), pckg1() ) ); + if ( !err ) + { + Write( aMessage, 0, pckg0 ); + } + aMessage.Complete( err ); + } + break; + + case ECRPlayRtpFile: + { + TPckgBuf pckg0( 0 ); + TPckgBuf pckg1; + Read( aMessage, 1, pckg1 ); + TRAP( err, pckg0() = PlayRtpFileL( pckg1() ) ); + if ( !err ) + { + Write( aMessage, 0, pckg0 ); + } + aMessage.Complete( err ); + } + break; + + case ECRPlayRtpHandle: + { + RFile fileHandle; + err = fileHandle.AdoptFromClient( aMessage, 1, 2 ); + if ( !err ) + { + TPckgBuf pckg( 0 ); + TRAP( err, pckg() = PlayRtpFileL( fileHandle ) ); + if ( !err ) + { + Write( aMessage, 0, pckg ); + } + } + fileHandle.Close(); + aMessage.Complete( err ); + } + break; + + case ECRRecordCurrentStream: + { + TPckgBuf pckg0( 0 ); + Read( aMessage, 0, pckg0 ); + TPckgBuf pckg1; + Read( aMessage, 1, pckg1 ); + TRAP( err, RecordCurrentStreamL( pckg0(), pckg1() ) ); + aMessage.Complete( err ); + } + break; + + case ECRRecordRtspStream: + { + TPckgBuf pckg0( 0 ); + TPckgBuf pckg1; + Read( aMessage, 1, pckg1 ); + TPckgBuf pckg2; + Read( aMessage, 2, pckg2 ); + TRAP( err, pckg0() = RecordRtspStreamL( pckg1(), pckg2() ) ); + aMessage.Complete( err ); + } + break; + + case ECRRecordDvbhStream: + { + TPckgBuf pckg0( 0 ); + TPckgBuf pckg1; + Read( aMessage, 1, pckg1 ); + TPckgBuf pckg2; + Read( aMessage, 2, pckg2 ); + TRAP( err, pckg0() = RecordDvbhStreamL( pckg1(), pckg2() ) ); + aMessage.Complete( err ); + } + break; + + case ECRPauseRecordStream: + { + TPckgBuf pckg0( 0 ); + Read( aMessage, 0, pckg0 ); + TPckgBuf pckg1( 0 ); + Read( aMessage, 1, pckg1 ); + aMessage.Complete( PauseRecordStream( pckg0(), pckg1() ) ); + } + break; + + case ECRStopRecordStream: + { + TPckgBuf pckg0( 0 ); + Read( aMessage, 0, pckg0 ); + aMessage.Complete( StopRecordStream( pckg0() ) ); + } + break; + + case ECRStartTimeShift: + { + TPckgBuf pckg0( 0 ); + TPckgBuf pckg1( 0 ); + Read( aMessage, 1, pckg1 ); + TRAP( err, pckg0() = StartTimeShiftL( pckg1() ) ); + if ( !err ) + { + Write( aMessage, 0, pckg0 ); + } + aMessage.Complete( err ); + } + break; + + case ECRStopTimeShift: + { + TPckgBuf pckg0( 0 ); + TPckgBuf pckg1( 0 ); + Read( aMessage, 0, pckg0 ); + Read( aMessage, 1, pckg1 ); + aMessage.Complete( StopTimeShift( pckg0(), pckg1() ) ); + } + break; + + case ECRPlayCommand: + { + TPckgBuf pckg0( 0 ); + Read( aMessage, 0, pckg0 ); + TPckgBuf pckg1( 0 ); + Read( aMessage, 1, pckg1 ); + TPckgBuf pckg2( 0 ); + Read( aMessage, 2, pckg2 ); + aMessage.Complete( PlayCommand( pckg0(), pckg1(), pckg2() ) ); + } + break; + + case ECRPauseCommand: + { + TPckgBuf pckg0( 0 ); + Read( aMessage, 0, pckg0 ); + aMessage.Complete( PauseCommand( pckg0() ) ); + } + break; + + case ECRStopCommand: + { + TPckgBuf pckg0( 0 ); + Read( aMessage, 0, pckg0 ); + aMessage.Complete( StopCommand( pckg0() ) ); + } + break; + + case ECRSetPosition: + { + TPckgBuf pckg0( 0 ); + Read( aMessage, 0, pckg0 ); + TPckgBuf pckg1( 0 ); + Read( aMessage, 1, pckg1 ); + err = SetPosition( pckg0(), pckg1() ); + aMessage.Complete( err ); + } + break; + + case ECRGetPosition: + { + TPckgBuf pckg0( 0 ); + Read( aMessage, 0, pckg0 ); + TPckgBuf pckg1( 0 ); + Read( aMessage, 1, pckg1 ); + TPckgBuf pckg2( 0 ); + err = GetPosition( pckg0(), pckg1(), pckg2() ); + Write( aMessage, 1, pckg1 ); + Write( aMessage, 2, pckg2 ); + aMessage.Complete( err ); + } + break; + + case ECRCloseSession: + { + TPckgBuf pckg0( 0 ); + Read( aMessage, 0, pckg0 ); + aMessage.Complete( CloseSession( pckg0() ) ); + } + break; + + case ECRPlayNullSource: + { + TPckgBuf pckg0( 0 ); + TRAP( err, pckg0() = PlayNullSourceL() ); + Write( aMessage, 0, pckg0 ); + aMessage.Complete( err ); + } + break; + + case ECRPlayRtspUrlToNullSink: + { + TPckgBuf pckg0( 0 ); + TPckgBuf pckg1; + Read( aMessage, 1, pckg1 ); + TRAP( err, pckg0() = PlayRtspUrlToNullSinkL( pckg1() ) ); + Write( aMessage, 0, pckg0 ); + aMessage.Complete( err ); + } + break; + + default: + aMessage.Complete( KErrNotSupported ); + break; + } + } + +//----------------------------------------------------------------------------- +// CCREngine::SessionStop() +// So, a session wants to quit. we can't just delete it here as return. +// Statement would then take us to deleted instance: put up a cleanup +// CAsyncCallBack and return. +//----------------------------------------------------------------------------- +// +void CCREngine::SessionStop( CCRStreamingSession* aSession ) + { + LOG1( "CCREngine::SessionStop(), Delete count: %d", iSessionsToDelete.Count() ); + + // InsertInAddressOrder checks for duplicate, if there is already + // entry for that session, the array will remain unchanged + TRAPD( err, iSessionsToDelete.InsertInAddressOrderL( aSession ) ); + if ( err ) + { + LOG1( "CCREngine::SessionStop(), InsertInAddressOrder leaved: %d", err ); + } + + // If not already active and sessions to delete? + if ( !iCleanUp->IsActive() && iSessionsToDelete.Count() ) + { + TCallBack cb( SessionStopCallBack, this ); + iCleanUp->Set( cb ); + iCleanUp->CallBack(); + } + } + +// ----------------------------------------------------------------------------- +// CCREngine::ConnectionStatusChange +// +// ----------------------------------------------------------------------------- +// +void CCREngine::ConnectionStatusChange( + TInt aSessionId, + TCRConnectionStatus aStatus, + TInt aErr ) + { + LOG3( "CCREngine::ConnectionStatusChange(), aSessionId: %d, aStatus: %d, Error: %d", + aSessionId, aStatus, aErr ); + + SCRQueueEntry entry = { ECRMsgQueueConnectionError, KErrNone }; + entry.iSessionId = aSessionId; + + switch ( aStatus ) + { + case ECRConnectionError: + { + entry.iMsg = ECRMsgQueueConnectionError; + entry.iErr = aErr; + } + break; + + case ECRAuthenticationNeeded: + { + entry.iMsg = ECRMsgQueueAuthenticationNeeded; + entry.iErr = KErrNone; + } + break; + + case ECRNotEnoughBandwidth: + { + entry.iMsg = ECRMsgQueueNotEnoughBandwidth; + entry.iErr = KErrNone; + } + break; + + case ECRNormalEndOfStream: + { + entry.iMsg = ECRMsgQueueNormalEndOfStream; + entry.iErr = aErr; + } + break; + + case ECRAttachCompleted: // fall through + case ECRSwitchingToTcp: + { + aStatus == ECRAttachCompleted ? + ( entry.iMsg = ECRMsgQueueAttachCompleted ) : + ( entry.iMsg = ECRMsgQueueSwitchingToTcp ); + + if ( aErr == KErrNone ) + { + // Releasing RSocket takes almost 40 seconds so we need + // to always use different port number, otherwise an + // "Already in use" error will be occurred. + TTime now; + now.UniversalTime(); + TInt port = KCRELoopbackPortStart + now.DateTime().Second(); + iLoopbackPort == port ? ( iLoopbackPort = port + 1 ) : + ( iLoopbackPort = port ); + + // Loopback port for VIA + entry.iErr = iLoopbackPort; + } + else + { + entry.iErr = aErr; + } + } + break; + + case ECRStreamIsLiveStream: + { + entry.iMsg = ECRMsgQueueStreamIsLiveStream; + entry.iErr = aErr; + } + break; + + case ECRStreamIsRealMedia: + { + entry.iMsg = ECRMsgQueueStreamIsRealMedia; + entry.iErr = aErr; + } + break; + + case ECRBearerChanged: + { + // Nobody is interested about this on the other side... + return; + } + + case ECRTestSinkData: + { + entry.iMsg = ECRMsgQueueTestSinkData; + entry.iErr = aErr; + } + break; + + case ECRSdpAvailable: + { + entry.iMsg = ECRMsgQueueSdpAvailable; + entry.iErr = aErr; + } + break; + + case ECRReadyToSeek: + { + entry.iMsg = ECRMsgQueueReadyToSeek; + entry.iErr = aErr; + } + break; + + case ECRRecordingStarted: + { + entry.iMsg = ECRMsgQueueRecordingStarted; + entry.iErr = aErr; + } + break; + + case ECRRecordingPaused: + { + entry.iMsg = ECRMsgQueueRecordingPaused; + entry.iErr = aErr; + } + break; + + case ECRRecordingEnded: + { + entry.iMsg = ECRMsgQueueRecordingEnded; + entry.iErr = aErr; + } + break; + + default: + // None + break; + } + + if ( iQueueName && iInformer ) + { + iInformer->SendMessage( iQueueName->Des(), entry ); + } + } + +// ----------------------------------------------------------------------------- +// CCREngine::VerifyCapabilitiesL +// Checks capabilities of user of DVR API. +// ----------------------------------------------------------------------------- +// +void CCREngine::VerifyCapabilitiesL( const RMessage2& aMessage ) + { + if ( !aMessage.HasCapability( ECapabilityNetworkServices, + __PLATSEC_DIAGNOSTIC_STRING( "NetworkServices" ) ) ) + { + LOG( "CCREngine::VerifyRbfCapabilitiesL(), Missing: NetworkServices !" ); + User::Leave( KErrPermissionDenied ); + } + + if ( !aMessage.HasCapability( ECapabilityReadUserData, + __PLATSEC_DIAGNOSTIC_STRING( "ReadUserData" ) ) ) + { + LOG( "CCREngine::VerifyRbfCapabilitiesL(), Missing: ReadUserData !" ); + User::Leave( KErrPermissionDenied ); + } + + if ( !aMessage.HasCapability( ECapabilityWriteUserData, + __PLATSEC_DIAGNOSTIC_STRING( "WriteUserData" ) ) ) + { + LOG( "CCREngine::VerifyRbfCapabilitiesL(), Missing: WriteUserData !" ); + User::Leave( KErrPermissionDenied ); + } + } + +// ----------------------------------------------------------------------------- +// CCREngine::SetConnectionId +// +// ----------------------------------------------------------------------------- +// +TInt CCREngine::SetConnectionId( const SCRRtspIapParams& aIap ) + { + LOG( "CCREngine::SetConnectionId() in" ); + + TRAPD ( err, CreateConnectionL() ) ; + if ( err == KErrNone ) + { + delete iQueueName; iQueueName = NULL; + TRAP( err, iQueueName = aIap.iQueueName.AllocL() ); + if ( err == KErrNone ) + { + iConnection->Attach( aIap.iConnectionId ); + } + + } + LOG1( "CCREngine::SetConnectionId() out, err: %d", err ); + return err; + } + +// ----------------------------------------------------------------------------- +// CCREngine::CancelSetConnectionId +// +// ----------------------------------------------------------------------------- +// +TInt CCREngine::CancelSetConnectionId() + { + LOG( "CCREngine::CancelSetConnectionId() in" ); + + TInt err( KErrNone ); + if ( iConnection ) + { + iSessions.ResetAndDestroy(); + delete iConnection; iConnection = NULL; + iSockServer.Close(); + err = iSockServer.Connect( KESockDefaultMessageSlots * 3 ); + if ( err == KErrNone ) + { + TRAP( err, iConnection = CCRConnection::NewL( iSockServer ) ); + if ( err == KErrNone && iConnection ) + { + err = iConnection->RegisterObserver( this ); + } + } + } + + LOG1( "CCREngine::CancelSetConnectionId() out, err: %d", err ); + return err; + } + +// ----------------------------------------------------------------------------- +// CCREngine::PlayRtspUrlL +// Go through stream sessions and try to find if given url is already playing. +// If not, create new RTSP session and add sink to it. +// ----------------------------------------------------------------------------- +// +TUint CCREngine::PlayRtspUrlL( const SCRRtspParams& aRtspParams ) + { + LOG( "CCREngine::PlayRtspUrlL() in" ); + + const TInt sessionIndex( VerifyRtspSessionL( aRtspParams ) ); + iSessions[sessionIndex]->CreateRtspSinkL( iLoopbackPort ); + TUint sessionChk( iSessions[sessionIndex]->SourceChecksum() ); + LOG1( "CCREngine::PlayRtspUrlL() out, sessionChk: %d", sessionChk ); + return sessionChk; + } + +// ----------------------------------------------------------------------------- +// CCREngine::PlayDvbhLiveL +// Go through stream sessions and try to find if current service is already +// If not, create new DVB-H session and add sink to it. +// ----------------------------------------------------------------------------- +// +TUint CCREngine::PlayDvbhLiveL( const SCRLiveParams& aLiveParams ) + { + LOG( "CCREngine::PlayDvbhLiveL() in" ); + + const TInt sessionIndex( VerifyDvbhSessionL( aLiveParams ) ); + iSessions[sessionIndex]->CreateXpsSinkL(); + iSessions[sessionIndex]->PostActionL(); + TUint sessionChk( iSessions[sessionIndex]->SourceChecksum() ); + LOG1( "CCREngine::PlayDvbhLiveL() out, sessionChk: %d", sessionChk ); + return sessionChk; + } + +// ----------------------------------------------------------------------------- +// CCREngine::ChangeDvbhServiceL +// Go through stream sessions and try to find if current service is already +// If not, create new DVB-H session and add sink to it. +// ----------------------------------------------------------------------------- +// +TUint CCREngine::ChangeDvbhServiceL( + const TUint aSessionChk, + const SCRLiveParams& aLiveParams ) + { + LOG( "CCREngine::ChangeDvbhServiceL() in" ); + + // Verify exist session + const TInt currentSession( VerifySession( aSessionChk ) ); + User::LeaveIfError( currentSession ); + + // Can't perform if recording or timeshift ongoing + if ( iSessions[currentSession]->ClipHandlerUsed() ) + { + User::Leave( KErrInUse ); + } + + // New session for new service + const TInt sessionIndex( VerifyDvbhSessionL( aLiveParams ) ); + if ( sessionIndex == currentSession ) + { + // Same service, no actions + return aSessionChk; + } + + // Stop sockets of DVB-H source (filters released) + User::LeaveIfError( iSessions[currentSession]->StopCommand() ); + + // Transfer ownership of existing XPS sink to a new session + User::LeaveIfError( iSessions[currentSession]->TransferSink( + CCRStreamingSession::ECRXpsSinkId, *iSessions[sessionIndex] ) ); + iSessions[sessionIndex]->PostActionL(); + TUint sessionChk( iSessions[sessionIndex]->SourceChecksum() ); + + // Delete existing unused session + SessionStop( iSessions[currentSession] ); + LOG1( "CCREngine::ChangeDvbhServiceL() out, sessionChk: %d", sessionChk ); + return sessionChk; + } + +// ----------------------------------------------------------------------------- +// CCREngine::PlayRtpFileL +// +// ----------------------------------------------------------------------------- +// +TUint CCREngine::PlayRtpFileL( const SCRRtpPlayParams& aRtpParams ) + { + LOG( "CCREngine::PlayRtpFileL() in" ); + + const TInt sessionIndex( VerifyRtpFileSessionL( aRtpParams ) ); + iSessions[sessionIndex]->CreateXpsSinkL(); + iSessions[sessionIndex]->PostActionL(); + TUint sessionChk( iSessions[sessionIndex]->SourceChecksum() ); + LOG1( "CCREngine::PlayRtpFileL() out, sessionChk: %d", sessionChk ); + return sessionChk; + } + +// ----------------------------------------------------------------------------- +// CCREngine::PlayRtpFileL +// +// ----------------------------------------------------------------------------- +// +TUint CCREngine::PlayRtpFileL( const RFile& aFileHandle ) + { + LOG( "CCREngine::PlayRtpFileL() in, with hadle" ); + + TInt sessionIndex( VerifyRtpFileSessionL( aFileHandle ) ); + iSessions[sessionIndex]->CreateXpsSinkL(); + iSessions[sessionIndex]->PostActionL(); + TUint sessionChk( iSessions[sessionIndex]->SourceChecksum() ); + LOG1( "CCREngine::PlayRtpFileL() out, sessionChk: %d", sessionChk ); + return sessionChk; + } + +// ----------------------------------------------------------------------------- +// CCREngine::RecordCurrentStreamL +// +// ----------------------------------------------------------------------------- +// +void CCREngine::RecordCurrentStreamL( + const TUint aSessionChk, + const SCRRecordParams& aRecordParams ) + { + LOG1( "CCREngine::StartRecordStreamL() in, aSessionChk: %d", aSessionChk ); + + // Verify session + const TInt sessionIndex( VerifySession( aSessionChk ) ); + + // Session exist? + if ( sessionIndex > KErrNotFound ) + { + CreateRecordingSinkL( sessionIndex, aRecordParams ); + iSessions[sessionIndex]->PostActionL(); + } + + LOG1( "CCREngine::StartRecordStreamL() out, sessionIndex: %d", sessionIndex ); + } + +// ----------------------------------------------------------------------------- +// CCREngine::RecordRtspStreamL +// +// ----------------------------------------------------------------------------- +// +TUint CCREngine::RecordRtspStreamL( + const SCRRtspParams& aRtspParams, + const SCRRecordParams& aRecordParams ) + { + LOG( "CCREngine::RecordRtspStreamL() in" ); + + // Verify session + const TInt sessionIndex( VerifyRtspSessionL( aRtspParams ) ); + + // Recording sink + CreateRecordingSinkL( sessionIndex, aRecordParams ); + TUint sessionChk( iSessions[sessionIndex]->SourceChecksum() ); + LOG1( "CCREngine::RecordRtspStreamL() out, sessionChk: %d", sessionChk ); + return sessionChk; + } + +// ----------------------------------------------------------------------------- +// CCREngine::RecordDvbhStreamL +// +// ----------------------------------------------------------------------------- +// +TUint CCREngine::RecordDvbhStreamL( + const SCRLiveParams& aLiveParams, + const SCRRecordParams& aRecordParams ) + { + LOG( "CCREngine::RecordDvbhStreamL() in" ); + + // Verify session + const TInt sessionIndex( VerifyDvbhSessionL( aLiveParams ) ); + + // Recording sink + CreateRecordingSinkL( sessionIndex, aRecordParams ); + iSessions[sessionIndex]->PostActionL(); + TUint sessionChk( iSessions[sessionIndex]->SourceChecksum() ); + LOG1( "CCREngine::RecordDvbhStreamL() out, sessionChk: %d", sessionChk ); + return sessionChk; + } + +// ----------------------------------------------------------------------------- +// CCREngine::PauseRecordStream +// +// ----------------------------------------------------------------------------- +// +TInt CCREngine::PauseRecordStream( const TUint aSessionChk, const TBool& aStart ) + { + LOG2( "CCREngine::PauseRecordStream() in, aSessionChk: %d, aStart: %d", + aSessionChk, aStart ); + // Verify session + const TInt sessionIndex( VerifySession( aSessionChk ) ); + + // Session exist? + if ( sessionIndex > KErrNotFound ) + { + TInt err( KErrNone ); + if ( aStart ) + { + // Clip format not known, so all types must try to pause + err = iSessions[sessionIndex]->PauseCommand( + CCRStreamingSession::ECRRtpRecSinkId ); + if ( err == KErrCompletion ) + { + err = iSessions[sessionIndex]->PauseCommand( + CCRStreamingSession::ECR3gpRecSinkId ); + } + + ConnectionStatusChange( aSessionChk, ECRRecordingPaused, err ); + } + else + { + // Clip format not known, so all types must try to pause + err = iSessions[sessionIndex]->RestoreCommand( + CCRStreamingSession::ECRRtpRecSinkId ); + if ( err == KErrCompletion ) + { + err = iSessions[sessionIndex]->RestoreCommand( + CCRStreamingSession::ECR3gpRecSinkId ); + } + + ConnectionStatusChange( aSessionChk, ECRRecordingStarted, err ); + } + + LOG1( "CCREngine::PauseRecordStream() out, err: %d", err ); + return err; + } + + LOG1( "CCREngine::PauseRecordStream() out, sessionIndex: %d", sessionIndex ); + return sessionIndex; + } + +// ----------------------------------------------------------------------------- +// CCREngine::StopRecordStream +// +// ----------------------------------------------------------------------------- +// +TInt CCREngine::StopRecordStream( const TUint aSessionChk ) + { + LOG1( "CCREngine::StopRecordStream() in, aSessionChk: %d", aSessionChk ); + + // Verify session + const TInt sessionIndex( VerifySession( aSessionChk ) ); + + // Session exist? + if ( sessionIndex > KErrNotFound ) + { + // Clip format not known, so all types must try to stop + iSessions[sessionIndex]->StopCommand( + CCRStreamingSession::ECRRtpRecSinkId ); + iSessions[sessionIndex]->StopCommand( + CCRStreamingSession::ECR3gpRecSinkId ); + // Possible error(s) ignored + } + + LOG1( "CCREngine::StopRecordStream() out, sessionIndex: %d", sessionIndex ); + return sessionIndex; + } + +// ----------------------------------------------------------------------------- +// CCREngine::StartTimeShift +// Existing stream play will be splitted to two sessions. Existing source will +// be directed to recording and existing sink will get new source from playback. +// ----------------------------------------------------------------------------- +// +TInt CCREngine::StartTimeShiftL( const TUint aSessionChk ) + { + LOG1( "CCREngine::StartTimeShiftL() in, aSessionChk: %d", aSessionChk ); + +#ifdef RD_IPTV_FEA_RTP_CLIP_SUPPORT + + // Verify session + const TInt currentSession( VerifySession( aSessionChk ) ); + User::LeaveIfError( currentSession ); + + // Initial time shift clip name + TPath initialName( KDvrTimeShiftFile ); + initialName.AppendNum( 0 ); + + // RTP clip handler + CreateClipHandlerL(); + + // Start recording of stream + SCRRecordParams recordParams; + recordParams.iFileName.Set( initialName ); + recordParams.iFormat = ECRRecordTimeShift; + iSessions[currentSession]->CreateRtpRecordSinkL( recordParams, iClipHandler ); + iSessions[currentSession]->PostActionL(); + + // Create new session for time shift clip playback + SCRRtpPlayParams params; + params.iFileName = initialName; + const TInt timeShiftSession( VerifyRtpFileSessionL( params ) ); + iSessions[timeShiftSession]->PostActionL(); + + // Transfer ownership of existing XPS sink to a new session + User::LeaveIfError( iSessions[currentSession]->TransferSink( + CCRStreamingSession::ECRXpsSinkId, *iSessions[timeShiftSession] ) ); + + // Session checksum + TUint sessionChk( iSessions[timeShiftSession]->SourceChecksum() ); + LOG1( "CCREngine::StartTimeShiftL() out, sessionChk: %d", sessionChk ); + return sessionChk; + +#else // RD_IPTV_FEA_RTP_CLIP_SUPPORT + ( void )aSessionChk; + return KErrNotSupported; +#endif // RD_IPTV_FEA_RTP_CLIP_SUPPORT + } + +// ----------------------------------------------------------------------------- +// CCREngine::StopTimeShift +// Streaming is set back to one session. Streaming orginal source stays and sink +// from temporary playback will be moved back to orginal session. +// ----------------------------------------------------------------------------- +// +TInt CCREngine::StopTimeShift( + const TUint aTimeShiftChk, + const TUint aCurrentChk ) + { + LOG2( "CCREngine::StopTimeShift() in, aTimeShiftChk: %d, aCurrentChk: %d", + aTimeShiftChk, aCurrentChk ); +#ifdef RD_IPTV_FEA_RTP_CLIP_SUPPORT + + // Verify session + int ret( KErrNotFound ); + const TInt currentSession( VerifySession( aCurrentChk ) ); + const TInt timeShiftSession( VerifySession( aTimeShiftChk ) ); + + // Session exist? + if ( currentSession > KErrNotFound && timeShiftSession > KErrNotFound ) + { + // Stop time shift clip recording + iSessions[currentSession]->StopCommand( + CCRStreamingSession::ECR3gpRecSinkId ); + + // Stop time shift clip playback + iSessions[timeShiftSession]->StopCommand(); + + // Transfer ownership of existing XPS sink back to the old session + ret = iSessions[timeShiftSession]->TransferSink( + CCRStreamingSession::ECRXpsSinkId, *iSessions[currentSession] ); + + // Delete second session + SessionStop( iSessions[timeShiftSession] ); + } + + // Clip handler not needed any longer + DeleteClipHandler( currentSession ); + + LOG1( "CCREngine::StopTimeShift() out, ret: %d", ret ); + return ret; + +#else // RD_IPTV_FEA_RTP_CLIP_SUPPORT + ( void )aTimeShiftChk; + ( void )aCurrentChk; + return KErrNotSupported; +#endif // RD_IPTV_FEA_RTP_CLIP_SUPPORT + } + +// ----------------------------------------------------------------------------- +// CCREngine::PlayCommand +// +// ----------------------------------------------------------------------------- +// +TInt CCREngine::PlayCommand( + const TUint aSessionChk, + const TReal aStartPos, + const TReal aEndPos ) + { + LOG1( "CCREngine::PlayCommand(), aSessionChk: %d", aSessionChk ); + + // Verify session + const TInt sessionIndex( VerifySession( aSessionChk ) ); + + // New session needed? + if ( sessionIndex > KErrNotFound ) + { + return iSessions[sessionIndex]->PlayCommand( aStartPos, aEndPos ); + } + + return KErrNotReady; + } + +// ----------------------------------------------------------------------------- +// CCREngine::PauseCommand +// +// ----------------------------------------------------------------------------- +// +TInt CCREngine::PauseCommand( const TUint aSessionChk ) + { + LOG1( "CCREngine::PauseCommand(), aSessionChk: %d", aSessionChk ); + + // Verify session + const TInt sessionIndex( VerifySession( aSessionChk ) ); + + // New session needed? + if ( sessionIndex > KErrNotFound ) + { + return iSessions[sessionIndex]->PauseCommand(); + } + + return KErrNotReady; + } + +// ----------------------------------------------------------------------------- +// CCREngine::StopCommand +// +// ----------------------------------------------------------------------------- +// +TInt CCREngine::StopCommand( const TUint aSessionChk ) + { + LOG1( "CCREngine::StopCommand(), aSessionChk: %d", aSessionChk ); + + // Verify session + const TInt sessionIndex( VerifySession( aSessionChk ) ); + + // New session needed? + if ( sessionIndex > KErrNotFound ) + { + return iSessions[sessionIndex]->StopCommand(); + } + + return KErrNotReady; + } + +// ----------------------------------------------------------------------------- +// CCREngine::SetPosition +// +// ----------------------------------------------------------------------------- +// +TInt CCREngine::SetPosition( + const TUint aSessionChk, + const TInt64 aPosition ) + { + LOG1( "CCREngine::SetPosition(), aSessionChk: %d", aSessionChk ); + + const TInt sessionIndex( VerifySession( aSessionChk ) ); + if ( sessionIndex > KErrNotFound ) + { + return iSessions[sessionIndex]->SetPosition( aPosition ); + } + + return KErrNotReady; + } + +// ----------------------------------------------------------------------------- +// CCREngine::GetPosition +// +// ----------------------------------------------------------------------------- +// +TInt CCREngine::GetPosition( + const TUint aSessionChk, + TInt64& aPosition, + TInt64& aDuration ) + { + const TInt sessionIndex( VerifySession( aSessionChk ) ); + if ( sessionIndex > KErrNotFound ) + { + return iSessions[sessionIndex]->GetPosition( aPosition, aDuration ); + } + + return KErrNotReady; + } + +// ----------------------------------------------------------------------------- +// CCREngine::CloseSession +// +// ----------------------------------------------------------------------------- +// +TInt CCREngine::CloseSession( const TUint aSessionChk ) + { + LOG1( "CCREngine::CloseSession(), aSessionChk: %d", aSessionChk ); + + // Verify session + const TInt sessionIndex( VerifySession( aSessionChk ) ); + if ( sessionIndex > KErrNotFound ) + { + SessionStop( iSessions[sessionIndex] ); + } + + return KErrNone; + } + +// ----------------------------------------------------------------------------- +// CCREngine::PlayNullSourceL +// +// ----------------------------------------------------------------------------- +// +TUint CCREngine::PlayNullSourceL() + { + LOG( "CCREngine::PlayNullSourceL() in" ); + + _LIT( KNullSourceClip, "NullSource.rtp" ); + TFileName nullSourcePath; +#if ( defined( __WINS__ ) || defined( __WINSCW__ ) ) + nullSourcePath = PathInfo::PhoneMemoryRootPath(); +#else // __WINS__ || __WINSCW__ + nullSourcePath = PathInfo::MemoryCardRootPath(); +#endif // __WINS__ || __WINSCW__ + nullSourcePath.Append( KNullSourceClip ); + + TInt sessionIndex( VerifySession( nullSourcePath ) ); + if ( sessionIndex == KErrNotFound ) + { + CCRStreamingSession* session = CCRStreamingSession::NewL( + iSockServer, iConnection, *this ); + CleanupStack::PushL( session ); + User::LeaveIfError( iSessions.Append( session ) ); + CleanupStack::Pop( session ); + + // Only one source per session + sessionIndex = iSessions.Count() - 1; + iSessions[sessionIndex]->OpenSourceL( nullSourcePath ); + } + + iSessions[sessionIndex]->CreateXpsSinkL(); + //iSessions[sessionIndex]->CreateNullSinkL(); + iSessions[sessionIndex]->PostActionL(); + TUint sessionChk( iSessions[sessionIndex]->SourceChecksum() ); + LOG1( "CCREngine::PlayNullSourceL() out, sessionChk: %d", sessionChk ); + return sessionChk; + } + +// ----------------------------------------------------------------------------- +// CCREngine::PlayRtspUrlToNullSinkL +// +// ----------------------------------------------------------------------------- +// +TUint CCREngine::PlayRtspUrlToNullSinkL( const SCRRtspParams& aRtspParams ) + { + LOG( "CCREngine::PlayRtspUrlToNullSinkL() in" ); + + const TInt sessionIndex( VerifyRtspSessionL( aRtspParams ) ); + iSessions[sessionIndex]->CreateNullSinkL(); + TUint sessionChk( iSessions[sessionIndex]->SourceChecksum() ); + LOG1( "CCREngine::PlayRtspUrlToNullSinkL() out, sessionChk: %d", sessionChk ); + return sessionChk; + } + +// ----------------------------------------------------------------------------- +// CCREngine::CreateConnectionL +// +// ----------------------------------------------------------------------------- +// +void CCREngine::CreateConnectionL( void ) + { + LOG( "CCREngine::CreateConnectionL() in" ); + + if ( iConnection == NULL ) + { + User::LeaveIfError( iSockServer.Connect( KESockDefaultMessageSlots * 3 ) ); + iConnection = CCRConnection::NewL( iSockServer ); + User::LeaveIfError( iConnection->RegisterObserver( this ) ); + + // Informer + if ( iInformer == NULL ) + { + iInformer = CCRClientInformer::NewL(); + } + } + + LOG( "CCREngine::CreateConnectionL() out" ); + } + +// ----------------------------------------------------------------------------- +// CCREngine::CreateClipHandlerL +// +// ----------------------------------------------------------------------------- +// +void CCREngine::CreateClipHandlerL( void ) + { + LOG1( "CCREngine::CreateClipHandlerL(), iClipHandler: %d", iClipHandler ); + +#ifdef RD_IPTV_FEA_RTP_CLIP_SUPPORT + + if ( !iClipHandler ) + { + iClipHandler = CRtpClipHandler::NewL(); + } +#endif // RD_IPTV_FEA_RTP_CLIP_SUPPORT + } + +// ----------------------------------------------------------------------------- +// CCREngine::VerifyRtspSessionL +// Go through stream sessions and try to find if RTSP stream is already playing. +// ----------------------------------------------------------------------------- +// +TInt CCREngine::VerifyRtspSessionL( const SCRRtspParams& aRtspParams ) + { + TInt sessionIndex( VerifySession( aRtspParams.iUrl ) ); + + // New session needed? + if ( sessionIndex == KErrNotFound ) + { + CCRStreamingSession* session = CCRStreamingSession::NewL( + iSockServer, iConnection, *this ); + CleanupStack::PushL( session ); + User::LeaveIfError( iSessions.Append( session ) ); + CleanupStack::Pop( session ); + + // Only one source per session + sessionIndex = iSessions.Count() - 1; + iSessions[sessionIndex]->OpenSourceL( aRtspParams, aRtspParams.iUrl ); + } + + User::LeaveIfError( sessionIndex ); + return sessionIndex; + } + +// ----------------------------------------------------------------------------- +// CCREngine::VerifyDvbhSessionL +// Go through stream sessions and try to find if DVB-H live is already playing. +// ----------------------------------------------------------------------------- +// +TInt CCREngine::VerifyDvbhSessionL( const SCRLiveParams& aLiveParams ) + { + // Verify session + HBufC* definition = HBufC::NewLC( aLiveParams.iSdpData.Length() ); + definition->Des().Copy( aLiveParams.iSdpData ); + TInt sessionIndex( VerifySession( *definition ) ); + + // New session needed? + if ( sessionIndex == KErrNotFound ) + { + CCRStreamingSession* session = CCRStreamingSession::NewL( + iSockServer, iConnection, *this ); + CleanupStack::PushL( session ); + User::LeaveIfError( iSessions.Append( session ) ); + CleanupStack::Pop( session ); + + // Only one source per session + sessionIndex = iSessions.Count() - 1; + iSessions[sessionIndex]->OpenSourceL( aLiveParams, *definition ); + } + + CleanupStack::PopAndDestroy( definition ); + User::LeaveIfError( sessionIndex ); + return sessionIndex; + } + +// ----------------------------------------------------------------------------- +// CCREngine::VerifyRtpFileSessionL +// Go through stream sessions and try to find if RTP clip is already playing. +// ----------------------------------------------------------------------------- +// +TInt CCREngine::VerifyRtpFileSessionL( const SCRRtpPlayParams& aRtpParams ) + { +#ifdef RD_IPTV_FEA_RTP_CLIP_SUPPORT + + // Verify session + TInt sessionIndex( VerifySession( aRtpParams.iFileName ) ); + + // New session needed? + if ( sessionIndex == KErrNotFound ) + { + + CCRStreamingSession* session = CCRStreamingSession::NewL( + iSockServer, iConnection, *this ); + CleanupStack::PushL( session ); + User::LeaveIfError( iSessions.Append( session ) ); + CleanupStack::Pop( session ); + + // RTP clip handler + CreateClipHandlerL(); + + // Only one source per session + sessionIndex = iSessions.Count() - 1; + iSessions[sessionIndex]->OpenSourceL( aRtpParams, iClipHandler, + aRtpParams.iFileName ); + } + + User::LeaveIfError( sessionIndex ); + return sessionIndex; + +#else // RD_IPTV_FEA_RTP_CLIP_SUPPORT + ( void )aRtpParams; + return KErrNotSupported; +#endif // RD_IPTV_FEA_RTP_CLIP_SUPPORT + } + +// ----------------------------------------------------------------------------- +// CCREngine::VerifyRtpFileSessionL +// Go through stream sessions and try to find if RTP clip is already playing. +// ----------------------------------------------------------------------------- +// +TInt CCREngine::VerifyRtpFileSessionL( const RFile& aFileHandle ) + { +#ifdef RD_IPTV_FEA_RTP_CLIP_SUPPORT + + TFileName fileName( KNullDesC ); + aFileHandle.FullName( fileName ); + TInt sessionIndex( VerifySession( fileName ) ); + + // New session needed? + if ( sessionIndex == KErrNotFound ) + { + CCRStreamingSession* session = CCRStreamingSession::NewL( + iSockServer, iConnection, *this ); + CleanupStack::PushL( session ); + User::LeaveIfError( iSessions.Append( session ) ); + CleanupStack::Pop( session ); + + // RTP clip handler + CreateClipHandlerL(); + + // Only one source per session + sessionIndex = iSessions.Count() - 1; + iSessions[sessionIndex]->OpenSourceL( aFileHandle, iClipHandler, fileName ); + } + + User::LeaveIfError( sessionIndex ); + return sessionIndex; + +#else // RD_IPTV_FEA_RTP_CLIP_SUPPORT + ( void )aFileHandle; + return KErrNotSupported; +#endif // RD_IPTV_FEA_RTP_CLIP_SUPPORT + } + +// ----------------------------------------------------------------------------- +// CCREngine::VerifySession +// Go through stream sessions and try to find stream is already playing. +// ----------------------------------------------------------------------------- +// +TInt CCREngine::VerifySession( const TDesC& aName ) + { + if ( iSessions.Count() > 0 ) + { + for ( TInt i( iSessions.Count() - 1 ); i >= 0; i-- ) + { + const TUint chksm( iSessions[i]->SourceDefinition( aName ) ); + if ( chksm == iSessions[i]->SourceChecksum() ) + { + return i; + } + } + } + + return KErrNotFound; + } + +// ----------------------------------------------------------------------------- +// CCREngine::VerifySession +// Go through stream sessions and try to find session index from active sessions. +// ----------------------------------------------------------------------------- +// +TInt CCREngine::VerifySession( const TUint aSessionChk ) + { + if ( iSessions.Count() > 0 ) + { + for ( TInt i( iSessions.Count() - 1 ); i >= 0; i-- ) + { + if ( aSessionChk == iSessions[i]->SourceChecksum() ) + { + return i; + } + } + } + + return KErrNotFound; + } + +//----------------------------------------------------------------------------- +// CCREngine::CreateRecordingSinkL +// +//----------------------------------------------------------------------------- +// +void CCREngine::CreateRecordingSinkL( + const TInt aSessionIndex, + const SCRRecordParams& aRecordParams ) + { + LOG1( "CCREngine::CreateRecordingSinkL(), aSessionIndex: %d", aSessionIndex ); + + User::LeaveIfError( aSessionIndex ); + switch ( aRecordParams.iFormat ) + { +#ifdef RD_IPTV_FEA_RTP_CLIP_SUPPORT + case ECRRecordFormatRtp: + // RTP clip handler + CreateClipHandlerL(); + iSessions[aSessionIndex]->CreateRtpRecordSinkL( + aRecordParams, iClipHandler ); + break; +#endif // RD_IPTV_FEA_RTP_CLIP_SUPPORT + + case ECRRecordFormat3gp: + iSessions[aSessionIndex]->Create3gpRecordSinkL( aRecordParams ); + break; + + default: + LOG( "CCREngine::CreateRecordingSinkL(), Clip format invalid !"); + User::Leave( KErrNotSupported ); + break; + } + } + +//----------------------------------------------------------------------------- +// CCREngine::SessionStopCallBack +// +//----------------------------------------------------------------------------- +// +TInt CCREngine::SessionStopCallBack ( TAny* aThis ) + { + LOG( "CCREngine::SessionStopCallBack()" ); + + CCREngine* self = static_cast( aThis ); + self->DoSessionStop(); + return self->iSessionsToDelete.Count(); + } + +//----------------------------------------------------------------------------- +// CCREngine::DoSessionStop +// +//----------------------------------------------------------------------------- +// +void CCREngine::DoSessionStop( void ) + { + LOG1( "CCREngine::DoSessionStop() in, iSessionsToDelete.Count = %d", iSessionsToDelete.Count() ); + + for ( TInt i( iSessionsToDelete.Count() - 1 ); i >= 0; i-- ) + { + for ( TInt j( iSessions.Count() - 1 ); j >= 0; j-- ) + { + if ( iSessions[j] == iSessionsToDelete[i] ) + { + delete iSessions[j]; + iSessions.Remove( j ); + } + } + } + + // Supposed to be empty by now + DeleteClipHandler( KErrNotFound ); + iSessionsToDelete.Reset(); + LOG( "CCREngine::DoSessionStop() out" ); + } + +//----------------------------------------------------------------------------- +// CCREngine::DeleteClipHandler +// +//----------------------------------------------------------------------------- +// +void CCREngine::DeleteClipHandler( const TInt aCurrentSessionIndex ) + { + LOG1( "CCREngine::DeleteClipHandler(), aCurrentSessionIndex: %d", + aCurrentSessionIndex ); +#ifdef RD_IPTV_FEA_RTP_CLIP_SUPPORT + + if ( iClipHandler ) + { + // Find out clip handler usage + TBool notUsed( ETrue ); + for ( TInt i( iSessions.Count() - 1 ); i >= 0; i-- ) + { + if ( i != aCurrentSessionIndex && iSessions[i]->ClipHandlerUsed() ) + { + notUsed = EFalse; + } + } + + // Clip handler not needed any longer? + if ( notUsed ) + { + delete iClipHandler; iClipHandler = NULL; + LOG( "CCREngine::DeleteClipHandler(), RTP ClipHandler deleted !" ); + } + } + +#else // RD_IPTV_FEA_RTP_CLIP_SUPPORT + ( void )aCurrentSessionIndex; +#endif // RD_IPTV_FEA_RTP_CLIP_SUPPORT + } + +// ----------------------------------------------------------------------------- +// CCREngine::Read +// Read from the client thread, if unsuccessful, panic the client. +// ----------------------------------------------------------------------------- +// +void CCREngine::Read( + const RMessage2& aMessage, + const TInt& aParam, + TDes8& aDes ) + { + TRAPD( err, aMessage.ReadL( aParam, aDes ) ); + if ( err ) + { + PanicClient( ECRPanicBadDescriptor, aMessage ); + } + } + +// ----------------------------------------------------------------------------- +// CCREngine::Read +// Read from the client thread, if unsuccessful, panic the client. +// ----------------------------------------------------------------------------- +// +void CCREngine::Read( + const RMessage2& aMessage, + const TInt& aParam, + TDes16& aDes ) + { + TRAPD( err, aMessage.ReadL( aParam, aDes ) ); + if ( err ) + { + PanicClient( ECRPanicBadDescriptor, aMessage ); + } + } + +// ----------------------------------------------------------------------------- +// CCREngine::Write +// Write to the client thread, if unsuccessful, panic the client. +// ----------------------------------------------------------------------------- +// +void CCREngine::Write( + const RMessage2& aMessage, + const TInt& aParam, + const TDesC8& aDes ) + { + TRAPD( err, aMessage.WriteL( aParam, aDes ) ); + if ( err ) + { + PanicClient( ECRPanicBadDescriptor, aMessage ); + } + } + +// ----------------------------------------------------------------------------- +// CCREngine::Write +// Write to the client thread, if unsuccessful, panic the client. +// ----------------------------------------------------------------------------- +// +void CCREngine::Write( + const RMessage2& aMessage, + const TInt& aParam, + const TDesC16& aDes ) + { + TRAPD( err, aMessage.WriteL( aParam, aDes ) ); + if ( err ) + { + PanicClient( ECRPanicBadDescriptor, aMessage ); + } + } + +// ----------------------------------------------------------------------------- +// CCREngine::PanicClient +// +// ----------------------------------------------------------------------------- +// +void CCREngine::PanicClient( TInt aPanic, const RMessage2& aMessage ) + { + // Panic the client on server side + _LIT( KRbfClientFault, "Common recording engine" ); + aMessage.Panic( KRbfClientFault, aPanic ); + } + +// End of File