diff -r 826cea16efd9 -r 13a33d82ad98 dvrengine/CommonRecordingEngine/src/CCRRTSPPacketSource.cpp --- /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 +#include "CCRTimer.h" +#include +#include +#include +#include +#include // 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 + +/* 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( 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( 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( 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 ( 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( + 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( + 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