--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dvrengine/CommonRecordingEngine/src/CCRRtpRecordSink.cpp Wed Sep 01 12:20:37 2010 +0100
@@ -0,0 +1,623 @@
+/*
+* 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: Class that takes packet from buffer and does not put them*
+*/
+
+
+
+
+// INCLUDES
+#include "CCRRtpRecordSink.h"
+#include "CCRPacketBuffer.h"
+#include "CCRStreamingSession.h"
+#include "MCRConnectionObserver.h"
+#include <ipvideo/CRtpClipHandler.h>
+#include <ipvideo/CRtpClipManager.h>
+#include <ipvideo/CDvrSdpParser.h>
+#include "CRtpTsConverter.h"
+#include "CRtpPacket.h"
+#include <bsp.h>
+#include "videoserviceutilsLogger.h"
+
+// CONSTANTS
+const TInt KDefaultBitRate( 256 + 64 ); // 320 kbps
+const TInt KDefGroupSize( 70 * 1024 ); // 70k
+const TInt KMaxGroupSize( 140 * 1024 ); // 140k
+const TInt KMaxGrouplength( 3000 ); // 3 s
+const TInt KGroupHeaderSize( KGroupHeaderBytes + KPacketsCountBytes );
+const TInt KGroupLenghtAccuracy( 20 ); // 20ms
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::NewL
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//
+
+CCRRtpRecordSink* CCRRtpRecordSink::NewL(
+ const SCRRecordParams& aRecordParams,
+ CCRStreamingSession::TCRSinkId aSinkId,
+ CCRStreamingSession& aOwningSession,
+ MCRConnectionObserver* aObserver,
+ CRtpClipHandler*& aClipHandler )
+ {
+ CCRRtpRecordSink* self = new( ELeave )
+ CCRRtpRecordSink( aSinkId, aOwningSession, aObserver, aClipHandler );
+ CleanupStack::PushL( self );
+ self->ConstructL( aRecordParams );
+ CleanupStack::Pop( self );
+ return self;
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::CCRRtpRecordSink
+// C++ default constructor can NOT contain any code, that might leave.
+// -----------------------------------------------------------------------------
+//
+CCRRtpRecordSink::CCRRtpRecordSink(
+ CCRStreamingSession::TCRSinkId aSinkId,
+ CCRStreamingSession& aOwningSession,
+ MCRConnectionObserver* aObserver,
+ CRtpClipHandler*& aClipHandler )
+ : CCRPacketSinkBase( aOwningSession, aSinkId ),
+ iObserver( aObserver ),
+ iClipHandler( aClipHandler ),
+ iGroupPointer( NULL, 0 ),
+ iGroupSize( KGroupHeaderSize ),
+ iPacketsCount( 0 ),
+ iWantedGroup( KMaxTInt ),
+ iOldestTs( KMaxTUint ),
+ iLatestAudio( NULL, 0 ),
+ iSaveMode( MRtpFileWriteObserver::ESaveNormal ),
+ iGroupMode( MRtpFileWriteObserver::ESaveIdle )
+ {
+ // None
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::ConstructL
+// 2nd phase.
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::ConstructL( const SCRRecordParams& aRecordParams )
+ {
+ LOG( "CCRRtpRecordSink::ConstructL()" );
+
+ // Params
+ iRecParams.iClipPath = aRecordParams.iFileName;
+ iRecParams.iSdpData.Set( aRecordParams.iSdpData );
+ iRecParams.iService.Set( aRecordParams.iServiceName );
+ iRecParams.iProgram.Set( aRecordParams.iProgramName );
+ iRecParams.iPostRule = aRecordParams.iPostRule;
+ iRecParams.iParental = aRecordParams.iParental;
+ iRecParams.iEndTime = aRecordParams.iEndTime;
+
+ if ( aRecordParams.iFormat == ECRRecordTimeShift )
+ {
+ iRecParams.iStartTime = 0;
+ iRecParams.iEndTime = KDvrMaximumTimeShift * 1e6;
+ iSaveMode = MRtpFileWriteObserver::ESaveTimeShift;
+ }
+
+#if defined( LIVE_TV_RDEBUG_TRACE ) || defined( LIVE_TV_FILE_TRACE )
+ LOG1( "CCRRtpRecordSink::ConstructL(), iClipPath: %S", &iRecParams.iClipPath );
+ TName buf( KNullDesC ); iRecParams.iStartTime.FormatL( buf, KTimeDateFormat );
+ LOG1( "CCRRtpRecordSink::ConstructL(), iStartTime: %S", &buf );
+ iRecParams.iEndTime.FormatL( buf, KTimeDateFormat );
+ LOG1( "CCRRtpRecordSink::ConstructL(), iEndTime: %S", &buf );
+#endif // LIVE_TV_RDEBUG_TRACE || LIVE_TV_FILE_TRACE
+
+ // Clip handler and group buffer
+ User::LeaveIfNull( iClipHandler );
+ iGroupBuffer = HBufC8::NewL( 0 );
+ iGroupPointer.Set( iGroupBuffer->Des() );
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::~CCRRtpRecordSink
+// Destructor.
+// -----------------------------------------------------------------------------
+//
+CCRRtpRecordSink::~CCRRtpRecordSink()
+ {
+ LOG( "CCRRtpRecordSink::~CCRRtpRecordSink()" );
+
+ if ( iClipHandler )
+ {
+ iClipHandler->StopRecording( KErrCancel );
+ }
+
+ delete iGroupBuffer;
+ delete iAudioConv;
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::SetSdpL
+// Sets SDP, parses it and initiates XPS.
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::SetSdpL( const TDesC8& aSdp )
+ {
+ TInt initiated( iRecParams.iSdpData.Length() );
+ LOG2( "CCRRtpRecordSink::SetSdpL(), aSdp len: %d, initiated: %d",
+ aSdp.Length(), initiated );
+ if ( !initiated && iClipHandler )
+ {
+ iRecParams.iSdpData.Set( aSdp );
+ iClipHandler->RegisterWriteObserver( this );
+ iClipHandler->StartRecordingL( iRecParams, iSaveMode );
+
+ // SDP parser
+ CDvrSdpParser* sdpParser = CDvrSdpParser::NewLC();
+ sdpParser->TryParseL( aSdp );
+
+ // Bit rates
+ TUint total( sdpParser->VideoBitrate() + sdpParser->AudioBitrate() );
+ TReal angle( TReal( total ) / KDefaultBitRate );
+ iWantedGroup = TInt( angle * KDefGroupSize );
+ LOG1( "SetSdpL::SetSdpL(), iWantedGroup: %d", iWantedGroup );
+ iGroupBuffer = iGroupBuffer->ReAllocL( iWantedGroup + KGroupHeaderSize );
+ iGroupPointer.Set( iGroupBuffer->Des() );
+
+ // TS converter
+ delete iAudioConv; iAudioConv = NULL;
+ iAudioConv = CRtpTsConverter::NewL( sdpParser->AudioTimerGranularity() );
+ LOG1( "CCRRtpRecordSink::SetSdpL(), AudioTimerGranularity: %d",
+ sdpParser->AudioTimerGranularity() );
+ CleanupStack::PopAndDestroy( sdpParser );
+
+ // Recording can start
+ iGroupMode = MRtpFileWriteObserver::ESaveNormal;
+ iObserver->ConnectionStatusChange( iOwningSession.SourceChecksum(),
+ MCRConnectionObserver::ECRRecordingStarted, KErrNone );
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::NewPacketAvailable
+// From CCRPacketSinkBase. New packet(s) to a group.
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::NewPacketAvailable()
+ {
+ // Keep group buffer untouch during clip writing
+ if ( iBuffer && iClipHandler && !iClipHandler->WritingActive() )
+ {
+ if ( iGroupMode == MRtpFileWriteObserver::ESaveNormal )
+ {
+ // New packets to a group
+ AddToGroup();
+
+ // Group size big enougth to write to clip?
+ if ( iGroupSize >= iWantedGroup )
+ {
+ SaveGroup( iGroupMode );
+ }
+
+ // Keep buffer size reasonable
+ iBuffer->HandleBufferSize();
+ }
+ else
+ {
+ if ( iGroupMode != MRtpFileWriteObserver::ESaveIdle )
+ {
+ AddToGroup();
+
+ // Handle user pause
+ if ( iGroupMode == MRtpFileWriteObserver::ESavePause )
+ {
+ AddPausePacket();
+ }
+
+ SaveGroup( iGroupMode );
+ iGroupMode = MRtpFileWriteObserver::ESaveIdle;
+ }
+ }
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::BufferResetting
+// From CCRPacketSinkBase.
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::BufferResetDone()
+ {
+ AddPausePacket();
+ if ( iClipHandler && !iClipHandler->WritingActive() )
+ {
+ SaveGroup( MRtpFileWriteObserver::ESavePause );
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::Pause
+// -----------------------------------------------------------------------------
+//
+TInt CCRRtpRecordSink::Pause()
+ {
+ LOG1( "CCRRtpRecordSink::Pause(), iGroupMode: %d", iGroupMode );
+
+ TInt err( KErrCompletion );
+ if ( iClipHandler )
+ {
+ if ( iSaveMode == MRtpFileWriteObserver::ESaveNormal )
+ {
+ // Normal pause
+ err = KErrNone;
+ iGroupMode = MRtpFileWriteObserver::ESavePause;
+ }
+ else
+ {
+ // Time shift pause
+ TRAP( err, iClipHandler->TimeShiftPauseL() );
+ }
+ }
+
+ return err;
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::Restore
+// -----------------------------------------------------------------------------
+//
+TInt CCRRtpRecordSink::Restore()
+ {
+ LOG1( "CCRRtpRecordSink::Restore(), iGroupMode: %d", iGroupMode );
+
+ iGroupMode = MRtpFileWriteObserver::ESaveNormal;
+ return KErrNone;
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::Stop
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::Stop()
+ {
+ LOG1( "CCRRtpRecordSink::Stop(), iGroupMode: %d", iGroupMode );
+
+ iGroupMode = MRtpFileWriteObserver::ESaveEnd;
+ if ( iClipHandler && !iClipHandler->WritingActive() )
+ {
+ iWantedGroup = KMaxTInt;
+ SaveGroup( iGroupMode );
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::GroupSaved
+// From MRtpFileWriteObserver.
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::GroupSaved()
+ {
+ ResetGroupVariables();
+ if ( iGroupMode != MRtpFileWriteObserver::ESaveNormal )
+ {
+ SaveGroup( iGroupMode );
+ iGroupMode = MRtpFileWriteObserver::ESaveIdle;
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::WriteStatus
+// From MRtpFileWriteObserver.
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::WriteStatus( const TInt aStatus )
+ {
+ LOG1( "CCRRtpRecordSink::WriteStatus(), aStatus: %d", aStatus );
+
+ ForceStopRecording( aStatus );
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::AddToGroup
+// Initialises time stamp converter for audio stream and adds packets to a group.
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::AddToGroup()
+ {
+ const TInt packets( iBuffer->PacketsCount( iSinkId ) );
+ for ( TInt i( packets ); i > KErrNotFound; i-- )
+ {
+ // Packet
+ TPtr8 packet( NULL, 0 );
+ MCRPacketSource::TCRPacketStreamId streamId(
+ MCRPacketSource::EStreamIdCount );
+ const TInt book( iBuffer->GetStream( iSinkId, streamId ) );
+ iBuffer->GetPacket( book, packet );
+
+ // TS converter
+ if ( streamId == MCRPacketSource::EAudioControlStream &&
+ iAudioConv && !iAudioConv->Initiated() )
+ {
+ iAudioConv->Init( packet );
+ }
+
+ // Type valid
+ MRtpFileWriteObserver::TRtpType type( MRtpFileWriteObserver::ERtpNone );
+ if ( packet.Length() && StreamToType( streamId, type ) )
+ {
+ TRAPD( err, AddPacketToGroupL( packet, type ) );
+ if ( err )
+ {
+ LOG1( "CCRRtpRecordSink::AddToGroup(), AddPacketToGroupL leaved: %d", err );
+ ForceStopRecording( err );
+ }
+ }
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::AddPacketToGroupL
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::AddPacketToGroupL(
+ const TDesC8& aPacket,
+ const MRtpFileWriteObserver::TRtpType& aType )
+ {
+ const TUint total( KPacketSizeBytesLen +
+ KPacketTypeBytesLen + aPacket.Length() );
+ iGroupSize += total;
+ if ( iGroupSize > iGroupPointer.MaxLength() )
+ {
+ iGroupBuffer = iGroupBuffer->ReAllocL( iGroupSize );
+ iGroupPointer.Set( iGroupBuffer->Des() );
+ LOG1( "CCRRtpRecordSink::AddPacketToGroupL(), New iGroupSize: %d", iGroupSize );
+ }
+
+ // Packet length (PTL), type and data
+ TBuf8<KPacketSizeBytesLen + KPacketTypeBytesLen> header;
+ CRtpUtil::MakeBytesL( total, header );
+ header.Append( KCharSpace );
+ header[KPacketTypeBytePoint] = ( TUint8 )( aType );
+ iGroupPointer.Append( header );
+ iGroupPointer.Append( aPacket );
+ iPacketsCount++;
+
+#ifdef CR_ALL_LOGS
+ const TUint8* pointer( &aPacket[2] );
+ TInt seq( BigEndian::Get16( pointer ) );
+ LOG3( "CCRRtpRecordSink::AddPacketToGroupL(), type: %d, packet: %d, seq: %d",
+ aType, aPacket.Length(), seq );
+ //RFileLogger::WriteFormat( _L( "livetv" ), _L( "record.log" ), EFileLoggingModeAppend,
+ // _L( "AddPacketToGroupL(), type: %d, packet: %d, seq: %d" ), aType, aPacket.Length(), seq );
+
+#endif // CR_ALL_LOGS
+
+ // Variables for TS delta
+ if ( aType == MRtpFileWriteObserver::ERtpAudio &&
+ iAudioConv && iAudioConv->Initiated() )
+ {
+ if ( iOldestTs == KMaxTUint )
+ {
+ iOldestTs = TsFromPacketL( aPacket );
+ }
+ else
+ {
+ iLatestAudio.Set( iGroupPointer.Right( aPacket.Length() ) );
+ }
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::SaveGroup
+// Saves RTP packets group to a clip.
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::SaveGroup( MRtpFileWriteObserver::TRtpSaveAction aAction )
+ {
+ TRAPD( err, SaveGroupL( aAction ) );
+ if ( err )
+ {
+ ForceStopRecording( err );
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::SaveGroup
+// Saves RTP packets group to a clip.
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::SaveGroupL( MRtpFileWriteObserver::TRtpSaveAction aAction )
+ {
+ // TS delta
+ TBool forceSave( aAction != MRtpFileWriteObserver::ESaveNormal );
+ TInt length( TReal( iGroupSize ) / iWantedGroup * KNormalRecGroupLength );
+ if ( iOldestTs != KMaxTUint )
+ {
+ length = TsFromPacketL( iLatestAudio ) - iOldestTs;
+ }
+ if ( length >= ( KNormalRecGroupLength - KGroupLenghtAccuracy ) )
+ {
+ forceSave = ETrue;
+ if ( length <= ( KNormalRecGroupLength + KGroupLenghtAccuracy ) )
+ {
+ iWantedGroup = ( iWantedGroup + iGroupSize ) / 2;
+ }
+ else
+ {
+ TReal angle( TReal( iGroupSize ) / length );
+ TInt wanted( TReal( KNormalRecGroupLength ) * angle );
+ if ( wanted > ( KDefGroupSize / 2 ) && wanted < KMaxGroupSize )
+ {
+ iWantedGroup = ( iWantedGroup + wanted ) / 2;
+ }
+ }
+ }
+
+ // Group ok to save?
+ if ( forceSave || iGroupSize > KMaxGroupSize )
+ {
+ // Group packets count (PTC)
+ HBufC8* bytes = CRtpUtil::MakeBytesLC( iPacketsCount );
+ iGroupPointer.Insert( 0, bytes->Des() );
+ CleanupStack::PopAndDestroy( bytes );
+
+ // Make sure that nasty length not end to the clip in case TS overflow
+ length = ( length <= KMaxGrouplength )? length: KMaxGrouplength;
+
+ // Save to clip
+ TInt err( KErrNotReady );
+ if ( iClipHandler )
+ {
+ TRAP( err, iClipHandler->SaveNextGroupL( iGroupPointer,
+ length, aAction ) );
+ }
+ if ( err )
+ {
+ LOG1( "CCRRtpRecordSink::SaveGroup(), SaveNextGroupL Leaved: %d", err );
+ ForceStopRecording( err );
+ }
+
+ LOG3( "CCRRtpRecordSink::SaveGroup(), iPacketsCount: %d, length: %u, iWantedGroup: %d",
+ iPacketsCount, length, iWantedGroup );
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::StreamToType
+// -----------------------------------------------------------------------------
+//
+TBool CCRRtpRecordSink::StreamToType(
+ const MCRPacketSource::TCRPacketStreamId& aStream,
+ MRtpFileWriteObserver::TRtpType& aType )
+ {
+ switch ( aStream )
+ {
+ case MCRPacketSource::EAudioStream:
+ aType = MRtpFileWriteObserver::ERtpAudio;
+ break;
+
+ case MCRPacketSource::EAudioControlStream:
+ aType = MRtpFileWriteObserver::ERtcpAudio;
+ break;
+
+ case MCRPacketSource::EVideoStream:
+ aType = MRtpFileWriteObserver::ERtpVideo;
+ break;
+
+ case MCRPacketSource::EVideoControlStream:
+ aType = MRtpFileWriteObserver::ERtcpVideo;
+ break;
+
+ case MCRPacketSource::ESubTitleStream:
+ aType = MRtpFileWriteObserver::ERtpSubTitle;
+ break;
+
+ case MCRPacketSource::ESubTitleControlStream:
+ aType = MRtpFileWriteObserver::ERtcpSubTitle;
+ break;
+
+ case MCRPacketSource::EDisContinousStream:
+ LOG( "CCRRtpRecordSink::StreamToType(), ERtpClipPause" );
+ aType = MRtpFileWriteObserver::ERtpClipPause;
+ break;
+
+ case MCRPacketSource::EStreamEndTag:
+ LOG( "CCRRtpRecordSink::StreamToType(), ERtpClipEnd" );
+ aType = MRtpFileWriteObserver::ERtpClipEnd;
+ break;
+
+ default:
+ return EFalse;
+ }
+
+ return ETrue;
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::TsFromPacketL
+// -----------------------------------------------------------------------------
+//
+TUint CCRRtpRecordSink::TsFromPacketL( const TDesC8& aPacket )
+ {
+ CRtpPacket* rtpPacket = CRtpPacket::NewLC();
+ TUint ts( KMaxTUint );
+ if ( !rtpPacket->ParseRtp( aPacket ) )
+ {
+ ts = iAudioConv->ConvertTs( rtpPacket->iRtpRecvHeader.iTimestamp, ETrue );
+ }
+
+ CleanupStack::PopAndDestroy( rtpPacket );
+ return ts;
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::AddPausePacket
+// Wrapper for AddPausePacketL().
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::AddPausePacket()
+ {
+ LOG( "CCRRtpRecordSink::AddPausePacket()");
+
+ TRAPD( err, AddPausePacketL() );
+ if ( err )
+ {
+ ForceStopRecording( err );
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::AddPausePacketL
+// Adds pause packet to the group.
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::AddPausePacketL()
+ {
+ HBufC8* data = CRtpUtil::MakeBytesLC( KMaxTUint );
+ AddPacketToGroupL( data->Des(), MRtpFileWriteObserver::ERtpClipPause );
+ CleanupStack::PopAndDestroy( data );
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::ForceStopRecording
+// Stops recording on clip handler and destroys the sink.
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::ForceStopRecording( const TInt& aStatus )
+ {
+ LOG2( "CCRRtpRecordSink::ForceStopRecording(), iGroupMode: %d, aStatus: %d",
+ iGroupMode, aStatus );
+ iGroupMode = MRtpFileWriteObserver::ESaveIdle;
+
+ if ( iClipHandler )
+ {
+ iClipHandler->StopRecording( aStatus );
+ }
+
+ iObserver->ConnectionStatusChange( iOwningSession.SourceChecksum(),
+ MCRConnectionObserver::ECRRecordingEnded, aStatus );
+ iOwningSession.SinkStops( Id() );
+ }
+
+// -----------------------------------------------------------------------------
+// CCRRtpRecordSink::ResetGroupVariables
+//
+// -----------------------------------------------------------------------------
+//
+void CCRRtpRecordSink::ResetGroupVariables()
+ {
+ iGroupSize = KGroupHeaderSize; // Room for group header and packets count
+ iPacketsCount = 0;
+ iGroupPointer.Zero();
+ iOldestTs = KMaxTUint;
+ iLatestAudio.Set( NULL, 0 );
+ }
+
+// End of File