dvrengine/CommonRecordingEngine/src/CCRRtpRecordSink.cpp
branchRCL_3
changeset 48 13a33d82ad98
parent 0 822a42b6c3f1
--- /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