diff -r 826cea16efd9 -r 13a33d82ad98 dvrengine/CommonRecordingEngine/src/CCRRtpRecordSink.cpp --- /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 +#include +#include +#include "CRtpTsConverter.h" +#include "CRtpPacket.h" +#include +#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 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