dvrengine/CommonRecordingEngine/DvrRtpClipHandler/src/CRtpToFile.cpp
changeset 0 822a42b6c3f1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dvrengine/CommonRecordingEngine/DvrRtpClipHandler/src/CRtpToFile.cpp	Thu Dec 17 09:14:38 2009 +0200
@@ -0,0 +1,712 @@
+/*
+* 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:    Implementation of the Common Recording Engine RTP save format class.*
+*/
+
+
+
+
+// INCLUDE FILES
+#include "CRtpToFile.h"
+#include <ipvideo/CRtpMetaHeader.h>
+#include <e32math.h>
+#include <bsp.h>
+#include "videoserviceutilsLogger.h"
+
+// CONSTANTS
+const TUint KMaxValidDelta( 500 );     // 0.5 s
+const TUint8 KDummyFullQuality( 100 ); // 100%
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// CRtpToFile::NewL
+// Static two-phased constructor. Leaves object to cleanup stack.
+// -----------------------------------------------------------------------------
+//
+CRtpToFile* CRtpToFile::NewL(
+    MRtpFileObserver& aFileObs,
+    MRtpFileWriteObserver& aWriteObs )
+    {
+    CRtpToFile* self = new( ELeave ) CRtpToFile( aFileObs, aWriteObs );
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// CRtpToFile::CRtpToFile
+// C++ default constructor can NOT contain any code, that might leave.
+// -----------------------------------------------------------------------------
+//
+CRtpToFile::CRtpToFile(
+    MRtpFileObserver& aFileObs,
+    MRtpFileWriteObserver& aWriteObs )
+  : CRtpFileBase(),
+    iFileObs( aFileObs ),
+    iWriteObs( aWriteObs ),
+    iCurrentTime( 0 ),
+    iPreviousTime( 0 ),
+    iPreviousDelta( 0 ),
+    iReferenceTime( 0 ),
+    iRecordEndTime( 0 ),
+    iSeekArrayReference( 0 ),
+    iGroupReUse( KErrNotFound ),
+    iAction( MRtpFileWriteObserver::ESaveEnd )
+    {
+    // None
+    }
+
+// -----------------------------------------------------------------------------
+// CRtpToFile::ConstructL
+// Symbian 2nd phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::ConstructL()
+    {
+    LOG( "CRtpToFile::ConstructL()" );
+    
+    CRtpFileBase::ConstructL();
+    iCurrentPath = HBufC::NewL( 0 );
+    }
+
+// -----------------------------------------------------------------------------
+// Destructor
+//
+CRtpToFile::~CRtpToFile()
+// -----------------------------------------------------------------------------
+    {
+    LOG( "CRtpToFile::~CRtpToFile()" );
+
+    Cancel();
+    }
+
+// -----------------------------------------------------------------------------
+// CRtpToFile::InitRtpSaveL
+// Sets path of RTP file and initiates variables.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::InitRtpSaveL(
+    const MRtpFileWriteObserver::SRtpRecParams& aParams,
+    const MRtpFileWriteObserver::TRtpSaveAction& aAction )
+    {
+    LOG1( "CRtpToFile::InitRtpSaveL() in, ClipPath: %S", &aParams.iClipPath );
+    User::LeaveIfError( ( iMode != EModeNone ) * KErrInUse );
+    
+    // Mode
+    switch ( aAction )
+        {
+        case MRtpFileWriteObserver::ESaveTimeShift:
+            iMode = EModeTimeShift;
+            break;
+
+        default:
+            iMode = EModeNormal;
+            break;
+        }
+
+    // File server
+    if ( !iFs.Handle() )
+        {
+        User::LeaveIfError( iFs.Connect() );
+        }
+
+    // Create clip
+    CreateNewClipL( aParams );
+
+    // Real clip's end time
+    iPreviousTime = 0;
+    iReferenceTime = iGroupTime * KSiKilo;
+    UpdateCurrentTimeL();
+    TInt64 duration( aParams.iEndTime.Int64() - 
+                     aParams.iStartTime.Int64() ); 
+    iRecordEndTime = iCurrentTime.Int64() + duration;
+    
+    // Prepare variables
+    iSeekArrayReference = iGroupTime;
+    iStartGroupTime = iGroupTime;
+
+    LOG( "CRtpToFile::InitRtpSaveL() out" );
+    }
+
+// -----------------------------------------------------------------------------
+// CRtpToFile::ActivateGroupsReuseL
+// Starts reuse packet groups for live record when they are played.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::ActivateGroupsReuseL()
+    {
+    LOG2( "CRtpToFile::ActivateGroupsReuseL(), iMode: %d, iGroupReUse: %d",
+                                              iMode, iGroupReUse );
+    if ( iGroupReUse != KErrNotFound || iMode != EModeTimeShift )
+        {
+        User::Leave( KErrInUse );
+        }
+
+    iGroupReUse = KErrInUse;
+    }
+    
+// -----------------------------------------------------------------------------
+// CRtpToFile::SwapClipL
+// Sets new path of RTP file and initiates variables.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::SwapClipL( const MRtpFileWriteObserver::SRtpRecParams& aParams )
+    {
+    LOG1( "CRtpToFile::SwapClipL(), aClipPath: %S", &aParams.iClipPath );
+
+    User::LeaveIfError( ( iMode != EModeTimeShift ) * KErrGeneral );
+
+    // Update old clip
+    WriteSeekHeaderL();
+    iGroupReUse = KErrNotFound;
+        
+    // Open new clip
+    CreateNewClipL( aParams );
+    }
+    
+// -----------------------------------------------------------------------------
+// CRtpToFile::SaveNextRtpGroupL
+// Saves one RTP packet group to a specified file.
+// -----------------------------------------------------------------------------
+//
+TInt CRtpToFile::SaveNextGroupL(
+    TPtr8& aGroup,
+    TUint& aGroupLength,
+    const MRtpFileWriteObserver::TRtpSaveAction& aAction )
+    {
+    // Verify data and mode
+    User::LeaveIfError( iMode );
+    
+    // Group
+    iDataPtr.Set( aGroup );
+    
+    // Set group variables
+    AddGroupL();
+    GroupTimeL( aGroupLength );
+    AddGroupHeaderL(); 
+
+    // Write to file
+    iAction = aAction;
+    iFile.Write( iThisGroup, iDataPtr, iGroupTotalLen, iStatus );
+    SetActive();
+
+    LOG2( "CRtpToFile::SaveNextGroupL(), iThisGroup: %d, iGroupTime: %u",
+                                         iThisGroup, iGroupTime );
+#ifdef CR_ALL_LOGS
+    LogVariables( _L( "SaveNextGroupL()" ) );
+#endif // CR_ALL_LOGS
+    
+    return iThisGroup;
+    }
+
+// -----------------------------------------------------------------------------
+// CRtpToFile::UpdatePreviousTimeL
+// Updates previous time after pause.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::UpdatePreviousTimeL()
+    {
+    UpdateCurrentTimeL();
+    iPreviousTime = iCurrentTime.Int64();
+    }
+    
+// -----------------------------------------------------------------------------
+// CRtpToFile::StopRtpSave
+// Stops file saving and finalizes header.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::StopRtpSave( const TInt aError )
+    {
+    Cancel();
+    const TRtpFileMode mode( iMode ); 
+    
+    // If active
+    if ( mode != EModeNone )
+        {
+#ifdef CR_ALL_LOGS
+        LogVariables( _L( "StopRtpSave()" ) );
+#endif // CR_ALL_LOGS
+        iMode = EModeNone;
+
+        // Update clip headers
+        if ( mode != EModeTimeShift )
+            {
+            TRAP_IGNORE( WriteFinalMetaHeaderL( aError ) );
+            }
+        else
+            {
+            iLastSeekAddr = KMaxTInt;
+            TRAP_IGNORE( WriteSeekHeaderL() );
+            }
+
+        // Close file
+        iFile.Flush();
+        iFile.Close();
+
+        if ( aError == KErrNoMemory && !iGroupsTotalCount )
+            {
+            // Failed due to insufficient disk space, and couldn't save any
+            // packets to clip. Happens when recording is started with disk 
+            // space already below threshold, and failed to free any space.
+            // Delete the clip completely, otherwise we are just consuming 
+            // space below threshold(s).
+            LOG( "CRtpToFile::StopRtpSave(), deleting file without packets !" );
+            iFs.Delete( *iCurrentPath );
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CRtpToFile::GetClipPath
+// Getter for full path of currently recorded clip.
+// -----------------------------------------------------------------------------
+//
+HBufC* CRtpToFile::ClipPath()
+    {
+    return iCurrentPath;
+    }
+
+// -----------------------------------------------------------------------------
+// CRtpFromFile::GetCurrentLength
+// Gets the current length of the clip during recording.
+// -----------------------------------------------------------------------------
+//
+TUint CRtpToFile::GetCurrentLength()
+    {
+    return iGroupTime;
+    }
+    
+// -----------------------------------------------------------------------------
+// CRtpFromFile::UpdateRecordEndTime
+// Uppdates the current recording end time.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::UpdateRecordEndTime( const TTime& aEndTime )
+    {
+    if ( aEndTime > iCurrentTime )
+        {
+        iRecordEndTime = aEndTime.Int64();
+        }
+    }
+    
+// -----------------------------------------------------------------------------
+// CRtpToFile::UpdatePlayAttL
+// Updates clip's playback count and spot attributes after watching.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::UpdatePlayAttL( const TInt aNewSpot )
+    {
+    CRtpMetaHeader* metaheader = CRtpMetaHeader::NewLC(
+                                 iFile, CRtpMetaHeader::EMetaUpdate );
+    CRtpMetaHeader::SAttributes att;
+    metaheader->ReadAttributesL( att );
+    
+    // Step playback counter by one
+    att.iPlayCount++;
+    // Update playback spot
+    att.iPlaySpot = ( aNewSpot > 0 && aNewSpot < iLastSeekAddr )? aNewSpot:
+                                                                  KErrNone;
+    metaheader->WriteAttributesL( att );
+    CleanupStack::PopAndDestroy( metaheader );
+    LOG2( "CRtpToFile::UpdatePlayAttL(), New playback count: %d, spot: %d", 
+                                         att.iPlayCount, att.iPlaySpot );
+    }
+    
+// -----------------------------------------------------------------------------
+// CRtpToFile::RunL
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::RunL()
+    {
+    User::LeaveIfError( iStatus.Int() );
+    User::LeaveIfError( iFile.Flush() );
+    
+    // Start packets re-use?
+    if ( iGroupReUse == KErrInUse )
+        {
+        const TInt point( iFileObs.CurrentFileReadPoint( 0 ) );
+        if ( point > ( iSeekHeaderPoint + KSeekHeaderBytes ) )
+            {
+            iGroupReUse = KErrNone;
+            }
+        }
+
+    // Stop recording if time shift too close to live
+    if ( iGroupReUse > KErrNone && 
+         iFileObs.CurrentFileReadPoint( 1 ) < KErrNone )
+        {
+        iAction = MRtpFileWriteObserver::ESaveEnd;
+        LOG( "CRtpToFile::RunL(), Time shift play too close to record !" );
+        }
+    
+    // Stop recording if end time reached
+    if ( iCurrentTime.Int64() > iRecordEndTime )
+        {
+        iAction = MRtpFileWriteObserver::ESaveEnd;
+        LOG( "CRtpToFile::RunL(), Record end time reached !" );
+        }
+    
+    iFileObs.RtpGroupSaved( iAction );
+    }
+    
+// -----------------------------------------------------------------------------
+// CRtpToFile::RunError
+// -----------------------------------------------------------------------------
+//
+TInt CRtpToFile::RunError( TInt aError )
+    {
+    LOG1( "CRtpToFile::RunError(), RunL Leaved: %d", aError );
+
+    if ( &iWriteObs )
+        {
+        iWriteObs.WriteStatus( aError );
+        }
+    
+    StopRtpSave( aError );
+    return KErrNone;
+    }
+
+// -----------------------------------------------------------------------------
+// CRtpToFile::DoCancel
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::DoCancel()
+    {
+    LOG( "CRtpToFile::DoCancel()" );
+    }
+
+// -----------------------------------------------------------------------------
+// CRtpToFile::CreateNewClipL
+// Opens new clip and creates initial headers.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::CreateNewClipL(
+    const MRtpFileWriteObserver::SRtpRecParams& aParams )
+    {
+    // Open file
+    iFile.Close();
+    User::LeaveIfError( iFile.Replace( iFs, aParams.iClipPath,
+                        EFileShareAny | EFileStream | EFileWrite ) );
+    // Headers
+    WriteInitialMetaHeaderL( aParams );
+    WriteSeekHeaderL();
+    const TInt firstGroup( iSeekHeaderPoint + KSeekHeaderBytes );
+    
+    // Variables
+    iGroupTime = 0;
+    iGroupsTotalCount = 0;
+    iFirstSeekAddr = firstGroup;
+    iLastSeekAddr = firstGroup;
+    iNextGroupPoint = firstGroup;
+    delete iCurrentPath; iCurrentPath = NULL;
+    iCurrentPath = aParams.iClipPath.AllocL();
+    }
+    
+// -----------------------------------------------------------------------------
+// CRtpToFile::AddGroupL
+// Updates file and packet group header variables for a new group.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::AddGroupL()
+    {
+    // New group
+    iThisGroup = iNextGroupPoint;
+    
+    // Group header
+    // Note ! KGroupHeaderBytes size is allocated to incoming group in 
+    //        CCRRtpRecordSink::ResetGroupVariables(), but data does not exits
+    //        before CRtpToFile::AddGroupHeaderL() method is called.
+    iGroupTotalLen = KGroupHeaderBytes + iDataPtr.Length();
+    iNextGroupPoint = iThisGroup + iGroupTotalLen;
+    const TInt prevGroup( iLastSeekAddr );
+
+    // Time shift handling
+    if ( iGroupReUse > KErrNone )
+        {
+        iGroupReUse--;
+        }
+    else
+        {
+        iGroupsTotalCount++;
+        iLastSeekAddr = ( iMode != EModeTimeShift )? iThisGroup: 0;
+        }
+    
+    // Start write to the beginning of the clip?
+    if ( iGroupReUse == KErrNone )
+        {
+        iGroupReUse = iGroupsTotalCount;
+        iNextGroupPoint = iSeekHeaderPoint + KSeekHeaderBytes;
+        LOG2( "CRtpToFile::AddGroupL(), iGroupReUse: %d, iNextGroupPoint: %d",
+                                        iGroupReUse, iNextGroupPoint );
+        }
+
+    // First group in clip?
+    if ( iGroupsTotalCount == 1 )
+        {
+        iPrevGroupPoint = 0;
+        WriteSeekHeaderL();
+        iSeekArrayReference = iGroupTime;
+        }
+    else
+        {
+        iPrevGroupPoint = prevGroup;
+        }
+    }
+    
+// -----------------------------------------------------------------------------
+// CRtpToFile::GroupTimeL
+// Generates group time from group length reported by ring buffer and actual
+// network time difference to previous group. Reference time is used to avoid
+// running time error caused by network burst.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::GroupTimeL( TUint& aGroupLength )
+    {
+    UpdateCurrentTimeL();
+    TUint syncLength( 0 );
+    
+    // previous time initiated?
+    if ( iPreviousTime > 0 )
+        {
+        const TInt64 delta( iCurrentTime.Int64() - iPreviousTime );
+        iReferenceTime+= delta;
+        const TInt timeDelta( delta / KSiKilo );
+        const TInt burstDelta( Abs( timeDelta - iPreviousDelta ) );
+#ifdef CR_ALL_LOGS    
+        LOG3( "CRtpToFile::GroupTimeL(), aGroupLength: %u, burstDelta: %d, timeDelta: %d",
+                                         aGroupLength, burstDelta, timeDelta );
+#endif // CR_ALL_LOGS    
+        
+        // Use reference time?
+        if ( timeDelta > KNormalRecGroupLength && 
+             Abs( burstDelta - aGroupLength ) < KMaxValidDelta )
+            {
+            iPreviousDelta = 0;
+            syncLength = iReferenceTime / KSiKilo;
+            }
+        else
+            {
+            iPreviousDelta = timeDelta;
+            syncLength = aGroupLength;
+            }
+        }
+    else
+        {
+        // In record start and after pause uses only the reported group length
+        iPreviousDelta = 0;
+        syncLength = aGroupLength;
+        iReferenceTime+= aGroupLength * KSiKilo;
+        }
+
+    // Update group time
+    iGroupTime += syncLength;
+    iPreviousTime = iCurrentTime.Int64();
+    
+    // Time shift ongoing?
+    if ( iMode == EModeTimeShift )
+        {
+        aGroupLength = syncLength;
+        }
+    else
+        {
+        // Update seek array
+        aGroupLength = 0;
+        if ( ( iGroupTime - iSeekArrayReference ) >= KSeekArrayInterval )
+            {
+            AppendSeekArrayL( iGroupTime, iThisGroup );
+            iSeekArrayReference = iGroupTime;
+            }
+        }
+    }
+    
+// -----------------------------------------------------------------------------
+// CRtpToFile::WriteInitialMetaHeaderL
+// Writes initial meta data header of clip.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::WriteInitialMetaHeaderL(
+    const MRtpFileWriteObserver::SRtpRecParams& aParams )
+    {
+    LOG( "CRtpToFile::WriteInitialMetaHeaderL() in" );
+
+    CRtpMetaHeader* metaheader = CRtpMetaHeader::NewLC(
+                                 iFile, CRtpMetaHeader::EMetaWrite );
+    // Attributes
+    CRtpMetaHeader::SAttributes att;
+    att.iOngoing = ETrue;
+    att.iCompleted = EFalse;
+    att.iProtected = EFalse;
+    att.iFailed = EFalse;
+    att.iVersion = KCurrentClipVersion;
+    att.iQuality = KDummyFullQuality;
+    att.iPostRule = aParams.iPostRule;
+    att.iParental = aParams.iParental;
+    att.iPlayCount = 0;
+    att.iPlaySpot = KErrNone;
+    metaheader->WriteAttributesL( att );
+    LOG1( "CRtpToFile::WriteInitialMetaHeaderL(), iPostRule: %d", att.iPostRule );
+    LOG1( "CRtpToFile::WriteInitialMetaHeaderL(), iParental: %d", att.iParental );
+    
+    // Start date/time
+    metaheader->WriteStartTimeL( aParams.iStartTime );
+    TName buf( KNullDesC );
+#if defined( LIVE_TV_RDEBUG_TRACE ) || defined( LIVE_TV_FILE_TRACE )
+    aParams.iStartTime.FormatL( buf, KTimeDateFormat );
+    LOG1( "CRtpToFile::WriteInitialMetaHeaderL(), iStartTime: %S", &buf );
+#endif // LIVE_TV_RDEBUG_TRACE || LIVE_TV_FILE_TRACE
+
+    // End time
+    metaheader->WriteEndTimeL( aParams.iEndTime );
+
+    // Duration
+    metaheader->WriteDurationL( 0 );
+    
+    // Seek array point
+    metaheader->WriteSeekArrayPointL( 0 );
+    
+    // Mime info
+    CRtpUtil::GetMimeInfo( buf );
+    metaheader->WriteUserIdL( buf );
+    LOG1( "CRtpToFile::WriteInitialMetaHeaderL(), Mime: %S", &buf );
+
+    // Device info
+    CRtpUtil::GetImeiL( buf );
+    metaheader->WriteDeviceInfoL( buf );
+    LOG1( "CRtpToFile::WriteInitialMetaHeaderL(), IMEI: %S", &buf );
+
+    // ESG info
+    metaheader->WriteEsgDataL( aParams.iService, aParams.iProgram );
+    LOG1( "CRtpToFile::WriteInitialMetaHeaderL(), Service: %S", 
+                                                 &aParams.iService );
+    LOG1( "CRtpToFile::WriteInitialMetaHeaderL(), Program: %S", 
+                                                 &aParams.iProgram );
+    // SRTP data ( Reserved for future use )
+    TBuf8<3> srtp;
+    srtp.Num( KErrNotFound ); 
+    metaheader->WriteSrtpDataL( srtp );
+    
+    // SDP file
+    metaheader->WriteSdpDataL( aParams.iSdpData );
+    LOG1( "CRtpToFile::WriteInitialMetaHeaderL(), SDP length: %d", 
+                                                  aParams.iSdpData.Length() );
+    metaheader->CommitL();
+    iSeekHeaderPoint = metaheader->SeekHeaderPoint();
+    CleanupStack::PopAndDestroy( metaheader );
+
+    LOG( "CRtpToFile::WriteInitialMetaHeaderL() out" );
+    }
+    
+// -----------------------------------------------------------------------------
+// CRtpToFile::WriteFinalMetaHeaderL
+// Writes final meta data header of clip.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::WriteFinalMetaHeaderL( const TInt aStatus )
+    {
+    LOG( "CRtpToFile::WriteFinalMetaHeaderL() in" );
+    CRtpMetaHeader* metaheader = CRtpMetaHeader::NewLC(
+                                 iFile, CRtpMetaHeader::EMetaUpdate );
+    // Update duration
+    UpdateDurationL( metaheader );
+
+    // Attributes
+    CRtpMetaHeader::SAttributes att;
+    metaheader->ReadAttributesL( att );
+    att.iOngoing = EFalse;
+    att.iCompleted = !aStatus;
+    att.iFailed = !iGroupsTotalCount;
+    metaheader->WriteAttributesL( att );
+    LOG1( "CRtpToFile::WriteFinalMetaHeaderL(), Completed: %d", att.iCompleted );
+    LOG1( "CRtpToFile::WriteFinalMetaHeaderL(), iFailed  : %d", att.iFailed );
+
+    // End date/time
+    metaheader->ReadStartTimeL( iCurrentTime );
+    iRecordEndTime = iCurrentTime.Int64() + iGroupTime;
+    metaheader->WriteEndTimeL( iRecordEndTime );
+#if defined( LIVE_TV_RDEBUG_TRACE ) || defined( LIVE_TV_FILE_TRACE )
+    TName buf( KNullDesC ); TTime( iRecordEndTime ).FormatL( buf, KTimeDateFormat );
+    LOG1( "CRtpToFile::WriteFinalMetaHeaderL(), endTime: %S", &buf );
+#endif // LIVE_TV_RDEBUG_TRACE || LIVE_TV_FILE_TRACE
+
+    // Seek array point
+    metaheader->WriteSeekArrayPointL( iNextGroupPoint );
+    LOG1( "CRtpToFile::WriteFinalMetaHeaderL(), Seek array: %d", iNextGroupPoint );
+    CleanupStack::PopAndDestroy( metaheader );
+
+    // Final seek header
+    SaveSeekArrayL();
+    WriteSeekHeaderL();
+    
+    // Set orginal start time as file date
+    iFile.SetModified( iCurrentTime );
+
+    LOG( "CRtpToFile::WriteFinalMetaHeaderL() out" );
+    }
+ 
+// -----------------------------------------------------------------------------
+// CRtpToFile::AddGroupHeaderL
+// Adds header of one RTP group.
+// Room for group header bytes and packets count comes from CCRRtpRecordSink.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::AddGroupHeaderL()
+    {
+    // Packets count (PTC) is added in CCRRtpRecordSink::SaveGroup()
+
+    // Group time
+    HBufC8* bytes = CRtpUtil::MakeBytesLC( iGroupTime );
+    iDataPtr.Insert( 0, bytes->Des() );
+    CleanupStack::PopAndDestroy( bytes );
+
+    // Previous group point
+    bytes = CRtpUtil::MakeBytesLC( iPrevGroupPoint );
+    iDataPtr.Insert( 0, bytes->Des() );
+    CleanupStack::PopAndDestroy( bytes );
+
+    // Next Group point
+    bytes = CRtpUtil::MakeBytesLC( iNextGroupPoint );
+    iDataPtr.Insert( 0, bytes->Des() );
+    CleanupStack::PopAndDestroy( bytes );
+
+    // Group total size
+    bytes = CRtpUtil::MakeBytesLC( iGroupTotalLen );
+    iDataPtr.Insert( 0, bytes->Des() );
+    CleanupStack::PopAndDestroy( bytes );
+    }
+ 
+// -----------------------------------------------------------------------------
+// CRtpToFile::UpdateDurationL
+// Updates clip's duration.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::UpdateDurationL( CRtpMetaHeader* aMetaHeader )
+    {
+    aMetaHeader->WriteDurationL( TInt( iGroupTime ) );
+    LOG1( "CRtpToFile::UpdateDurationL(), new duration: %u", iGroupTime );
+    }
+    
+// -----------------------------------------------------------------------------
+// CRtpToFile::UpdateCurrentTimeL
+// Gets current time as network time.
+// -----------------------------------------------------------------------------
+//
+void CRtpToFile::UpdateCurrentTimeL()
+    {
+    iCurrentTime.UniversalTime();
+    }
+
+// End of File
+