/*
* 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: RTSP Client impl.*
*/
// INCLUDE FILES
#include "CCRRtspPacketSource.h"
#include "CCRPunchPacketSender.h"
#include "CCRRtpTcpStreamer.h"
#include "CCRRtspCommand.h"
#include "CCRPacketBuffer.h"
#include <ipvideo/CDvrSdpParser.h>
#include "CCRTimer.h"
#include <Uri16.h>
#include <e32msgqueue.h>
#include <centralrepository.h>
#include <WebUtilsInternalCRKeys.h>
#include <mmf/common/mmferrors.h> // ROP error codes
// DATA TYPES
// ######################################################
// WARNING: JUMBOJET-SIZED KLUDGE AHEAD:
// ######################################################
#define private public
// Explanation: timestamp getter in rtcp sender report
// class is broken beyond repair. It may be fixed but the
// broken version is already shipped to millions of phones
// around the world. The broken getter method can't
// be overridden as it requires access to private part
// of sender reports instance variables. The item we
// need (ntp timestamp) is there intact in private instance
// variables but there is useless getter for that.
#include <rtcp.h>
/* sender report (SR) */
class TRtcpSRPart
{
public:
TUint32 ssrc; /**< sender generating this report */
TUint32 ntp_sec; /**< NTP timestamp */
TUint32 ntp_frac; /**< Fractal seconds */
TUint32 rtp_ts; /**< RTP timestamp */
TUint32 psent; /**< packets sent */
TUint32 osent; /**< octets sent */
};
#undef private
// ######################################################
// Major kludge ends here.
// ######################################################
// CONSTANTS
const TInt KCRPortNumberBase( 16670 );
const TInt KCSeqForRtspNegoation( 42 );
const TInt KRtspPortNumber( 554 );
const TInt KRtpPacketVersion( 2 );
const TUint KSenderReportPacketType( 0xC8 ); // 200 decimal
const TInt KDVR10Seconds( 10000000 );
// The number of sequential packets that must be received
// before a stream is considered good. 1 means no delay, start
// from very first packet
const TInt KDVRMinSequential( 1 );
// The maximum number of dropped packets to be considered a
// dropout, as opposed to an ended and restarted stream.
const TInt KDVRMaxMisorder( 50 );
// The maximum number of packets by which a packet can be delayed
// before it is considered dropped.
const TInt KDVRMaxDropOut( 3000 );
_LIT( KRtspPortString, "554" );
_LIT8( KCRCName, "N++ " );
// Timeout for RTP/UDP reception before switching to TCP mode
const TTimeIntervalMicroSeconds32 KCRRtspRtpUdpTimeout( 10 * 1e6 );
// Timeout for waiting for server response to any RTSP command
const TTimeIntervalMicroSeconds32 KCRRtspResponseTimeout( 15 * 1e6 );
// Timeout for waiting for server response to TIERDOWN command
const TTimeIntervalMicroSeconds32 KCRRtspTierdownTimeout( 3 * 1e6 );
// ============================ MEMBER FUNCTIONS ===============================
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CCRRtspPacketSource* CCRRtspPacketSource::NewL(
const SCRRtspParams& aParams,
CCRConnection& aConnection,
RSocketServ& aSockServer,
MCRStreamObserver& aSessionObs,
CCRStreamingSession& aOwningSession )
{
CCRRtspPacketSource* self = new( ELeave )
CCRRtspPacketSource( aConnection, aSockServer, aSessionObs, aOwningSession );
CleanupStack::PushL( self );
self->ConstructL( aParams );
CleanupStack::Pop( self );
return self;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::CCRRtspPacketSource
// C++ default constructor can NOT contain any code, that might leave.
// -----------------------------------------------------------------------------
//
CCRRtspPacketSource::CCRRtspPacketSource(
CCRConnection& aConnection,
RSocketServ& aSockServer,
MCRStreamObserver& aSessionObs,
CCRStreamingSession& aOwningSession )
: CCRPacketSourceBase( aOwningSession, CCRStreamingSession::ECRRtspSourceId ),
iSockServer( aSockServer ),
iConnection( aConnection ),
iStage( ERTSPInit ),
iCSeq( KCSeqForRtspNegoation ),
iClientPort( KCRPortNumberBase ),
iSessionId(NULL, 0 ),
iReadyToPlay(EFalse),
iSessionObs( aSessionObs ),
iStartPos( KRealZero ),
iEndPos( KRealMinusOne ),
iUdpFound( EFalse ),
iTrafficFound( EFalse )
{
// None
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::ConstructL( const SCRRtspParams& aParams )
{
LOG( "CCRRtspPacketSource::ConstructL() in" );
if ( aParams.iUrl.Length() == 0 )
{
User::Leave ( KErrArgument );
}
iSentData = HBufC8::NewL ( KCROptionsReply().Length() + KMaxInfoName );
iRtpTcpStreamer = CCRRtpTcpStreamer::NewL( *this );
iRtspUri = aParams.iUrl.AllocL();
iRtspUri8 = HBufC8::NewL( aParams.iUrl.Length());
iRtspUri8->Des().Copy( aParams.iUrl );
iUserName = aParams.iUserName.AllocL();
iPassword = aParams.iPassword.AllocL();
User::LeaveIfError( iConnection.RegisterObserver( this ) );
iUdpReceptionTimer = CCRTimer::NewL( EPriorityLow, *this );
iProxyServerAddr = aParams.iProxyServerAddr;
iProxyServerPort = aParams.iProxyServerPort;
DoConnectL(); // Makes no sense to construct without immediately connecting
LOG( "CCRRtspPacketSource::ConstructL() out" );
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::~CCRRtspPacketSource
// Destructor.
// -----------------------------------------------------------------------------
//
CCRRtspPacketSource::~CCRRtspPacketSource()
{
LOG( "CCRRtspPacketSource::~CCRRtspPacketSource() in" );
// Deletes everything related to session
CleanUp();
delete iRtspTimeout;
delete iSentData;
delete iAuthType;
delete iRtspUri8;
delete iUserName;
delete iPassword;
delete iNonce;
delete iOpaque;
delete iRealm;
delete iRtpTcpStreamer;
delete iUdpReceptionTimer;
iReceiveStreams.Reset();
iObserver = NULL;
iConnection.UnregisterObserver( this );
LOG( "CCRRtspPacketSource::~CCRRtspPacketSource() out" );
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::CleanUp
// Callback method called from cleanup-cidle that just calls the actual
// cleanup method.
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::CleanUp()
{
LOG( "CCRRtspPacketSource::CleanUp() in" );
if ( iUdpReceptionTimer )
{
iUdpReceptionTimer->Cancel();
}
delete iRtspPingTimer;
iRtspPingTimer = NULL;
iRtpRecvSrcAudio.Close();
iRtpRecvSrcVideo.Close();
iAudioSession.Close();
iVideoSession.Close();
iReadyToPlay = EFalse;
delete iSdpParser; iSdpParser = NULL;
delete iRtspUri; iRtspUri = NULL;
delete iRtspSock; iRtspSock = NULL;
for ( TInt i( 0 ); i < ERTPMaxSockets; i++ )
{
delete iRTPSockArr[i];
iRTPSockArr[i] = NULL;
}
for ( TInt i( 0 ); i < ERTSPLastStage; i++ )
{
delete iPrevCommands[i];
iPrevCommands[i] = NULL;
delete iResponses[i];
iResponses[i] = NULL;
}
iSessionId.Set( NULL, 0 );
delete iPunchPacketSenderAudio; iPunchPacketSenderAudio = NULL;
delete iPunchPacketSenderVideo; iPunchPacketSenderVideo = NULL;
delete iUserAgent; iUserAgent = NULL;
delete iWapProfile; iWapProfile = NULL;
iStartPos = KRealZero;
iEndPos = KRealMinusOne;
LOG( "CCRRtspPacketSource::CleanUp() out" );
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::DoConnectL
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::DoConnectL( void )
{
if ( !iRtspUri )
{
User::Leave( KErrNotReady );
}
if ( iRtspSock )
{
delete iRtspSock; iRtspSock = NULL;
}
iRtspSock = CCRSock::NewL( *this, ERTPControl, iConnection.Connection(),
iSockServer, ETrue, ETrue );
TUriParser uriParser;
User::LeaveIfError( uriParser.Parse( iRtspUri->Des() ) );
iRtspUriHost.Set( uriParser.Extract( EUriHost ) );
TPtrC portString( KRtspPortString );
if ( uriParser.IsPresent( EUriPort ) )
{
portString.Set( uriParser.Extract( EUriPort ) );
}
TLex portLex( portString );
TInt port( KRtspPortNumber );
if ( portLex.Val( port ) != KErrNone )
{
User::Leave( KErrMMInvalidURL );
}
if ( iProxyServerAddr.Length() && iProxyServerPort )
{
LOG2( "CCRRtspPacketSource::DoConnectL(), Proxy: %S port: %d",
&iProxyServerAddr, iProxyServerPort );
User::LeaveIfError( iRtspSock->ConnectSock( iProxyServerAddr, iProxyServerPort ) );
}
else
{
User::LeaveIfError(iRtspSock->ConnectSock( iRtspUriHost, port ) );
}
iCSeq = KCSeqForRtspNegoation;
TTime now;
now.UniversalTime();
iClientPort =
KCRPortNumberBase + ( ( now.DateTime().MicroSecond() / 1000 ) * 2 );
// Get transport method from connection heuristics
iTransport = ( iConnection.GetHeuristic(
CCRConnection::EUdpStreamingBlocked ) )? ERTPOverTCP: ERTPOverUDP;
LOG1( "CCRRtspPacketSource::DoConnectL(), RTP transport: %d (0=UDP, 1=TCP)", iTransport );
// Get user agent, bandwidth and wap profile based on connection bearer (3G or not)
TConnMonBearerType bearer = iConnection.BearerType();
TBool is3g( iConnection.IsBearerWLANor3G( bearer ) );
// Fetch wap profile from WebUtils repository
if ( !iWapProfile )
{
CRepository* repository = CRepository::NewLC( KCRUidWebUtils );
TUint32 profilekey = ( is3g )? KWebUtilsUaProf3G: KWebUtilsUaProf;
TFileName profilebuf( KNullDesC );
if ( !repository->Get( profilekey, profilebuf ) )
{
iWapProfile = HBufC8::NewL( profilebuf.Length() );
iWapProfile->Des().Copy( profilebuf );
}
CleanupStack::PopAndDestroy( repository );
LOG1( "CCRRtspPacketSource::DoConnectL(), iWapProfile: %S", &profilebuf );
}
// Fetch user agent
// Should we add version information to user agent string?
delete iUserAgent; iUserAgent = NULL;
iUserAgent = KCRRTSPDefaultUserAgent().AllocL();
// Get bandwidth from connection
iBandwidth = iConnection.MaximumBandwidth();
LOG( "CCRRtspPacketSource::DoConnectL out" );
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::URI
// -----------------------------------------------------------------------------
//
TPtr CCRRtspPacketSource::URI(void)
{
__ASSERT_DEBUG( iRtspUri != NULL , User::Panic( _L( "RTSP source" ), KErrBadHandle ) );
TPtr retval ( NULL , 0 );
if ( iRtspUri )
{
retval.Set( iRtspUri->Des() );
}
else
{
LOG( "CCRRtspPacketSource::URI iRtspUri was NULL !!!!!!!!!! " );
}
return retval;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::GetSdp
// -----------------------------------------------------------------------------
//
TInt CCRRtspPacketSource::GetSdp( TPtrC8& aSdp )
{
TInt retval( KErrNotReady );
if ( iSdpParser )
{
return iSdpParser->GetSdp( aSdp );
}
return retval;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SeqAndTS
// -----------------------------------------------------------------------------
//
TInt CCRRtspPacketSource::SeqAndTS(
TUint& aAudioSeq,
TUint& aAudioTS,
TUint& aVideoSeq,
TUint& aVideoTS )
{
TInt retval( KErrNotReady );
if ( iSeqFromRtpInfoForVideo != 0 || iSeqFromRtpInfoForAudio != 0 )
{
aAudioSeq = iSeqFromRtpInfoForAudio;
aAudioTS = iRTPTimeStampAudio;
aVideoSeq = iSeqFromRtpInfoForVideo;
aVideoTS = iRTPTimeStampVideo;
retval = KErrNone;
}
return retval;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::PostActionL
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::PostActionL()
{
LOG1( "CCRRtspPacketSource::PostActionL(), SDP will be handled, iSdpParser: %d",
iSdpParser );
User::LeaveIfNull( iSdpParser );
iSessionObs.StatusChanged( MCRPacketSource::ERtpStateSdpAvailable );
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::Play
// -----------------------------------------------------------------------------
//
TInt CCRRtspPacketSource::Play( const TReal& aStartPos, const TReal& aEndPos )
{
LOG2( "CCRRtspPacketSource::Play(), aStartPos: %f, aEndPos: %f",
aStartPos, aEndPos );
LOG2( "CCRRtspPacketSource::Play(), sent seq: %d, rec: %d",
iCSeq, iLastReceivedSeq );
iReadyToPlay = ETrue;
iStartPos = aStartPos;
iEndPos = aEndPos;
ResetStreamFlags();
// In xps case we never get startpos with this method.
// instead setposition will be called
if ( iBuffer )
{
iBuffer->ResetBuffer();
}
// If both audio and video sessions are closed, we
// need to open at least one of them:
TInt err( KErrNone );
if ( iStage == ERTSPReadyToPlay || iStage == ERTSPPauseSent )
{
if ( iStage == ERTSPReadyToPlay || iCSeq == ( iLastReceivedSeq + 1 ) )
{
TRAP( err, SendPlayCommandL() );
}
else
{
// We have a fast-fingered user in charge; play has been issued
// but the previous pause has not been completed yet: postpone this
// operation
iPostPonedPlay = ETrue;
}
}
return err;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::Pause
// -----------------------------------------------------------------------------
//
TInt CCRRtspPacketSource::Pause()
{
LOG1( "CCRRTSPPacketSource::Pause() stage %d", iStage );
TInt err( KErrNotReady );
if ( iStage == ERTSPPlaying )
{
if ( iResponses[ERTSPPlaySent]->IsLiveStream() || iSdpParser->IsLiveStream() )
{
err = KErrNotSupported;
}
else
{
TRAP( err, SendPauseCommandL() );
}
}
if ( iStage == ERTSPPauseSent )
{
err = KErrNone;
}
return err;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::Stop
// -----------------------------------------------------------------------------
//
TInt CCRRtspPacketSource::Stop()
{
LOG( "CCRRtspPacketSource::Stop()" );
iReadyToPlay = EFalse;
iPostPonedPlay = EFalse;
iStartPos = KRealZero;
TInt err( KErrDisconnected );
if ( iStage == ERTSPPlaySent || iStage == ERTSPPlaying ||
iStage == ERTSPPauseSent || iStage == ERTSPSetupAudioSent ||
iStage == ERTSPSetupVideoSent )
{
err = KErrNone;
if ( iRtspSock )
{
iRtspSock->Cancel();
}
TRAP_IGNORE( SendTearDownCommandL() ); // if this fails, we don't care
iStage = ERTSPTearDownSent;
StartRtspTimeout( KCRRtspTierdownTimeout );
}
return err;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SetPosition
// -----------------------------------------------------------------------------
TInt CCRRtspPacketSource::SetPosition( const TInt64 aPosition )
{
LOG1( "CCRRtspPacketSource::SetPosition(), iStartPos: %f", iStartPos );
if ( aPosition == -2 )
{
if ( iStage != ERTSPPlaySent && iObserver )
{
iObserver->ConnectionStatusChange(
iOwningSession.SourceChecksum(),
ECRReadyToSeek, KErrNone );
}
}
return KErrNone;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::GetRange
// -----------------------------------------------------------------------------
void CCRRtspPacketSource::GetRange( TReal& aLower, TReal& aUpper )
{
aLower = KRealZero;
aUpper = KRealMinusOne;
if ( ( iStage == ERTSPPlaySent || iStage == ERTSPPlaying ) &&
iResponses[ERTSPPlaySent] )
{
iResponses[ERTSPPlaySent]->GetRange(aLower,aUpper);
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::DataReceived
// This is called when data is received from socket.
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::DataReceived( TInt /*aSockId*/, const TDesC8& aData )
{
// Find out RTCP message or RTP packet from IP packet
iRtpTcpStreamer->DataAvailable( aData, ( iTransport == ERTPOverTCP ) );
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::RtspMsgAvailable
// This is called when data is received from socket.
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::RtspMsgAvailable( const TDesC8& aData )
{
#if defined ( LIVE_TV_FILE_TRACE ) || defined ( LIVE_TV_RDEBUG_TRACE )
if ( aData.Length() > 0 )
{
LOG1( "CCRRtspPacketSource::RtspMsgAvailable(), aData len: %d", aData.Length() );
TName d( KNullDesC );
for( TInt i( 0 ); i < aData.Length(); i++ )
{
TChar c = aData[i];
d.Append( c );
if ( ( i > 0 ) && ( i % 80 ) == 0 )
{
LOG1( ">%S<", &d );
d.Zero();
}
}
LOG1( ">%S<", &d );
}
#endif // LIVE_TV_FILE_TRACE || LIVE_TV_RDEBUG_TRACE
TRAPD( err, ProcessRtspResponseL( aData ) );
if ( err )
{
LOG1( "CCRRtspPacketSource::RtspMsgAvailable(), ProcessRtspResponseL Leaved, err: %d", err );
if ( err == KErrNotSupported )
{
// The response did not look like rtsp response at all.
// some servers decide to send rtsp commands to us so lets
// try interpreting it as a command
err = KErrNone;
TRAP( err, ProcessRTSPCommandL( aData ) )
if ( err )
{
iOwningSession.SourceStop();
}
}
else
{
iOwningSession.SourceStop();
}
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SockStatusChange
// This is called when socket status changes.
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::SockStatusChange(
TInt aSockId,
CCRSock::TCRSockStatus aStatus,
TInt aError )
{
#if defined ( LIVE_TV_FILE_TRACE ) || defined ( LIVE_TV_RDEBUG_TRACE )
if ( aStatus == CCRSock::EFailed )
{
LOG3( "CCRRtspPacketSource::SockStatusChange(), aSockId: %d, aStatus: %d, aError: %d",
aSockId, aStatus, aError );
}
#else // LIVE_TV_FILE_TRACE || LIVE_TV_RDEBUG_TRACE
( void )aSockId;
( void )aError;
#endif // LIVE_TV_FILE_TRACE || LIVE_TV_RDEBUG_TRACE
if ( aStatus == CCRSock::EFailed )
{
// Ask session to perform cleanup
iOwningSession.SourceStop();
if ( iStage == ERTSPInit && aSockId == ERTPControl && aError == KErrCouldNotConnect )
{
// map error to different error id, so we can know that showing reconnect query is pointless.
aError = KErrEof;
}
// Inform the observer that there is a problem. Exclude case where we're closing
// and the error is KErrEof
if ( ! ( iStage == ERTSPTearDownSent && aError == KErrEof ) )
{
if ( iObserver )
{
iObserver->ConnectionStatusChange(
iOwningSession.SourceChecksum(), ECRConnectionError, aError );
}
}
else
{
LOG( "CCRRtspPacketSource::SockStatusChange(), eof in closing: normal" );
}
}
else if ( aSockId == ERTPControl && aStatus == CCRSock::EIdle &&
iStage == ERTSPInit )
{
// Called once from here for lifetime of this object
TRAPD( err, SendRtspCommandL() );
if ( err )
{
LOG1( "CCRRtspPacketSource::SockStatusChange(), SendRtspCommandL Leaved: %d", err );
// Ask session to perform cleanup
iOwningSession.SourceStop();
}
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::RtpTcpPacketAvailable
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::RtpTcpPacketAvailable(
TInt aChannel,
const TDesC8& aPacket )
{
// Map embedded TCP channel to streamid:
// video: channel=(0,1) --> id=(2,3)
// audio: channel=(2,3) --> id=(0,1) when video present
// audio: channel=(0,1) --> id=(0,1) when audio only
TInt mappedChannel( ( iSdpParser->VideoControlAddr().Length() )?
( aChannel + 2 ) % 4: aChannel );
MCRPacketSource::TCRPacketStreamId streamid(
( MCRPacketSource::TCRPacketStreamId )( mappedChannel ) );
iBuffer->AddPacket( streamid, aPacket );
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::ForwardRtpTcpChunk
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::ForwardRtpTcpChunck( const TDesC8& aChunk )
{
if ( iRtspSock )
{
iRtspSock->SendData( aChunk );
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::TimerExpired
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::TimerExpired( CCRTimer* )
{
LOG( "CCRRtspPacketSource::TimerExpired: RTP/UDP timer expired, switching to RTP/TCP" );
if ( !iUdpFound )
{
// Signal heuristic for TCP streaming
LOG( "CCRRtspPacketSource::TimerExpired - Switch to TCP" );
iConnection.SetHeuristic( CCRConnection::EUdpStreamingBlocked, ETrue );
}
else
{
// We had UDP before in this session but now it is lost for some reason.
// Try UDP again.
// Flag UDP found away
iUdpFound = EFalse;
iTrafficFound = EFalse;
// Clear stream followup
iReceiveStreams.Reset();
LOG( "CCRRtspPacketSource::TimerExpired - Trying UDP again" );
}
// Notify client to close us and start a new session
if ( iObserver )
{
// Notify client
iObserver->ConnectionStatusChange(
iOwningSession.SourceChecksum(), ECRSwitchingToTcp, KErrNone );
}
else
{
// If no client observer, teardown and cleanup ourselves
iPostPonedPlay = EFalse;
TRAPD( err, SendTearDownCommandL() );
if ( err != KErrNone )
{
LOG1( "CCRRtspPacketSource::TimerExpired() Send TEARDOWN failed: %d", err );
}
CleanUp();
iSessionObs.StatusChanged( MCRPacketSource::ERtpStateClosing );
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::ProcessRTSPCommandL
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::ProcessRTSPCommandL( const TDesC8& aData )
{
LOG1( "CCRRtspPacketSource::ProcessRTSPCommandL(), iStage: %d", ( int )iStage );
CCRRtspCommand* cmd = CCRRtspCommand::NewL();
CleanupStack::PushL( cmd );
cmd->TryParseL( aData );
switch ( cmd->Command() )
{
case CCRRtspCommand::ERTSPCommandOPTIONS:
iSentData->Des().Format( KCROptionsReply, cmd->CSeq() );
iRtspSock->SendData( iSentData->Des() );
break;
default:
// Server sent us a command and it is not options.
// for sure they want us to stop ; is there
iOwningSession.SourceStop();
break;
}
CleanupStack::PopAndDestroy( cmd );
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::ProcessRtspResponseL
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::ProcessRtspResponseL( const TDesC8& aData )
{
LOG1( "CCRRtspPacketSource::ProcessRtspResponseL(), iStage: %d", iStage );
// Cancel timeout timer
if ( iRtspTimeout )
{
iRtspTimeout->Cancel();
}
// The server responded to our TEARDOWN command. No need to parse the response
// since we don't care what the server said. Ask session to clean us up.
if ( iStage == ERTSPTearDownSent )
{
iOwningSession.SourceStop();
return;
}
// First parse response
CCRRtspResponse* resp = CCRRtspResponse::NewL();
CleanupStack::PushL( resp );
resp->TryParseL( aData );
// Then find the command that this resp is associated with:
iLastReceivedSeq = resp->CSeq();
TBool commandFound( EFalse );
for ( TInt i( 0 ); i < ERTSPLastStage && !commandFound; i++ )
{
LOG2( "CCRRtspPacketSource:: prevcommand stage: %d cseq: %d",
i, ( iPrevCommands[i] )? iPrevCommands[i]->CSeq(): KErrNotFound );
if ( iPrevCommands[i] && ( iPrevCommands[i]->CSeq() == resp->CSeq() ) )
{
LOG1( "CCRRtspPacketSource::ProcessRtspResponseL(), matching command: %d", i );
LOG1( "CCRRtspPacketSource::ProcessRtspResponseL(), cseq was: %d", resp->CSeq() );
delete iResponses[i];
CleanupStack::Pop( resp );
iResponses[i] = resp;
commandFound = ETrue;
if ( i == ERTSPOptSent )
{
// Process options no further, used only for ping here
return;
}
}
}
// Delete response if sequency not match
if ( !commandFound )
{
CleanupStack::PopAndDestroy( resp );
LOG1( "CCRRtspPacketSource::ProcessRtspResponseL(), Command not found, cseq: %d", resp->CSeq() );
}
else
{
if ( iResponses[iStage]->StatusCode() == CCRRtspResponse::ERTSPRespOK || // 200
iResponses[iStage]->StatusCode() == CCRRtspResponse::ERTSPRespCreated ) // 201
{
// Extract useful information from response depending on stage:
switch ( iStage )
{
case ERTSPSetupAudioSent: // From setups take session id
case ERTSPSetupVideoSent:
if ( !iSessionId.Ptr() )
{
iResponses[iStage]->SessionId( iSessionId );
}
// Check for sdp parser and send punch packets for UDP transport
// (TCP or multicast: session setup and PLAY in SendRTSPCommand)
if ( iSdpParser && iTransport == ERTPOverUDP )
{
// If we see that we don't need to send further setups,
// do send punch packets now.
if ( ( iSdpParser->VideoControlAddr().Length() && // if we have video
iResponses[ERTSPSetupVideoSent] && // and we have video se tup
iSdpParser->AudioControlAddr().Length() && // and we have audio
iResponses[ERTSPSetupAudioSent] ) || // and we have audio set up or...
( !iSdpParser->VideoControlAddr().Length() && // if we have no video
!iResponses[ERTSPSetupVideoSent] && // and we've video not set up
iSdpParser->AudioControlAddr().Length() && // and it shows we have audio
iResponses[ERTSPSetupAudioSent] ) || // and we've audio set up or...
( iSdpParser->VideoControlAddr().Length() && // if we have video
iResponses[ERTSPSetupVideoSent] && // and we have video set up
!iSdpParser->AudioControlAddr().Length() && // and we have no audio
!iResponses[ERTSPSetupAudioSent] ) ) // and we have no audio set up
{
SendPunchPacketsL();
}
}
// Notify sink that SETUP repply received
iSessionObs.StatusChanged(
MCRPacketSource::ERtpStateSetupRepply );
break;
case ERTSPDescSent: // From desc take sdp
if ( iObserver && iResponses[iStage]->ContentLen() <= 0 )
{
// This should not happen
if ( iObserver )
{
iObserver->ConnectionStatusChange(
iOwningSession.SourceChecksum(), ECRConnectionError, KErrUnderflow );
}
iOwningSession.SourceStop();
}
else
{
delete iSdpParser; iSdpParser = NULL;
iSdpParser = CDvrSdpParser::NewL();
if ( iResponses[iStage]->ContentBase().Length() )
{
iSdpParser->TryParseL( iResponses[iStage]->Content(),
iResponses[iStage]->ContentBase() );
}
else
{
iSdpParser->TryParseL( iResponses[iStage]->Content(),
iRtspUri8->Des() );
}
// Check for multicast address in SDP
if ( iSdpParser->IsMultiCastSdp() )
{
iTransport = ERTPOverMulticast;
}
if ( iObserver && iSdpParser->IsRealMediaContent() )
{
iObserver->ConnectionStatusChange(
iOwningSession.SourceChecksum(),
ECRStreamIsRealMedia, KErrNotSupported );
iOwningSession.SourceStop();
return; // Make sure we don't continue with SETUP commands
}
else // do not send realmedia sdp to sinks
{
if ( iObserver && iSdpParser->IsLiveStream() )
{
iObserver->ConnectionStatusChange(
iOwningSession.SourceChecksum(),
ECRStreamIsLiveStream, KErrNone );
}
// then check for bandwidth requirements even before we start:
if ( iObserver )
{
// Unknown bitrate or bandwidth are returned as zero.
// Bitrates in kbit/s
TInt bitrate( iSdpParser->VideoBitrate() +
iSdpParser->AudioBitrate() );
TInt bandwidth( iConnection.MaximumBandwidth() / 1000 );
if ( bitrate && bandwidth && bandwidth < bitrate )
{
LOG2( "CCRRtspPacketSource::ProcessRtspResponseL(), bitrate:%d, bandwidth: %d -> NotEnoughBandwidth",
bitrate, bandwidth);
iObserver->ConnectionStatusChange(
iOwningSession.SourceChecksum(),
ECRNotEnoughBandwidth, KErrNone );
return; // Make sure we don't tell sinks anything about
// sdp that has too high bitrate for our network bearer
}
}
// But if we didn't have realmedia stream and the bandwidth check
// is also all right, then go on and tell the sinks ->
iSessionObs.StatusChanged(
MCRPacketSource::ERtpStateSdpAvailable );
}
}
break;
case ERTSPPlaySent:
{
CCRRtspResponse::SRTPInfoHeader rtpInfo;
iResponses[ERTSPPlaySent]->RTPInfoHeader( rtpInfo );
TPtrC8 videoAddr ( NULL, 0 );
if ( iSdpParser->VideoControlAddr().Length() )
{
videoAddr.Set ( iSdpParser->VideoControlAddr() );
}
TPtrC8 audioAddr ( NULL , 0 );
if ( iSdpParser->AudioControlAddr().Length() )
{
audioAddr.Set ( iSdpParser->AudioControlAddr() );
}
if ( iSdpParser->VideoControlAddr().Length() &&
rtpInfo.iFirstURL.Length() &&
videoAddr.Find( rtpInfo.iFirstURL ) >= 0 )
{
iRTPTimeStampVideo = rtpInfo.iFirstTS ? rtpInfo.iFirstTS : 1;
iSeqFromRtpInfoForVideo = rtpInfo.iFirstSeq;
}
if ( iSdpParser->VideoControlAddr().Length() &&
rtpInfo.iSecondURL.Length() &&
videoAddr.Find( rtpInfo.iSecondURL ) >= 0 )
{
iRTPTimeStampVideo = rtpInfo.iSecondTS ? rtpInfo.iSecondTS : 1;
iSeqFromRtpInfoForVideo = rtpInfo.iSecondSeq;
}
if ( iSdpParser->AudioControlAddr().Length() &&
rtpInfo.iFirstURL.Length() &&
audioAddr.Find( rtpInfo.iFirstURL) >= 0 )
{
iRTPTimeStampAudio = rtpInfo.iFirstTS ? rtpInfo.iFirstTS : 1;
iSeqFromRtpInfoForAudio = rtpInfo.iFirstSeq;
}
if ( iSdpParser->AudioControlAddr().Length() &&
rtpInfo.iSecondURL.Length() &&
audioAddr.Find( rtpInfo.iSecondURL) >= 0 )
{
iRTPTimeStampAudio = rtpInfo.iSecondTS ? rtpInfo.iSecondTS : 1;
iSeqFromRtpInfoForAudio = rtpInfo.iSecondSeq;
}
// ok, if we don't have rtp-info header, we don't know yet.
if ( rtpInfo.iFirstURL.Length() == 0 &&
rtpInfo.iSecondURL.Length() == 0 )
{
iNoRtpInfoHeader++;
}
else
{
// We have RTP-info, so control stream is no longer mandatory
// Mark control streams as "found"
StreamFound( EAudioControlStream );
StreamFound( EVideoControlStream );
//StreamFound( ESubTitleControlStream );
iSessionObs.StatusChanged(
MCRPacketSource::ERtpStateSeqAndTSAvailable );
}
// Live state
if ( iResponses[ERTSPPlaySent]->IsLiveStream() || iSdpParser->IsLiveStream() )
{
if ( iObserver )
{
iObserver->ConnectionStatusChange(
iOwningSession.SourceChecksum(),
ECRStreamIsLiveStream, KErrNone );
}
}
// Notify seeking
if ( iObserver )
{
iObserver->ConnectionStatusChange(
iOwningSession.SourceChecksum(),
ECRReadyToSeek, KErrNone );
}
}
break;
default:
// by default extract no information
break;
}
// Then continue with business:
SendRtspCommandL(); // will change iStage also
}
// Authentication needed..
else if ( iResponses[iStage]->StatusCode() ==
CCRRtspResponse::ERTSPRespUnauthorized || // 401
iResponses[iStage]->StatusCode() ==
CCRRtspResponse::ERTSPRespProxyAuthenticationRequired ) // 407
{
iAuthFailedCount++;
if ( iUserName &&
iUserName->Length() &&
iPassword &&
iAuthFailedCount == 1 )
{
iAuthenticationNeeded = ETrue;
iAuthType = iResponses[iStage]->AuthenticationTypeL().AllocL();
iRealm = iResponses[iStage]->RealmL().AllocL();
iOpaque = iResponses[iStage]->OpaqueL().AllocL();
iNonce = iResponses[iStage]->NonceL().AllocL();
SendAuthDescribeL();
}
else
{
iAuthFailedCount = 0;
LOG( "CCRRtspPacketSource::ProcessRtspResponseL() Authentication failure !" );
// Cleanup
iOwningSession.SourceStop();
if ( iObserver )
{
iObserver->ConnectionStatusChange(
iOwningSession.SourceChecksum(),
ECRAuthenticationNeeded, KErrNone );
}
}
}
else if ( iResponses[iStage]->StatusCode() == CCRRtspResponse::ERTSPRespUnsupportedTransport ) // 461
{
LOG1( "CCRRtspPacketSource::ProcessRtspResponseL() - Unsupported Transport: %d", iTransport );
if ( iConnection.GetHeuristic( CCRConnection::EUdpStreamingBlocked ) )
{
// Using TCP, change to UDP
LOG( "CCRRtspPacketSource::ProcessRtspResponseL() - Change TCP to UDP" );
iConnection.SetHeuristic( CCRConnection::EUdpStreamingBlocked, EFalse );
// Notify observer at client side:
ProcessRtspErrorResponseL( iResponses[iStage]->StatusCode() );
}
else
{
// Using UDP, change to TCP
LOG( "CCRRtspPacketSource::ProcessRtspResponseL() - Change UDP to TCP");
iConnection.SetHeuristic( CCRConnection::EUdpStreamingBlocked, ETrue );
// Notify observer at client side:
ProcessRtspErrorResponseL( iResponses[iStage]->StatusCode() );
}
}
else
{
// before doing cleanup, notify observer at client side:
ProcessRtspErrorResponseL( iResponses[iStage]->StatusCode() );
}
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::ProcessRtspErrorResponseL
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::ProcessRtspErrorResponseL(
CCRRtspResponse::TResponseCode aErrorCode )
{
SCRQueueEntry entry;
entry.iMsg = ECRMsgQueueConnectionError;
switch ( aErrorCode )
{
case CCRRtspResponse::ERTSPRespLowOnStorageSpace:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespMultipleChoices:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespMovedPermanently:
entry.iErr = KErrNotFound;
break;
case CCRRtspResponse::ERTSPRespMovedTemporarily:
entry.iErr = KErrNotFound;
break;
case CCRRtspResponse::ERTSPRespSeeOther:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespNotModified:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespUseProxy:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespBadRequest:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespPaymentRequired:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespForbidden:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespGone:
case CCRRtspResponse::ERTSPRespConferenceNotFound:
case CCRRtspResponse::ERTSPRespNotFound:
entry.iErr = KErrNotFound;
break;
case CCRRtspResponse::ERTSPRespMethodNotAllowed:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespNotAcceptable:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespRequestTimeOut:
entry.iErr = KErrTimedOut;
break;
case CCRRtspResponse::ERTSPRespLengthRequired:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespPreconditionFailed:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespRequestEntityTooLarge:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespRequestURITooLarge:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespParameterNotUnderstood:
entry.iErr = KErrArgument;
break;
case CCRRtspResponse::ERTSPRespNotEnoughBandwidth:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespSessionNotFound:
entry.iErr = KErrCouldNotConnect;
break;
case CCRRtspResponse::ERTSPRespMethodNotValidInThisState:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespHeaderFieldNotValidForResource:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespInvalidRange:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespParameterIsReadOnly:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespAggregateOperationNotAllowed:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespOnlyAggregateOperationAllowed:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespUnsupportedTransport:
entry.iErr = KErrCouldNotConnect;
break;
case CCRRtspResponse::ERTSPRespDestinationUnreachable:
entry.iErr = KErrCouldNotConnect;
break;
case CCRRtspResponse::ERTSPRespInternalServerError:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespNotImplemented:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespBadGateway:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespServiceUnavailable:
entry.iErr = KErrCouldNotConnect;
break;
case CCRRtspResponse::ERTSPRespGatewayTimeOut:
entry.iErr = KErrGeneral;
break;
case CCRRtspResponse::ERTSPRespUnsupportedMediaType:
case CCRRtspResponse::ERTSPRespOptionNotSupported:
case CCRRtspResponse::ERTSPRespRTSPVersionNotSupported:
entry.iErr = KErrNotSupported;
break;
default:
entry.iErr = KErrGeneral;
break;
}
if ( iObserver )
{
iObserver->ConnectionStatusChange(
iOwningSession.SourceChecksum(), ECRConnectionError, entry.iErr );
}
// Try tear down first
if ( Stop() == KErrDisconnected )
{
iOwningSession.SourceStop();
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::StartRtspTimeout
// Starts RTSP command response timeout.
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::StartRtspTimeout( TTimeIntervalMicroSeconds32 aTime )
{
// Start a timeout timer to wait for the server to respond.
// If the server doesn't respond in time, cleanup will be initialized.
if ( !iRtspTimeout )
{
TRAPD( err, iRtspTimeout =
CPeriodic::NewL( CActive::EPriorityStandard ) );
if ( err != KErrNone )
{
// Timer creation failed, start cleanup immediately
iOwningSession.SourceStop();
}
}
else
{
iRtspTimeout->Cancel();
}
// Start timeout timer
iRtspTimeout->Start(
aTime,
aTime,
TCallBack( RtspTimeoutCallback, this ) );
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::RtspTimeoutCallback
// Callback for RTSP response timeout. Just ask session to start cleanup
// -----------------------------------------------------------------------------
//
TInt CCRRtspPacketSource::RtspTimeoutCallback( TAny* aPtr )
{
LOG( "CCRRtspPacketSource::RtspTimeoutCallback()" );
CCRRtspPacketSource* self = static_cast<CCRRtspPacketSource*>( aPtr );
self->iRtspTimeout->Cancel();
self->iOwningSession.SourceStop();
return 0;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SendRtspCommandL
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::SendRtspCommandL()
{
LOG1( "CCRRtspPacketSource::SendRtspCommandL(), iStage: %d", iStage );
if ( iPostPonedPlay )
{
iPostPonedPlay = EFalse;
Play( iStartPos, iEndPos );
}
else
{
switch ( iStage )
{
case ERTSPInit:
case ERTSPOptSent:
{
delete iPrevCommands[ERTSPDescSent];
iPrevCommands[ERTSPDescSent] = NULL;
iPrevCommands[ERTSPDescSent] = CCRRtspCommand::NewL();
iPrevCommands[ERTSPDescSent]->SetCommand(
CCRRtspCommand::ERTSPCommandDESCRIBE );
TPtrC8 uriDes ( iRtspUri8->Des() );
iPrevCommands[ERTSPDescSent]->SetURL( uriDes );
iPrevCommands[ERTSPDescSent]->SetCSeq( iCSeq++ );
if ( iUserAgent )
{
iPrevCommands[ERTSPDescSent]->SetUserAgentL( *iUserAgent );
}
if ( iWapProfile )
{
iPrevCommands[ERTSPDescSent]->SetWapProfileL( *iWapProfile );
}
if ( iBandwidth )
{
iPrevCommands[ERTSPDescSent]->SetBandwidth( iBandwidth );
}
if ( iRtspSock )
{
iRtspSock->SendData( iPrevCommands[ERTSPDescSent]->ProduceL() );
StartRtspTimeout( KCRRtspResponseTimeout );
iStage = ERTSPDescSent;
}
}
break;
case ERTSPDescSent:
if ( iSdpParser )
{
const TInt audio( iSdpParser->MediaIdentifierAudio() );
const TInt video( iSdpParser->MediaIdentifierVideo() );
TBool videoExists( iSdpParser->VideoControlAddr().Length() > 0 );
TBool audioExists( iSdpParser->AudioControlAddr().Length() > 0 );
/* If both medias are reported with dynamic payload
* type and audio stream is reported with lower
* payload type, then some servers don't work correctly
* if the SETUP commands are not in correct order, ie.
* we need to first SETUP the audio stream here.
*/
const TBool audioBeforeVideo(
audioExists && audio >= 96 && video >= 96 && audio < video );
if ( videoExists && !audioBeforeVideo )
{
SendSetupCommandL( iSdpParser->VideoControlAddr(), EFalse );
iStage = ERTSPSetupVideoSent;
}
else if ( audioExists )
{
SendSetupCommandL( iSdpParser->AudioControlAddr(), ETrue );
iStage = ERTSPSetupAudioSent;
}
else
{
LOG1( "CCRRtspPacketSource::SendRtspCommand stag %d have no audio nor video",
( TInt )iStage );
// no audio, no video, el panique grande
iOwningSession.SourceStop();
}
}
break;
case ERTSPSetupAudioSent:
{
const TInt audio( iSdpParser->MediaIdentifierAudio() );
const TInt video( iSdpParser->MediaIdentifierVideo() );
if ( audio >= 96 && video >= 96 && audio < video &&
iSdpParser && iSdpParser->VideoControlAddr().Length() )
{
// Video exists also and has not been setup before, so
// let's setup it now.
TPtrC8 ctrlAddr ( iSdpParser->VideoControlAddr() );
SendSetupCommandL( ctrlAddr, EFalse );
iStage = ERTSPSetupVideoSent;
}
else
{
ConditionallySetupMultiCastOrTcpStreamingL();
}
}
break;
case ERTSPSetupVideoSent:
{
const TInt audio( iSdpParser->MediaIdentifierAudio() );
const TInt video( iSdpParser->MediaIdentifierVideo() );
// Check explanation for this in case ERTSPDescSent above.
const TBool audioBeforeVideo(
audio >= 96 && video >= 96 && audio < video );
// Then send audio, if applicable:
if ( iSdpParser && iSdpParser->AudioControlAddr().Length() &&
!audioBeforeVideo )
{
TPtrC8 ctrlAddr ( iSdpParser->AudioControlAddr() );
SendSetupCommandL( ctrlAddr, ETrue );
iStage = ERTSPSetupAudioSent;
}
else
{
// there is no audio that need setup so lets check also multicast+tcp
ConditionallySetupMultiCastOrTcpStreamingL();
}
}
break;
case ERTSPPauseSent:
// If we're paused, do zero the buffer, in tcp streaming case
// some servers seem to send packets even after play..
break;
case ERTSPReadyToPlay:
// In these stages send no further commands
break;
case ERTSPPlaySent:
// Start timer for UDP reception and start streaming
if ( iTransport == ERTPOverUDP )
{
iUdpReceptionTimer->Cancel();
iUdpReceptionTimer->After( KCRRtspRtpUdpTimeout );
}
iStage = ERTSPPlaying;
if ( !iNoRtpInfoHeader )
{
iSessionObs.StatusChanged( MCRPacketSource::ERtpStatePlaying );
}
break;
case ERTSPPlaying:
// None
break;
case ERTSPTearDownSent:
iPostPonedPlay = EFalse;
iOwningSession.SourceStop();
break;
default:
// By default send no further commands
break;
}
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SendPlayCommandL
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::SendPlayCommandL(void)
{
delete iPrevCommands[ERTSPPlaySent];
iPrevCommands[ERTSPPlaySent] = NULL;
iPrevCommands[ERTSPPlaySent] = CCRRtspCommand::NewL();
iPrevCommands[ERTSPPlaySent]->SetCommand ( CCRRtspCommand::ERTSPCommandPLAY );
TPtrC8 uriDes( iRtspUri8->Des() );
iPrevCommands[ERTSPPlaySent]->SetURL( uriDes );
iPrevCommands[ERTSPPlaySent]->SetCSeq( iCSeq ++ );
iPrevCommands[ERTSPPlaySent]->SetRange( iStartPos , iEndPos );
if ( iUserAgent )
{
iPrevCommands[ERTSPPlaySent]->SetUserAgentL( *iUserAgent );
}
if ( iSessionId.Ptr() )
{
iPrevCommands[ERTSPPlaySent]->SetSessionId( iSessionId );
}
if ( iAuthenticationNeeded )
{
AddAuthenticationL( ERTSPPlaySent );
}
if ( iRtspSock )
{
iRtspSock->SendData( iPrevCommands[ERTSPPlaySent]->ProduceL() );
StartRtspTimeout( KCRRtspResponseTimeout );
iStage = ERTSPPlaySent;
}
iStartPos = KRealZero;
iEndPos = KRealMinusOne;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SendPauseCommandL
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::SendPauseCommandL(void)
{
delete iPrevCommands[ERTSPPauseSent];
iPrevCommands[ERTSPPauseSent] = NULL;
iPrevCommands[ERTSPPauseSent] = CCRRtspCommand::NewL();
iPrevCommands[ERTSPPauseSent]->SetCommand ( CCRRtspCommand::ERTSPCommandPAUSE );
TPtrC8 uriDes( iRtspUri8->Des() );
iPrevCommands[ERTSPPauseSent]->SetURL( uriDes );
iPrevCommands[ERTSPPauseSent]->SetCSeq( iCSeq ++ );
if ( iUserAgent )
{
iPrevCommands[ERTSPPauseSent]->SetUserAgentL( *iUserAgent );
}
if ( iSessionId.Ptr() )
{
iPrevCommands[ERTSPPauseSent]->SetSessionId( iSessionId );
}
if ( iAuthenticationNeeded )
{
AddAuthenticationL( ERTSPPauseSent );
}
if ( iRtspSock )
{
iRtspSock->SendData( iPrevCommands[ERTSPPauseSent]->ProduceL() );
StartRtspTimeout( KCRRtspResponseTimeout );
iStage = ERTSPPauseSent;
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SendSetupCommandL
// -----------------------------------------------------------------------------
//
TInt CCRRtspPacketSource::SendSetupCommandL(
const TDesC8& aControlAddr,
TBool aForAudio )
{
TCRRTSPStage newStage = aForAudio ? ERTSPSetupAudioSent : ERTSPSetupVideoSent;
delete iPrevCommands[newStage];
iPrevCommands[newStage] = NULL;
iPrevCommands[newStage] = CCRRtspCommand::NewL();
iPrevCommands[newStage]->SetCommand ( CCRRtspCommand::ERTSPCommandSETUP );
iPrevCommands[newStage]->SetURL( aControlAddr );
iPrevCommands[newStage]->SetCSeq( iCSeq ++ );
iPrevCommands[newStage]->SetTransport( iTransport );
// Map stream to port number (when streaming over UDP) or channel (over TCP)
// base: iClientPort for UDP, 0 for TCP
// video: (base+0, base+1)
// audio: (base+2, base+3) or (base+0, base+1) when audio only
TInt portbase( ( iTransport == ERTPOverUDP )? iClientPort: 0 );
TInt portoffset( ( aForAudio && iSdpParser->VideoControlAddr().Length() )? 2: 0 );
iPrevCommands[newStage]->SetClientPort( portbase + portoffset );
if ( iSessionId.Ptr() )
{
iPrevCommands[newStage]->SetSessionId ( iSessionId );
}
if ( iAuthenticationNeeded )
{
AddAuthenticationL( newStage );
}
if ( iUserAgent )
{
iPrevCommands[newStage]->SetUserAgentL( *iUserAgent );
}
if ( iWapProfile )
{
iPrevCommands[newStage]->SetWapProfileL( *iWapProfile );
}
if ( iRtspSock )
{
iRtspSock->SendData( iPrevCommands[newStage]->ProduceL() );
StartRtspTimeout( KCRRtspResponseTimeout );
}
return KErrNone;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SendTearDownCommandL
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::SendTearDownCommandL()
{
CCRRtspCommand*& teardowncmd = iPrevCommands[ERTSPTearDownSent];
iPostPonedPlay = EFalse;
if ( teardowncmd )
{
delete teardowncmd; teardowncmd = NULL;
}
teardowncmd = CCRRtspCommand::NewL();
teardowncmd->SetCommand( CCRRtspCommand::ERTSPCommandTEARDOWN );
TPtrC8 uri( iRtspUri8->Des() );
teardowncmd->SetURL( uri );
teardowncmd->SetCSeq( iCSeq++ );
if ( iSessionId.Ptr() )
{
teardowncmd->SetSessionId( iSessionId );
}
if ( iUserAgent )
{
teardowncmd->SetUserAgentL( *iUserAgent );
}
if ( iAuthenticationNeeded )
{
AddAuthenticationL( ERTSPTearDownSent );
}
if ( iRtspSock )
{
iRtspSock->SendData( teardowncmd->ProduceL() );
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SendOptionsCommandL
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::SendOptionsCommandL(void)
{
delete iPrevCommands[ERTSPOptSent];
iPrevCommands[ERTSPOptSent] = NULL;
iPrevCommands[ERTSPOptSent] = CCRRtspCommand::NewL();
iPrevCommands[ERTSPOptSent]->SetCommand ( CCRRtspCommand::ERTSPCommandOPTIONS );
TPtrC8 uriDes ( iRtspUri8->Des() );
iPrevCommands[ERTSPOptSent]->SetURL ( uriDes );
iPrevCommands[ERTSPOptSent]->SetCSeq ( iCSeq ++ );
if ( iUserAgent )
{
iPrevCommands[ERTSPOptSent]->SetUserAgentL( *iUserAgent );
}
if ( iSessionId.Ptr() )
{
iPrevCommands[ERTSPOptSent]->SetSessionId ( iSessionId );
}
if ( iAuthenticationNeeded )
{
AddAuthenticationL( ERTSPOptSent );
}
if ( iRtspSock )
{
iRtspSock->SendData( iPrevCommands[ERTSPOptSent]->ProduceL() );
}
// Sending options ping does not change our state
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SetupRTPSessions
// -----------------------------------------------------------------------------
//
TInt CCRRtspPacketSource::SetupRTPSessions( void )
{
TInt retval( KErrNone );
if ( !iRtspSock )
{
retval = KErrNotReady;
}
else
{
TInetAddr localAddr( iRtspSock->LocalAddr() );
TInetAddr remoteAddr( iRtspSock->ConnectedAddr() );
// Clear used streams
iReceiveStreams.Reset();
iTrafficFound = EFalse;
// First audio:
if ( iRtspSock && iResponses[ERTSPSetupAudioSent] )
{
if ( iTransport == ERTPOverMulticast )
{
retval = CreateMulticastSocket( ERTPAudioSend1,
iResponses[ERTSPSetupAudioSent]->Destination(),
iResponses[ERTSPSetupAudioSent]->ClientPort() );
if ( retval == KErrNone )
{
retval = CreateMulticastSocket( ERTPAudioSend2,
iResponses[ERTSPSetupAudioSent]->Destination(),
iResponses[ERTSPSetupAudioSent]->ClientPort()+1 );
}
}
else
{
localAddr.SetPort( iResponses[ERTSPSetupAudioSent]->ClientPort() );
remoteAddr.SetPort( iResponses[ERTSPSetupAudioSent]->ServerPort() );
#if defined ( LIVE_TV_FILE_TRACE ) || defined ( LIVE_TV_RDEBUG_TRACE )
TName _addr;
localAddr.Output( _addr );
LOG2( "localaddr for video is %S:%d", &_addr, localAddr.Port() );
remoteAddr.Output( _addr );
LOG2( "remoteAddr for video is %S:%d", &_addr, remoteAddr.Port() );
#endif // LIVE_TV_FILE_TRACE || LIVE_TV_RDEBUG_TRACE
retval = CreateUnicastSocket( ERTPAudioSend1, localAddr, remoteAddr );
if ( retval == KErrNone )
{
localAddr.SetPort( localAddr.Port()+1 );
remoteAddr.SetPort( remoteAddr.Port()+1 );
retval = CreateUnicastSocket( ERTPAudioSend2, localAddr, remoteAddr );
}
}
if ( retval == KErrNone )
{
TRAP( retval, iAudioSession.OpenL(
iRTPSockArr[ERTPAudioSend1]->Socket(),
KAverageExpectedRtpPacketMaxSize,
iRTPSockArr[ERTPAudioSend2]->Socket(),
EPriorityNormal, KCRCName() ) );
}
LOG1( "CCRRtspPacketSource::SetupRTPSessions audio sess open: %d", retval );
if ( !retval )
{
SetRtpSession( iAudioSession , iSdpParser->AudioTimerGranularity() );
iAudioSession.SetBandwidth( iSdpParser->AudioBitrate() * 1000 );
TRAP( retval, iAudioSession.PrivRegisterEventCallbackL( ERtpNewSource,
( TRtpCallbackFunction )CCRRtspPacketSource::AudioRTPCallBack, this ) );
TReceiveStream audioDataStream;
audioDataStream.iStreamType = EAudioStream;
audioDataStream.iDataReceived = EFalse;
iReceiveStreams.Append( audioDataStream );
LOG( "CCRRtspPacketSource::SetupRTPSessions - AudioStream found" );
TReceiveStream audioControlStream;
audioControlStream.iStreamType = EAudioControlStream;
audioControlStream.iDataReceived = EFalse;
LOG( "CCRRtspPacketSource::SetupRTPSessions - AudioControlStream found" );
iReceiveStreams.Append( audioControlStream );
LOG2( "CCRRtspPacketSource::SetupRTPSessions audio stat: %d, ts: %u",
retval, ( TUint )iRTPTimeStampAudio );
}
else
{
if ( iObserver )
{
iObserver->ConnectionStatusChange(
iOwningSession.SourceChecksum(),
ECRConnectionError, retval );
}
iOwningSession.SourceStop();
}
}
// Then video
if ( retval == KErrNone && iRtspSock && iResponses[ERTSPSetupVideoSent] )
{
if ( iTransport==ERTPOverMulticast )
{
retval = CreateMulticastSocket( ERTPVideoSend1,
iResponses[ERTSPSetupVideoSent]->Destination(),
iResponses[ERTSPSetupVideoSent]->ClientPort() );
if ( retval==KErrNone )
{
retval = CreateMulticastSocket( ERTPVideoSend2,
iResponses[ERTSPSetupVideoSent]->Destination(),
iResponses[ERTSPSetupVideoSent]->ClientPort()+1 );
}
}
else
{
localAddr.SetPort( iResponses[ERTSPSetupVideoSent]->ClientPort() );
remoteAddr.SetPort( iResponses[ERTSPSetupVideoSent]->ServerPort() );
#if defined ( LIVE_TV_FILE_TRACE ) || defined ( LIVE_TV_RDEBUG_TRACE )
TName _addr;
localAddr.Output( _addr );
LOG2( "localaddr for video is %S:%d", &_addr, localAddr.Port() );
remoteAddr.Output( _addr );
LOG2( "remoteAddr for video is %S:%d", &_addr, remoteAddr.Port() );
#endif // LIVE_TV_FILE_TRACE || LIVE_TV_RDEBUG_TRACE
retval = CreateUnicastSocket( ERTPVideoSend1, localAddr, remoteAddr );
if ( retval == KErrNone )
{
localAddr.SetPort( localAddr.Port() + 1 );
remoteAddr.SetPort( remoteAddr.Port() + 1 );
retval = CreateUnicastSocket( ERTPVideoSend2, localAddr, remoteAddr );
}
}
if ( retval == KErrNone )
{
TRAP( retval, iVideoSession.OpenL( iRTPSockArr[ERTPVideoSend1]->Socket(),
KAverageExpectedRtpPacketMaxSize, iRTPSockArr[ERTPVideoSend2]->Socket(),
EPriorityNormal, KCRCName() ) );
}
LOG1( "CCRRtspPacketSource::SetupRTPSessions video sess open: %d", retval );
if ( !retval )
{
SetRtpSession( iVideoSession , iSdpParser->VideoTimerGranularity() );
iVideoSession.SetBandwidth( iSdpParser->VideoBitrate() * 1000 );
TRAP( retval, iVideoSession.PrivRegisterEventCallbackL( ERtpNewSource,
( TRtpCallbackFunction )CCRRtspPacketSource::VideoRTPCallBack, this ) );
TReceiveStream videoDataStream;
videoDataStream.iStreamType = EVideoStream;
videoDataStream.iDataReceived = EFalse;
LOG( "CCRRtspPacketSource::SetupRTPSessions - VideoStream found" );
iReceiveStreams.Append( videoDataStream );
TReceiveStream videoControlStream;
videoControlStream.iStreamType = EVideoControlStream;
videoControlStream.iDataReceived = EFalse;
LOG( "CCRRtspPacketSource::SetupRTPSessions - VideoControlStream found" );
iReceiveStreams.Append( videoControlStream );
LOG2( "CCRRtspPacketSource::SetupRTPSessions video stat: %d, ts: %u",
retval, ( TUint )iRTPTimeStampVideo );
}
else
{
if ( iObserver )
{
iObserver->ConnectionStatusChange(
iOwningSession.SourceChecksum(),
ECRConnectionError, retval );
}
iOwningSession.SourceStop();
}
}
}
return retval;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::CreateMulticastSocket
// -----------------------------------------------------------------------------
//
TInt CCRRtspPacketSource::CreateMulticastSocket(
TCRRTPSockId aSockId,
const TInetAddr& aGroupAddr,
TInt aPort )
{
// Alias for socket being created
CCRSock*& sock = iRTPSockArr[aSockId];
// Delete if already existing
if ( sock )
{
delete sock;
sock = NULL;
}
// Create socket
TRAPD( err, sock = CCRSock::NewL( *this, aSockId, iConnection.Connection(),
iSockServer, EFalse, EFalse) );
if ( err != KErrNone )
{
LOG2( "CCRRtspPacketSource::CreateMulticastSocket: CCRSock::NewL FAILED, sockId: %d, err: %d",
aSockId, err );
return err;
}
// Bind socket to local UDP port, issue no reads -> handled by RRtpSession
err = sock->ListenPort( aPort );
if ( err != KErrNone )
{
LOG2( "CCRRtspPacketSource::CreateMulticastSocket: ListenPort FAILED, port: %d, err: %d",
aPort, err );
return err;
}
err = sock->JoinGroup( aGroupAddr );
if ( err != KErrNone )
{
LOG1( "CCRRtspPacketSource::CreateMulticastSocket: JoinGroup FAILED, err: %d", err );
return err;
}
#if defined(LIVE_TV_FILE_TRACE) || defined(LIVE_TV_RDEBUG_TRACE)
TName group;
aGroupAddr.Output( group );
LOG3( "CCRRtspPacketSource::CreateMulticastSocket: sockid: %d, group: '%S', port: %d OK",
aSockId, &group, aPort );
#endif // LIVE_TV_FILE_TRACE || LIVE_TV_RDEBUG_TRACE
return KErrNone;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::CreateUnicastSocket
// -----------------------------------------------------------------------------
TInt CCRRtspPacketSource::CreateUnicastSocket(
TCRRTPSockId aSockId,
const TInetAddr& aLocalAddr,
const TInetAddr& /*aRemoteAddr*/ )
{
// Alias for socket being created
CCRSock*& sock = iRTPSockArr[aSockId];
// Delete if already existing
if ( sock )
{
delete sock;
sock = NULL;
}
// Create socket: EFalse=UDP, EFalse=issue no read (handled by RRtpSession)
TRAPD( err, sock = CCRSock::NewL( *this,aSockId, iConnection.Connection(),
iSockServer, EFalse, EFalse ) );
if ( err != KErrNone )
{
LOG2( "CCRRtspPacketSource::CreateUnicastSocket: CCRSock::NewL FAILED, sockId: %d, err: %d",
aSockId, err );
return err;
}
// Bind to local port, ignore remote address and port
TInt port = aLocalAddr.Port();
err = sock->ListenPort( port );
if ( err != KErrNone )
{
LOG2( "CCRRtspPacketSource::CreateUnicastSocket: ListenPort FAILED, port: %d, err: %d",
port, err );
return err;
}
#if defined(LIVE_TV_FILE_TRACE) || defined(LIVE_TV_RDEBUG_TRACE)
LOG2( "CCRRtspPacketSource::CreateUnicastSocket: sockid: %d, port: %d OK",
aSockId, port );
#endif // LIVE_TV_FILE_TRACE || LIVE_TV_RDEBUG_TRACE
return KErrNone;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::RTPPayloadProcessor
// This is called from audio and video callbacks when real payload packet
// is received from rtp stack.
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::RTPPayloadProcessor(
const TRtpEvent& aEvent,
const TBool aIsAudio )
{
// If udp traffic hasn't been flagged as found
// keep marking streams as found
if ( !iTrafficFound )
{
if ( aIsAudio )
{
StreamFound( EAudioStream );
}
else
{
StreamFound( EVideoStream );
}
// Cancel UDP timer, so as not to trigger TCP streaming
if ( CheckReceiveOfStreams() )
{
// We have traffic from all needed streams, cancel reception timer
// and set UDP flag.
iUdpReceptionTimer->Cancel();
iUdpFound = ETrue;
iTrafficFound = ETrue;
}
}
// Here process packet
RRtpReceivePacket p = aEvent.ReceiveSource().Packet();
TUint32 flag( 0 );
BigEndian::Put32( ( TUint8* )&flag, p.Flags() );
// Header
TCRRtpMessageHeader packetHeader;
memcpy( &packetHeader, &flag, sizeof( flag ) );
BigEndian::Put32( ( TUint8* )&packetHeader.iTimestamp, p.Timestamp() );
BigEndian::Put32( ( TUint8* )&packetHeader.iSSRC, p.SSRC() );
TPtrC8 rtpHeader( ( TUint8* )&packetHeader, sizeof( packetHeader ) );
if ( iNoRtpInfoHeader )
{
ConstructSeqAndTsForSink(
aIsAudio ? MCRPacketSource::EAudioStream : MCRPacketSource::EVideoStream,
0 /*nop*/, 0 /*nop*/, 0 /*nop*/, p.SequenceNumber() );
}
// Stream
MCRPacketSource::TCRPacketStreamId stream(
( aIsAudio )? MCRPacketSource::EAudioStream :
MCRPacketSource::EVideoStream );
iBuffer->AddPacket( stream, rtpHeader, p.Payload() );
// Count of packets
if ( aIsAudio )
{
iAudioBytes += p.Payload( ).Length();
iAudioPackets ++;
}
else
{
iVideoBytes += p.Payload( ).Length();
iVideoPackets ++;
}
p.Close();
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::AudioRTPCallBack
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::AudioRTPCallBack(
CCRRtspPacketSource* aPtr,
const TRtpEvent& aEvent )
{
switch ( aEvent.Type() )
{
case ERtpPacketReceived:
static_cast<CCRRtspPacketSource*>( aPtr )->
RTPPayloadProcessor( aEvent, ETrue );
break;
// RTCP
case ERtpSR:
{
// We have audio control traffic
if ( !aPtr->iTrafficFound )
{
aPtr->StreamFound( EAudioControlStream );
if ( aPtr->CheckReceiveOfStreams() )
{
// Cancel UDP timer, so as not to trigger TCP streaming
aPtr->iUdpReceptionTimer->Cancel();
aPtr->iUdpFound = ETrue;
aPtr->iTrafficFound = ETrue;
}
}
// Sender report
SenderReport( aPtr, aEvent, MCRPacketSource::EAudioControlStream );
}
break;
case ERtpNewSource:
{
// Handle audio
TRAPD( err, HandleNewSourceL( aPtr, aPtr->iRtpRecvSrcAudio, aEvent,
( TRtpCallbackFunction )CCRRtspPacketSource::AudioRTPCallBack ) );
if ( err )
{
LOG1( "CCRRtspPacketSource::AudioRTPCallBack(), HandleNewSourceL Leaved: %d", err );
aPtr->iOwningSession.SourceStop();
}
}
break;
case ERtpSessionFail:
case ERtpSourceFail:
LOG( "CCRRtspPacketSource::VideoRTPCallBack(), source/session fail" );
aPtr->iOwningSession.SourceStop();
if ( aPtr->iObserver )
{
aPtr->iObserver->ConnectionStatusChange(
aPtr->iOwningSession.SourceChecksum(),
ECRNormalEndOfStream, KErrSessionClosed );
}
break;
case ERtpBYE:
LOG( "CCRRtspPacketSource::AudioRTPCallBack(), ERtpBYE" );
if ( aPtr->iObserver )
{
aPtr->iObserver->ConnectionStatusChange(
aPtr->iOwningSession.SourceChecksum(),
ECRNormalEndOfStream, KErrNone );
}
break;
default:
LOG1( "CCRRtspPacketSource::AudioRTPCallBack default case, type 0x%x",
( TUint )( aEvent.Type() ) );
// by do nothing
break;
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::VideoRTPCallBack
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::VideoRTPCallBack(
CCRRtspPacketSource* aPtr,
const TRtpEvent& aEvent )
{
switch ( aEvent.Type() )
{
case ERtpPacketReceived:
static_cast<CCRRtspPacketSource*>( aPtr )->
RTPPayloadProcessor( aEvent, EFalse );
break;
// RTCP
case ERtpSR:
{
// We have video control traffic
if ( !aPtr->iTrafficFound )
{
aPtr->StreamFound( EVideoControlStream );
if ( aPtr->CheckReceiveOfStreams() )
{
// Cancel UDP timer, so as not to trigger TCP streaming
aPtr->iUdpReceptionTimer->Cancel();
aPtr->iUdpFound = ETrue;
aPtr->iTrafficFound = ETrue;
}
}
// Sender report
SenderReport( aPtr, aEvent, MCRPacketSource::EVideoControlStream );
}
break;
case ERtpNewSource:
{
// Handle video
TRAPD( err, HandleNewSourceL( aPtr, aPtr->iRtpRecvSrcVideo, aEvent,
( TRtpCallbackFunction )CCRRtspPacketSource::VideoRTPCallBack ) );
if ( err )
{
LOG1( "CCRRtspPacketSource::VideoRTPCallBack(), HandleNewSourceL Leaved: %d", err );
aPtr->iOwningSession.SourceStop();
}
}
break;
case ERtpSessionFail:
case ERtpSourceFail:
LOG( "CCRRtspPacketSource::VideoRTPCallBack(), Source/session fail" );
aPtr->iOwningSession.SourceStop();
if ( aPtr->iObserver )
{
aPtr->iObserver->ConnectionStatusChange(
aPtr->iOwningSession.SourceChecksum(),
ECRNormalEndOfStream, KErrSessionClosed );
}
break;
case ERtpBYE:
LOG( "CCRRtspPacketSource::VideoRTPCallBack(), ERtpBYE" );
if ( aPtr->iObserver )
{
aPtr->iObserver->ConnectionStatusChange(
aPtr->iOwningSession.SourceChecksum(),
ECRNormalEndOfStream, KErrNone );
}
break;
default:
LOG1( "CCRRtspPacketSource::VideoRTPCallBack default case, type 0x%x",
( TUint )( aEvent.Type() ) );
// By do nothing
break;
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SenderReport
// rfc-1305:
// NTP timestamps are represented as a 64-bit unsigned fixed-
// point number, in seconds relative to 0h on 1 January 1900.
// The integer part is in the first 32 bits and the fraction
// part in the last 32 bits.
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::SenderReport(
CCRRtspPacketSource* aPtr,
const TRtpEvent& aEvent,
MCRPacketSource::TCRPacketStreamId aStreamId )
{
TCRRtpSRReportHeader srReport;
srReport.iVersion = KRtpPacketVersion; // value is 2
srReport.iPadding = 0;
srReport.iReportCount = 0;
srReport.iPacketType = KSenderReportPacketType;
RRtpReceiveSource source( aEvent.ReceiveSource() );
BigEndian::Put16( ( TUint8* )&srReport.iLength, 6 );
BigEndian::Put32( ( TUint8* )&srReport.iSenderSSRC,
source.SSRC() );
BigEndian::Put32( ( TUint8* )&srReport.iMSWTimestamp,
source.GetSR().iSrPtr.ntp_sec );
BigEndian::Put32( ( TUint8* )&srReport.iLSWTimestamp,
source.GetSR().iSrPtr.ntp_frac );
BigEndian::Put32( ( TUint8* )&srReport.iRTPTimestamp,
source.GetSR().RTPTimestamp() );
BigEndian::Put32( ( TUint8* )&srReport.iSenderPacketCount,
aPtr->iAudioPackets );
BigEndian::Put32( ( TUint8* )&srReport.iSenderOctetCount,
aPtr->iAudioBytes );
TPtrC8 rtcpHeader( ( TUint8* )&srReport, sizeof( srReport ) );
aPtr->iBuffer->AddPacket( aStreamId, rtcpHeader );
// Verify Seq and Ts
if ( aPtr->iNoRtpInfoHeader )
{
aPtr->ConstructSeqAndTsForSink (
aStreamId,
source.GetSR().iSrPtr.ntp_sec,
source.GetSR().iSrPtr.ntp_frac,
source.GetSR().RTPTimestamp(),
0 ); // 0 not used
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::HandleNewSourceL
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::HandleNewSourceL(
CCRRtspPacketSource* aPtr,
RRtpReceiveSource& aSource,
const TRtpEvent& aEvent,
TRtpCallbackFunction aCallback )
{
// Cancel UDP timer, so as not to trigger TCP streaming
aPtr->iUdpReceptionTimer->Cancel();
delete aPtr->iPunchPacketSenderAudio;
aPtr->iPunchPacketSenderAudio = NULL;
if ( aSource.IsOpen() )
{
aSource.Close();
}
// Source
aSource = aEvent.Session().NewReceiveSourceL();
aSource.PrivRegisterEventCallbackL( ERtpPacketReceived, aCallback, aPtr );
aSource.PrivRegisterEventCallbackL( ERtpSR, aCallback, aPtr );
aSource.PrivRegisterEventCallbackL( ERtpBYE, aCallback, aPtr );
aSource.PrivRegisterEventCallbackL( ERtpSessionFail, aCallback, aPtr );
aSource.PrivRegisterEventCallbackL( ERtpSourceFail, aCallback, aPtr );
// Ping Timer
if ( !aPtr->iRtspPingTimer )
{
aPtr->iRtspPingTimer = CPeriodic::NewL( CActive::EPriorityLow );
aPtr->iRtspPingTimer->Start(
KDVR10Seconds, 2 * KDVR10Seconds, TCallBack( SendRtspPing, aPtr ) );
}
aEvent.Session().SendAPPL( KCRCName() );
aEvent.Session().SetRTCPAutoSend( ETrue );
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SendAuthDescribeL
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::SendAuthDescribeL( )
{
delete iPrevCommands[ERTSPDescSent];
iPrevCommands[ERTSPDescSent] = NULL;
iPrevCommands[ERTSPDescSent] = CCRRtspCommand::NewL();
iPrevCommands[ERTSPDescSent]->SetCommand (
CCRRtspCommand::ERTSPCommandDESCRIBE );
TPtrC8 uriDes ( iRtspUri8->Des() );
iPrevCommands[ERTSPDescSent]->SetURL ( uriDes );
iPrevCommands[ERTSPDescSent]->SetCSeq ( iCSeq ++ );
if ( iAuthType )
{
iPrevCommands[ERTSPDescSent]->SetAuthenticationTypeL( iAuthType->Des() );
}
if ( iNonce )
{
iPrevCommands[ERTSPDescSent]->SetNonceL( iNonce->Des() );
}
if ( iRealm )
{
iPrevCommands[ERTSPDescSent]->SetRealmL( iRealm->Des() );
}
if ( iOpaque )
{
iPrevCommands[ERTSPDescSent]->SetOpaqueL( iOpaque->Des() );
}
if ( iUserAgent )
{
iPrevCommands[ERTSPDescSent]->SetUserAgentL( *iUserAgent );
}
if ( iWapProfile )
{
iPrevCommands[ERTSPDescSent]->SetWapProfileL( *iWapProfile );
}
if ( iBandwidth )
{
iPrevCommands[ERTSPDescSent]->SetBandwidth( iBandwidth );
}
iPrevCommands[ERTSPDescSent]->SetUserNameL( iUserName->Des() );
iPrevCommands[ERTSPDescSent]->SetPassWdL( iPassword->Des() );
iPrevCommands[ERTSPDescSent]->SetRtspUriL( iRtspUri->Des() );
iPrevCommands[ERTSPDescSent]->SetAuthentication ( iAuthenticationNeeded );
if ( iRtspSock )
{
iRtspSock->SendData( iPrevCommands[ERTSPDescSent]->ProduceL() );
StartRtspTimeout( KCRRtspResponseTimeout );
}
iStage = ERTSPDescSent;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::AddAuthenticationL
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::AddAuthenticationL( TInt aCommand )
{
if ( iPrevCommands[aCommand] && iNonce &&
iOpaque && iUserName && iPassword )
{
iPrevCommands[aCommand]->SetAuthenticationTypeL( iAuthType->Des() );
iPrevCommands[aCommand]->SetNonceL( iNonce->Des() );
iPrevCommands[aCommand]->SetRealmL( iRealm->Des() );
iPrevCommands[aCommand]->SetOpaqueL( iOpaque->Des() );
iPrevCommands[aCommand]->SetUserNameL( iUserName->Des() );
iPrevCommands[aCommand]->SetPassWdL( iPassword->Des() );
iPrevCommands[aCommand]->SetRtspUriL( iRtspUri->Des() );
iPrevCommands[aCommand]->SetAuthentication ( iAuthenticationNeeded );
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::PunchPacketsSent
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::PunchPacketsSent( CCRPunchPacketSender* aPuncher )
{
if ( iPunchPacketSenderVideo && aPuncher == iPunchPacketSenderVideo )
{
iPunchPacketSentForVideo = ETrue;
}
if ( iPunchPacketSenderAudio && aPuncher == iPunchPacketSenderAudio )
{
iPunchPacketSentForAudio = ETrue;
}
if ( ( iPunchPacketSenderVideo && !iPunchPacketSenderAudio &&
iPunchPacketSentForVideo ) ||
( !iPunchPacketSenderVideo && iPunchPacketSenderAudio &&
iPunchPacketSentForAudio ) ||
( iPunchPacketSenderVideo && iPunchPacketSenderAudio &&
iPunchPacketSentForVideo && iPunchPacketSentForAudio ) )
{
LOG1( "PunchPacketsSent, play readiness: %d", iReadyToPlay );
SetupSessionsAndPlay();
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SetupSessionsAndPlay
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::SetupSessionsAndPlay()
{
// all needed punch packets are sent:
if ( SetupRTPSessions() != KErrNone )
{
iOwningSession.SourceStop();
}
else
{
// if we're ready to play, play
if ( iReadyToPlay )
{
TRAPD( err, SendPlayCommandL() );
if ( err != KErrNone )
{
iOwningSession.SourceStop();
}
}
else
{
iStage = ERTSPReadyToPlay;
}
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SendPunchPackets
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::SendPunchPacketsL( void )
{
LOG( "CCRRtspPacketSource::SendPunchPacketsL in" );
delete iPunchPacketSenderAudio;
iPunchPacketSenderAudio = NULL;
delete iPunchPacketSenderVideo;
iPunchPacketSenderVideo = NULL;
if ( iSdpParser &&iRtspSock && iResponses[ERTSPSetupVideoSent] )
{
TInetAddr localAddr = iRtspSock->LocalAddr();
TInetAddr remoteAddr = iRtspSock->ConnectedAddr();
localAddr.SetPort(iResponses[ERTSPSetupVideoSent]->ClientPort());
remoteAddr.SetPort(iResponses[ERTSPSetupVideoSent]->ServerPort());
iPunchPacketSenderVideo = CCRPunchPacketSender::NewL(
iConnection.Connection(), iSockServer,
localAddr, remoteAddr, 0, *this );
}
if ( iSdpParser && iRtspSock && iResponses[ERTSPSetupAudioSent] )
{
TInetAddr localAddr = iRtspSock->LocalAddr();
TInetAddr remoteAddr = iRtspSock->ConnectedAddr();
localAddr.SetPort(iResponses[ERTSPSetupAudioSent]->ClientPort());
remoteAddr.SetPort(iResponses[ERTSPSetupAudioSent]->ServerPort());
iPunchPacketSenderAudio = CCRPunchPacketSender::NewL(
iConnection.Connection(), iSockServer,
localAddr, remoteAddr, 0, *this );
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::ConnectionStatusChange
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::ConnectionStatusChange(
TInt /*aSessionId*/,
TCRConnectionStatus aStatus,
TInt /* aErr */ )
{
switch( aStatus )
{
// Connection has gone up or bearer has changed -> check bandwidth
case ECRBearerChanged:
{
LOG( "CCRRtspPacketSource::ConnectionStatusChange: IapUp or IapUp2G" );
if ( iSdpParser && iObserver )
{
// Unknown bitrate or bandwidth are returned as zero. Bitrates in kbit/s
TInt bitrate( iSdpParser->VideoBitrate() +
iSdpParser->AudioBitrate() );
TInt bandwidth( iConnection.MaximumBandwidth() / 1000 );
if ( bitrate > 0 && bandwidth > 0 && bandwidth < bitrate )
{
LOG2( "CCRRtspPacketSource::ConnectionStatusChange: clip_bitrate: %d, connection_bandwidth: %d -> NotEnoughBandwidth",
bitrate, bandwidth );
iObserver->ConnectionStatusChange(
iOwningSession.SourceChecksum(), ECRNotEnoughBandwidth, KErrNone );
}
}
break;
}
// Connection has gone down or error occured -> switch back to RTP/UDP transport
case ECRConnectionError:
case ECRIapDown:
{
LOG( "CCRRtspPacketSource::ConnectionStatusChange: IapDown or ConnectionError -> switch to RTP/UDP streaming" );
iConnection.SetHeuristic( CCRConnection::EUdpStreamingBlocked, EFalse );
break;
}
// Nothing to do for:
// ECRConnecting
// ECRAuthenticationNeeded
// ECRNotEnoughBandwidth
// ECRNormalEndOfStream
default:
{
LOG1( "CCRRtspPacketSource::ConnectionStatusChange: unhandled status: %d", aStatus );
break;
}
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::RegisterConnectionObs
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::RegisterConnectionObs( MCRConnectionObserver* aObserver )
{
iObserver = aObserver;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::UnregisterConnectionObs
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::UnregisterConnectionObs( )
{
iObserver = NULL;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SetRtpSession
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::SetRtpSession(
RRtpSession& aSession,
TReal aGranularity )
{
// Unit is 1/second
__ASSERT_DEBUG( iSdpParser != NULL, User::Panic( _L( "RTSP source" ), KErrBadHandle ) );
TUint32 howManyNanoSecondsIsOneTick(
( TUint32 )( TReal( 1000000000.0L ) / aGranularity ) );
LOG1( "CCRRtspPacketSource::SetRtpSession clock tick: %u", howManyNanoSecondsIsOneTick );
aSession.SetRTPTimeConversion( 0, howManyNanoSecondsIsOneTick );
aSession.SetRtpStreamParameters( KDVRMinSequential, // 1
KDVRMaxMisorder, // 50
KDVRMaxDropOut ); // 3000
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::SendRtspPing
// -----------------------------------------------------------------------------
//
TInt CCRRtspPacketSource::SendRtspPing( TAny* aSelfPtr )
{
CCRRtspPacketSource* ptr = static_cast<CCRRtspPacketSource*> ( aSelfPtr );
TRAPD( err, ptr->SendOptionsCommandL() );
return err;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::ConstructSeqAndTsForSink
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::ConstructSeqAndTsForSink (
MCRPacketSource::TCRPacketStreamId aStreamId,
TUint32 aMSWTimestamp,
TUint32 aLSWTimestamp,
TUint32 aRTPTimestamp,
TUint aSeq )
{
switch ( aStreamId )
{
case EAudioStream:
if ( iRTPTimeStampAudio )
{
iSeqFromRtpInfoForAudio = aSeq;
if ( iSeqFromRtpInfoForAudio == 0 )
{
iSeqFromRtpInfoForAudio++;
}
LOG1( "CCRRtspPacketSource::ConstructSeqAndTsForSink(), Audio seq: %d ", ( int )aSeq );
// We may declare that we have seq+ts if we're here and have only audio or
// if we're here and have both audio and video and have also seq for video
if ( ( iSdpParser->SupportedContent() == CDvrSdpParser::EDvrAudioOnly ) ||
( iSdpParser->SupportedContent() == CDvrSdpParser::EDvrBothAudioAndVideo &&
iSeqFromRtpInfoForVideo && iRTPTimeStampVideo ) )
{
iSessionObs.StatusChanged(
MCRPacketSource::ERtpStateSeqAndTSAvailable );
iNoRtpInfoHeader = EFalse;
if ( iStage == ERTSPPlaying )
{
iSessionObs.StatusChanged(
MCRPacketSource::ERtpStatePlaying );
}
}
}
break;
case EAudioControlStream:
if ( !iMSWTimestamp )
{ // no wall clock time yet set
iMSWTimestamp = aMSWTimestamp;
iLSWTimestamp = aLSWTimestamp;
iRTPTimeStampAudio = aRTPTimestamp;
if ( iRTPTimeStampAudio == 0 )
{
iRTPTimeStampAudio++;
}
}
else
{
// Sync audio with video
TInt64 wallClockOfVideo = MAKE_TINT64 ( iMSWTimestamp , iLSWTimestamp );
TInt64 wallClockOfAudio = MAKE_TINT64 ( aMSWTimestamp , aLSWTimestamp );
// Then figure out the difference. unit is now difficult ; upper
// 32 bits contain whole seconds, lower contains fraction
TInt64 wallClockDifference( wallClockOfVideo - wallClockOfAudio );
// Now, the aRTPTimestamp has different scale, declared in SDP.
// first make one second that has same scale as wallClockDifference
TInt64 granularity( MAKE_TINT64( 1, 0 ) );
// Then divide that one second with the given granularity. variable
// granularity will now contain in its low 32 bits the fraction of the
// second that re-presents one clock tick (e.g. 1/90000 sec for video)
granularity = granularity / static_cast<TInt64>(
iSdpParser->AudioTimerGranularity() );
// Then divide our difference with this fraction of second
TInt64 wallClockDifferenceGranular = wallClockDifference / granularity;
// unit of wallClockDifferenceGranular is now 2^32 / granularity
TInt32 wallClockDifferenceGranular32 = wallClockDifferenceGranular;
LOG2( "CCRRtspPacketSource::ConstructSeqAndTsForSink(), Audio ts: %u adjust by: %d",
aRTPTimestamp , wallClockDifferenceGranular32 );
iRTPTimeStampAudio = aRTPTimestamp + wallClockDifferenceGranular32;
if ( iRTPTimeStampAudio == 0 )
{
iRTPTimeStampAudio++;
}
}
break;
case EVideoStream:
if ( iRTPTimeStampVideo )
{
iSeqFromRtpInfoForVideo = aSeq;
if ( iSeqFromRtpInfoForVideo == 0 )
{
iSeqFromRtpInfoForVideo++;
}
LOG1( "CCRRtspPacketSource::ConstructSeqAndTsForSink(), Video seq: %d ",
( int )aSeq );
// We may declare that we have seq+ts if we're here and have only video or
// if we're here and have both and have also seq for video
if ( ( iSdpParser->SupportedContent() == CDvrSdpParser::EDvrVideoOnly ) ||
( iSdpParser->SupportedContent() == CDvrSdpParser::EDvrBothAudioAndVideo &&
iSeqFromRtpInfoForAudio && iRTPTimeStampAudio ) )
{
iSessionObs.StatusChanged(
MCRPacketSource::ERtpStateSeqAndTSAvailable );
iNoRtpInfoHeader = EFalse;
if ( iStage == ERTSPPlaying )
{
iSessionObs.StatusChanged(
MCRPacketSource::ERtpStatePlaying );
}
}
}
break;
case EVideoControlStream:
if ( !iMSWTimestamp )
{ // No wall clock time yet set
iMSWTimestamp = aMSWTimestamp;
iLSWTimestamp = aLSWTimestamp;
iRTPTimeStampVideo = aRTPTimestamp;
if ( iRTPTimeStampVideo == 0 )
{
iRTPTimeStampVideo++;
}
}
else
{
// Sync audio with video
TInt64 wallClockOfAudio = MAKE_TINT64 ( iMSWTimestamp , iLSWTimestamp );
TInt64 wallClockOfVideo = MAKE_TINT64 ( aMSWTimestamp , aLSWTimestamp );
// Then figure out the difference. unit is now difficult ; upper
// 32 bits contain whole seconds, lower contains fraction
TInt64 wallClockDifference( wallClockOfAudio - wallClockOfVideo );
// Now, the aRTPTimestamp has different scale, declared in SDP.
// first make one second that has same scale as wallClockDifference
TInt64 granularity( MAKE_TINT64( 1, 0 ) );
// Then divide that one second with the given granularity. variable
// granularity will now contain in its low 32 bits the fraction of the
// second that re-presents one clock tick (e.g. 1/90000 sec for video)
granularity = granularity / static_cast<TInt64>(
iSdpParser->VideoTimerGranularity());
// Then divide our difference with this fraction of second
TInt64 wallClockDifferenceGranular = wallClockDifference / granularity;
// Unit of wallClockDifferenceGranular is now 2^32 / granularity
TInt32 wallClockDifferenceGranular32 = wallClockDifferenceGranular;
LOG2( "CCRRtspPacketSource::ConstructSeqAndTsForSink(), Video ts: %u adjust by: %d",
aRTPTimestamp , wallClockDifferenceGranular32 );
iRTPTimeStampVideo = aRTPTimestamp + wallClockDifferenceGranular32;
if ( iRTPTimeStampVideo == 0 )
{
iRTPTimeStampVideo++;
}
}
break;
default:
// no thing
break;
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::ConditionallySetupMultiCastOrTcpStreamingL
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::ConditionallySetupMultiCastOrTcpStreamingL ( void )
{
// UDP: Punch packets or play sent in ProcessRTSPResponseL, so do nothing.
if ( iTransport == ERTPOverUDP )
{
}
// Multicast: no punch packets needed but session setup yes
else if ( iTransport == ERTPOverMulticast )
{
SetupSessionsAndPlay();
}
// TCP: no punch packets or session, just send PLAY .. but wait for UI
else if ( iTransport == ERTPOverTCP )
{
if ( iReadyToPlay )
{
SendPlayCommandL();
}
else
{
iStage = ERTSPReadyToPlay;
}
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::CheckReceiveOfStreams
// -----------------------------------------------------------------------------
//
TBool CCRRtspPacketSource::CheckReceiveOfStreams()
{
TBool retVal( ETrue );
// Go through all streams and check that all streams have receive flag on,
// if not return false.
for ( TInt i = 0 ; i < iReceiveStreams.Count() ; i++ )
{
if ( iReceiveStreams[i].iDataReceived == EFalse )
{
LOG1( "CCRRtspPacketSource::CheckReceiveOfStreams - Missing atleast stream %d", iReceiveStreams[i].iStreamType );
retVal = EFalse;
break;
}
}
if ( retVal )
{
LOG( "CCRRtspPacketSource::CheckReceiveOfStreams - Receiving from all streams!" );
}
return retVal;
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::StreamFound
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::StreamFound( TCRPacketStreamId aStreamType )
{
// Go through streams and find correct stream to set the receive flag.
for ( TInt i = 0 ; i < iReceiveStreams.Count(); i++ )
{
if ( iReceiveStreams[i].iStreamType == aStreamType )
{
iReceiveStreams[i].iDataReceived = ETrue;
LOG1( "CCRRtspPacketSource::StreamFound - Stream %d found", iReceiveStreams[i].iStreamType );
break;
}
}
}
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::ResetStreamFlags
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::ResetStreamFlags( )
{
// Go through streams and clear receiving flag.
for ( TInt i = 0 ; i < iReceiveStreams.Count() ; i++ )
{
iReceiveStreams[i].iDataReceived = EFalse;
}
// We have to check receive again
iTrafficFound = EFalse;
}
#if defined ( LIVE_TV_FILE_TRACE ) || defined ( LIVE_TV_RDEBUG_TRACE )
// -----------------------------------------------------------------------------
// CCRRtspPacketSource::ShowHeader
// -----------------------------------------------------------------------------
//
void CCRRtspPacketSource::ShowHeader(
const TDesC8& aRtcpHeader,
const TCRRtpSRReportHeader& aSrReport )
{
TBuf<100> b( KNullDesC );
LOG1( "CCRRtspPacketSource::TCP control packet len: %d", aRtcpHeader.Length() );
for ( TInt j( 0 ); j < 32 && j < aRtcpHeader.Length(); j++ )
{
b.AppendFormat( _L( "%2X " ), ( unsigned )( aRtcpHeader[j] ) );
if ( j > 0 && ( ( j % 16 ) == 0 ) )
{
LOG2( "%d -> %S", j, &b );
b.Zero();
}
}
LOG1( "iVersion %u", ( unsigned )aSrReport.iVersion );
LOG1( "iPadding %u", ( unsigned )aSrReport.iPadding );
LOG1( "iReportCount %u",( unsigned )aSrReport.iReportCount );
LOG1( "iPacketType %u", ( unsigned )aSrReport.iPacketType );
LOG1( "iLength %u",
( unsigned)BigEndian::Get16( ( const TUint8* )&aSrReport.iLength ) );
LOG1( "iSenderSSRC %u",
( unsigned )BigEndian::Get32( ( const TUint8* )&aSrReport.iSenderSSRC ) );
LOG1( "iMSWTimestamp %u",
( unsigned )BigEndian::Get32( ( const TUint8* )&aSrReport.iMSWTimestamp) );
LOG1( "iLSWTimestamp %u",
( unsigned)BigEndian::Get32( ( const TUint8* )&aSrReport.iLSWTimestamp ) );
LOG1( "iRTPTimestamp %u",
( unsigned )BigEndian::Get32( ( const TUint8* )&aSrReport.iRTPTimestamp ) );
LOG1( "iSenderPacketCount %u",
( unsigned )BigEndian::Get32( ( const TUint8* )&aSrReport.iSenderPacketCount) );
LOG1( "iSenderOctetCount %u",
( unsigned )BigEndian::Get32( ( const TUint8* )&aSrReport.iSenderOctetCount ) );
}
#endif // LIVE_TV_FILE_TRACE || LIVE_TV_RDEBUG_TRACE
// End of File