/*
* 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 DVB-H Recording Manager RTP read class.*
*/
// INCLUDE FILES
#include "CRtpToFile.h"
#include "CRtpFromFile.h"
#include <ipvideo/CRtpMetaHeader.h>
#include <ipvideo/CRtpClipManager.h>
#include "CRtpTimer.h"
#include <bsp.h>
#include "videoserviceutilsLogger.h"
// CONSTANTS
const TInt KReadTimerInterval( 1000 );
// ============================ MEMBER FUNCTIONS ===============================
// -----------------------------------------------------------------------------
// CRtpFromFile::NewL
// Static two-phased constructor. Leaves object to cleanup stack.
// -----------------------------------------------------------------------------
//
CRtpFromFile* CRtpFromFile::NewL(
MRtpFileReadObserver& aReadObs,
CRtpToFile* aToFile )
{
CRtpFromFile* self = new( ELeave ) CRtpFromFile( aReadObs, aToFile );
CleanupStack::PushL( self );
self->ConstructL();
CleanupStack::Pop( self );
return self;
}
// -----------------------------------------------------------------------------
// CRtpFromFile::CRtpFromFile
// C++ default constructor can NOT contain any code, that might leave.
// -----------------------------------------------------------------------------
//
CRtpFromFile::CRtpFromFile( MRtpFileReadObserver& aReadObs, CRtpToFile* aToFile )
: CRtpFileBase(),
iReadObs( aReadObs ),
iToFile( aToFile ),
iSkippedRead( EFalse ),
iDuration( 0 )
{
// None
}
// -----------------------------------------------------------------------------
// CRtpFromFile::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CRtpFromFile::ConstructL()
{
LOG( "CRtpFromFile::ConstructL()" );
CRtpFileBase::ConstructL();
}
// -----------------------------------------------------------------------------
// Destructor
//
CRtpFromFile::~CRtpFromFile()
// -----------------------------------------------------------------------------
{
LOG( "CRtpFromFile::~CRtpFromFile()" );
Cancel();
delete iTimer; iTimer = NULL;
delete iFileData; iFileData = NULL;
}
// -----------------------------------------------------------------------------
// CRtpFromFile::InitRtpReadL
// Sets path of RTP file.
// -----------------------------------------------------------------------------
//
void CRtpFromFile::InitRtpReadL(
const TDesC& aClipPath,
TInt8& aVersion,
const TBool aTimeShift )
{
LOG1( "CRtpFromFile::InitRtpReadL(), aClipPath: %S", &aClipPath );
// File server
if ( !iFs.Handle() )
{
User::LeaveIfError( iFs.Connect() );
}
// Open clip
aVersion = SwapClipL( aClipPath );
// Mode
iMode = ( aTimeShift )? EModeTimeShift: EModeNormal;
#ifdef CR_ALL_LOGS
LogVariables( _L( "InitRtpReadL()" ) );
#endif // CR_ALL_LOGS
}
// -----------------------------------------------------------------------------
// CRtpFromFile::InitRtpReadL
// Sets path of RTP file.
// -----------------------------------------------------------------------------
//
void CRtpFromFile::InitRtpReadL(
const RFile& aFileHandle,
TInt8& aVersion )
{
LOG( "CRtpFromFile::InitRtpReadL(), with handle" );
// File handle
if ( !iFs.Handle() )
{
User::LeaveIfError( iFs.Connect() );
}
// Duplicate handle
iFile.Close();
iFile.Duplicate( aFileHandle );
// File header
ReadClipHeaderL( aVersion );
delete iCurrentPath; iCurrentPath = NULL;
TFileName name( KNullDesC );
iFile.FullName( name );
iCurrentPath = name.AllocL();
// Mode
iMode = EModeNormal;
#ifdef CR_ALL_LOGS
LogVariables( _L( "InitRtpReadL()" ) );
#endif // CR_ALL_LOGS
}
// -----------------------------------------------------------------------------
// CRtpFromFile::SwapClipL
// Sets new path of RTP file.
// -----------------------------------------------------------------------------
//
TInt8 CRtpFromFile::SwapClipL( const TDesC& aClipPath )
{
LOG1( "CRtpFromFile::SwapClipL(), aClipPath: %S", &aClipPath );
iFile.Close();
if ( !iFs.Handle() )
{
User::Leave( KErrBadHandle );
}
// Delete used clip
if ( iMode == EModeTimeShift )
{
iFs.Delete( *iCurrentPath );
}
// Open new
User::LeaveIfError( iFile.Open( iFs, aClipPath,
EFileShareAny | EFileStream | EFileRead ) );
// File header
TInt8 version( 0 );
ReadClipHeaderL( version );
delete iCurrentPath; iCurrentPath = NULL;
iCurrentPath = aClipPath.AllocL();
return version;
}
// -----------------------------------------------------------------------------
// CRtpFromFile::ReadNextGroupL
// Reads next RTP packets group from a specified file.
// -----------------------------------------------------------------------------
//
TInt CRtpFromFile::ReadNextGroupL( const TInt aGroupPoint )
{
User::LeaveIfError( iMode );
TBool delayedRead( EFalse );
// Allready active??
if ( iFileData )
{
if ( iTimer || IsActive() )
{
return KErrInUse; // Read already started, indication, not error
}
else
{
// Packet read may happen during iReadObs.RtpGroupReaded() call
LOG( "CRtpFromFile::ReadNextGroupL(), Delayed read !" );
delayedRead = ETrue;
}
}
// Is watch during recording too close to live?
if ( iToFile && iNextGroupPoint >= LastSeekAddr() )
{
iSkippedRead = ETrue;
LOG( "CRtpFromFile::ReadNextGroupL(), Too close to live !" );
return KErrEof; // No read actions now, indication, not error
}
// Group
iThisGroup = ( aGroupPoint > KErrNotFound )? aGroupPoint: iNextGroupPoint;
// Ok to read more?
if ( iThisGroup > iLastSeekAddr || iGroupTime >= iDuration )
{
LOG( "CRtpFromFile::ReadNextGroupL(), All packets readed !" );
User::Leave( KErrEof );
}
// Read group
if ( delayedRead )
{
delete iTimer; iTimer = NULL;
iTimer = CRtpTimer::NewL( *this );
iTimer->After( KReadTimerInterval );
}
else
{
ReadGroupHeaderL();
ReadNextGroupFromFileL();
}
return KErrNone;
}
// -----------------------------------------------------------------------------
// CRtpFromFile::GetClipSdpL
// Reads SDP from a current clip. SDP is stored to meta header during recording.
// -----------------------------------------------------------------------------
//
HBufC8* CRtpFromFile::GetClipSdpL()
{
User::LeaveIfError( iMode );
CRtpMetaHeader* metaheader = CRtpMetaHeader::NewLC(
iFile, CRtpMetaHeader::EMetaRead );
HBufC8* sdp = metaheader->ReadSdpDataL();
CleanupStack::PopAndDestroy( metaheader );
return sdp;
}
// -----------------------------------------------------------------------------
// CRtpFromFile::ReadSkippedGroup
// Reads one RTP packet from a specified file if previous read was skipped.
// -----------------------------------------------------------------------------
//
void CRtpFromFile::ReadSkippedGroup()
{
if ( iSkippedRead && iNextGroupPoint < LastSeekAddr() &&
iMode != EModeNone && iFileData != NULL )
{
iSkippedRead = EFalse;
iThisGroup = iNextGroupPoint;
TRAP_IGNORE( ReadGroupHeaderL();
ReadNextGroupFromFileL() );
}
}
// -----------------------------------------------------------------------------
// CRtpFromFile::UpdateLastSeekAddr
// Updates final last seek addres from clip write when recording stopped.
// -----------------------------------------------------------------------------
//
void CRtpFromFile::UpdateLastSeekAddr()
{
if ( iToFile )
{
iDuration = iToFile->GetCurrentLength();
iLastSeekAddr = iToFile->LastSeekAddr();
LOG2( "CRtpFromFile::UpdateLastSeekAddr(), iLastSeekAddr: %d, iDuration: %d",
iLastSeekAddr, iDuration );
// Recording is stopped
iToFile = NULL;
}
}
// -----------------------------------------------------------------------------
// CRtpFromFile::SetSeekPointL
// Sets the seek point of the clip.
// -----------------------------------------------------------------------------
//
void CRtpFromFile::SetSeekPointL( const TUint aTime )
{
Cancel();
delete iTimer; iTimer = NULL;
delete iFileData; iFileData = NULL;
User::LeaveIfError( iMode );
// Group from the seek array, accuracy 30s
iThisGroup = FindSeekGroup( aTime, ( iToFile )? iToFile->SeekArray(): iSeekArray );
LOG2( "CRtpFromFile::SetSeekPointL(), aTime: %d, group from seek array: %d",
aTime, iThisGroup );
if ( iThisGroup == KErrNotFound )
{
iThisGroup = iFirstSeekAddr;
}
ReadGroupHeaderL();
// Find group basing on the seek time, accuracy 0 - 3 s
if ( aTime > 0 )
{
while ( aTime > iGroupTime && iNextGroupPoint < iLastSeekAddr )
{
// Next group
iThisGroup = iNextGroupPoint;
ReadGroupHeaderL();
#ifdef CR_ALL_LOGS
LOG2( "CRtpFromFile::SetSeekPointL(), iThisGroup: %u, iGroupTime: %u",
iThisGroup, iGroupTime );
#endif // CR_ALL_LOGS
}
}
// Prepare for next read, one extra group back looks better
iNextGroupPoint = ( iPrevGroupPoint > iFirstSeekAddr ) ?
iPrevGroupPoint : iThisGroup;
delete iFileData; iFileData = NULL;
LOG1( "CRtpFromFile::SetSeekPointL(), iNextGroupPoint: %d",
iNextGroupPoint );
}
// -----------------------------------------------------------------------------
// CRtpFromFile::StopRtpRead
// Stops file reading.
// -----------------------------------------------------------------------------
//
void CRtpFromFile::StopRtpRead( const TInt aStatus, const TUint aPlayerBuf )
{
LOG2( "CRtpFromFile::StopRtpRead(), aStatus: %d, aPlayerBuf: %u",
aStatus, aPlayerBuf );
LOG2( "CRtpFromFile::StopRtpRead(), iMode: %d, iGroupTime: %d",
iMode, iGroupTime );
Cancel();
if ( iMode != EModeNone )
{
iFile.ReadCancel();
#ifdef CR_ALL_LOGS
LogVariables( _L( "StopRtpRead()" ) );
#endif // CR_ALL_LOGS
}
delete iTimer; iTimer = NULL;
delete iFileData; iFileData = NULL;
if ( iMode == EModeNormal || iMode == EModeHandle )
{
// Try to seek back to what user sees for continue play spot
if ( !aStatus & iThisGroup > 0 && iThisGroup < iLastSeekAddr )
{
const TInt thisGroup( iThisGroup );
TRAPD( err, SetSeekPointL( iGroupTime - aPlayerBuf ) );
if ( err )
{
LOG1( "CRtpFromFile::StopRtpRead(), SetSeekPointL Leaved: %d", err );
iThisGroup = thisGroup;
}
}
// Update meta header if no error
if ( !aStatus )
{
TInt err ( KErrNone );
if ( iToFile )
{
TRAP( err, iToFile->UpdatePlayAttL( iThisGroup ) );
}
else
{
TRAP( err, UpdatePlayAttL() );
}
// Possible error ignored
if ( err )
{
LOG1( "CRtpFromFile::StopRtpRead(), UpdatePlayAttL Leaved: %d", err );
}
}
}
iMode = EModeNone;
iFile.Close();
}
// -----------------------------------------------------------------------------
// CRtpFromFile::RunL
// -----------------------------------------------------------------------------
//
void CRtpFromFile::RunL()
{
User::LeaveIfError( iStatus.Int() );
// All groups readed?
if ( iThisGroup >= iLastSeekAddr ||
( iNextGroupPoint >= iLastSeekAddr &&
iToFile && iToFile->Action() == MRtpFileWriteObserver::ESavePause ) )
{
LOG2( "CRtpFromFile::RunL(), All groups readed ! total: %d, iDuration: %d",
iThisGroup, iDuration );
iGroupTime = iDuration;
}
iReadObs.GroupReadedL( iDataPtr, iGroupTime, ( iGroupTime >= iDuration ) );
delete iFileData; iFileData = NULL;
}
// -----------------------------------------------------------------------------
// CRtpFromFile::RunError
// -----------------------------------------------------------------------------
//
TInt CRtpFromFile::RunError( TInt aError )
{
LOG1( "CRtpFromFile::RunError(), RunL Leaved: %d", aError );
iReadObs.ReadStatus( aError );
StopRtpRead( aError, 0 );
return KErrNone;
}
// -----------------------------------------------------------------------------
// CRtpFromFile::DoCancel
// -----------------------------------------------------------------------------
//
void CRtpFromFile::DoCancel()
{
LOG( "CRtpFromFile::DoCancel()" );
}
// -----------------------------------------------------------------------------
// CRtpFromFile::TimerEventL
// Internal timer call this when triggered.
// -----------------------------------------------------------------------------
//
void CRtpFromFile::TimerEventL()
{
LOG( "CRtpFromFile::TimerEventL() in" );
ReadGroupHeaderL();
ReadNextGroupFromFileL();
delete iTimer; iTimer = NULL;
LOG( "CRtpFromFile::TimerEventL() out" );
}
// -----------------------------------------------------------------------------
// CRtpFromFile::TimerError
// Internal timer call this when TimerEventL() leaves.
// -----------------------------------------------------------------------------
//
void CRtpFromFile::TimerError( const TInt aError )
{
LOG1( "CRtpFromFile::TimerError(), TimerEventL Leaved: %d", aError );
StopRtpRead( aError, 0 );
delete iTimer; iTimer = NULL;
}
// -----------------------------------------------------------------------------
// CRtpFromFile::ReadClipHeaderL
// Reads meta data and seek header from the beginning of the file.
// -----------------------------------------------------------------------------
//
void CRtpFromFile::ReadClipHeaderL( TInt8& aVersion )
{
LOG1( "CRtpFromFile::ReadClipHeaderL(), iToFile: %d", iToFile );
if ( !iToFile )
{
TInt seekArrayPoint( KErrNotFound );
aVersion = ReadMetaHeaderL( iSeekHeaderPoint, seekArrayPoint );
ReadSeekHeaderL();
// Read seek array if exist
if ( seekArrayPoint > iLastSeekAddr )
{
ReadSeekArrayL( seekArrayPoint );
}
}
else // Recording ongoing with the same clip
{
aVersion = KCurrentClipVersion;
iSeekHeaderPoint = iToFile->SeekHeaderPoint();
iGroupsTotalCount = iToFile->GroupsTotalCount();
iFirstSeekAddr = iToFile->FirstSeekAddr();
iLastSeekAddr = iToFile->LastSeekAddr();
}
iNextGroupPoint = iFirstSeekAddr;
}
// -----------------------------------------------------------------------------
// CRtpFromFile::ReadMetaHeaderL
// Reads meta data header from the beginning of the file.
// -----------------------------------------------------------------------------
//
TInt8 CRtpFromFile::ReadMetaHeaderL(
TInt& aSeekHeaderPoint,
TInt& aSeekArrayPoint )
{
CRtpMetaHeader* metaheader = CRtpMetaHeader::NewLC(
iFile, CRtpMetaHeader::EMetaRead );
aSeekHeaderPoint = metaheader->SeekHeaderPoint();
metaheader->ReadSeekArrayPointL( aSeekArrayPoint );
LOG2( "CRtpFromFile::ReadMetaHeaderL(), aSeekHeaderPoint: %d, aSeekArrayPoint: %d",
aSeekHeaderPoint, aSeekArrayPoint );
// Clip version
CRtpMetaHeader::SAttributes att;
metaheader->ReadAttributesL( att );
metaheader->ReadDurationL( iDuration );
// Verify post rule
CRtpClipManager* clipManager = CRtpClipManager::NewLC();
clipManager->VerifyPostRuleL( att.iPostRule, metaheader );
CleanupStack::PopAndDestroy( clipManager );
CleanupStack::PopAndDestroy( metaheader );
LOG2( "CRtpFromFile::ReadMetaHeaderL(), Version: %d, Duration: %d",
att.iVersion, iDuration );
return att.iVersion;
}
// -----------------------------------------------------------------------------
// CRtpFromFile::ReadNextGroupFromFileL
// Reads RTP payload from a file.
// Payload is allways after data header, so read position set is not needed.
// -----------------------------------------------------------------------------
//
void CRtpFromFile::ReadNextGroupFromFileL()
{
LOG2( "CRtpFromFile::ReadNextGroupFromFileL(), iThisGroup: %d, iGroupTime: %u",
iThisGroup, iGroupTime );
#ifdef CR_ALL_LOGS
LogVariables( _L( "ReadNextGroupFromFileL()" ) );
#endif // CR_ALL_LOGS
const TInt len( iGroupTotalLen - KGroupHeaderBytes );
if ( len <= 0 || iThisGroup < iFirstSeekAddr || iThisGroup > iLastSeekAddr )
{
#ifdef CR_ALL_LOGS
LogVariables( _L( "ReadNextGroupFromFileL()" ) );
#endif // CR_ALL_LOGS
LOG( "CRtpFromFile::ReadNextGroupFromFileL(), No More Groups" );
User::Leave( KErrEof );
}
// Reading should never be active at this point
if ( iFileData != NULL )
{
LOG( "CRtpFromFile::ReadNextGroupFromFileL(), Invalid usage of class !" );
User::Leave( KErrGeneral );
}
// Start reading group
iFileData = HBufC8::NewL( len );
iDataPtr.Set( iFileData->Des() );
iFile.Read( iThisGroup + KGroupHeaderBytes, iDataPtr, len, iStatus );
SetActive();
}
// -----------------------------------------------------------------------------
// CRtpFromFile::FindSeekGroup
// Finds closes point with seek array, accuracy about 0 - 30 s.
// -----------------------------------------------------------------------------
//
TInt CRtpFromFile::FindSeekGroup( const TUint aTime, CArrayFix<SSeek>* aArray )
{
if ( aArray->Count() && aTime >= aArray->At( 0 ).iTime )
{
for ( TInt i( aArray->Count() - 1 ); i > 0 ; i-- )
{
#ifdef CR_ALL_LOGS
LOG3( "CRtpFromFile::FindSeekGroup(), ind: %d, aTime: %u, array time: %u",
i, aTime, aArray->At( i ).iTime );
#endif //CR_ALL_LOGS
if ( aTime > aArray->At( i ).iTime )
{
return aArray->At( i ).iPoint;
}
}
return aArray->At( 0 ).iPoint;
}
return KErrNotFound;
}
// -----------------------------------------------------------------------------
// CRtpFromFile::UpdatePlayAttL
// Updates clip's playback count and spot attributes after watching.
// -----------------------------------------------------------------------------
//
void CRtpFromFile::UpdatePlayAttL()
{
// Update attributes
if ( iMode == EModeNormal )
{
iFile.Close();
User::LeaveIfError( iFile.Open( iFs, *iCurrentPath,
EFileShareAny | EFileStream | EFileWrite ) );
}
CRtpMetaHeader* metaheader = CRtpMetaHeader::NewLC(
iFile, CRtpMetaHeader::EMetaUpdate );
TTime startTime( 0 );
metaheader->ReadStartTimeL( startTime );
CRtpMetaHeader::SAttributes att;
metaheader->ReadAttributesL( att );
// Step playback counter by one
att.iPlayCount++;
// Update play spot
att.iPlaySpot = ( iThisGroup > 0 && iThisGroup < iLastSeekAddr )? iThisGroup:
KErrNone;
metaheader->WriteAttributesL( att );
CleanupStack::PopAndDestroy( metaheader );
LOG2( "CRtpFromFile::UpdatePlayAttL(), New play count: %d, spot: %d",
att.iPlayCount, att.iPlaySpot );
// Set start time to file date
iFile.SetModified( startTime );
}
// -----------------------------------------------------------------------------
// CRtpFromFile::LastSeekAddr
// Gets last seek addres.
// It is either from opened clip or from file writer when recording is ongoing.
// -----------------------------------------------------------------------------
//
TInt CRtpFromFile::LastSeekAddr()
{
if ( iToFile )
{
iLastSeekAddr = iToFile->LastSeekAddr();
}
return iLastSeekAddr;
}
// End of File