--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/dvrengine/CommonRecordingEngine/src/CCRRTSPPacketSource.cpp Wed Sep 01 12:20:37 2010 +0100
@@ -0,0 +1,2854 @@
+/*
+* 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