dvrengine/CommonRecordingEngine/src/CCRRtspSink.cpp
branchRCL_3
changeset 23 13a33d82ad98
parent 0 822a42b6c3f1
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dvrengine/CommonRecordingEngine/src/CCRRtspSink.cpp	Wed Sep 01 12:20:37 2010 +0100
@@ -0,0 +1,922 @@
+/*
+* 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:    Class that instructs rtsp client about getting rtp*
+*/
+
+
+
+
+// INCLUDE FILES
+#include "CCRRtspSink.h"
+#include "CCRPacketBuffer.h"
+#include "CRRTSPCommon.h"
+#include "CCRConnection.h"
+#include "CCRStreamingSession.h"
+#include <ipvideo/CDvrSdpParser.h>
+#include <e32msgqueue.h>
+#include "videoserviceutilsLogger.h"
+
+// CONSTANTS
+const TInt KCCRRtspSinkDefaultServerPort( 20042 );
+
+_LIT( KCCRRtspSink, "Rtsp sink" );
+_LIT ( KCRLocalIPAddr, "127.0.0.1" );
+_LIT8( KBaseUrl, "rtsp://127.0.0.1/" ); 
+_LIT8( KVis0, "v=0\r\n" );
+_LIT8( KSdpOLine, "o=- 1 2 IN IP4 127.0.0.1\r\n" );
+_LIT8( KSdpSLine, "s=cre\r\n"); 
+_LIT8( KSdpCLine, "c=IN IP4 0.0.0.0\r\n"); 
+_LIT8( KSdpTLine, "t=0 0\r\n"); 
+_LIT8( KSdpBLine, "b=AS:"); 
+_LIT8( KSdpAudioMLine, "m=audio 0 RTP/AVP %d\r\n" );
+_LIT8( KSdpAudioAControlLine,
+       "a=control:rtsp://127.0.0.1/default.3gp/AudioControlAddress\r\n" );
+_LIT8( KSdpvideoMLine, "m=video 0 RTP/AVP %d\r\n" );
+_LIT8( KSdpvideoAControlLine,
+       "a=control:rtsp://127.0.0.1/default.3gp/VideoControlAddress\r\n" );
+_LIT8( KDescribeReply,
+       "RTSP/1.0 200 OK\r\nCseq: %d\r\nContent-length: %d\r\n"
+       "Content-Type: application/sdp\r\n\r\n%S" );
+_LIT8( KSetupReply, 
+       "RTSP/1.0 200 OKr\nCseq: %dr\nSession: 42\r\n"
+       "Transport: RTP/AVP;unicast;mode=play;client_port=%d-%d;"
+       "server_port=%d-%d\r\n\r\n" );
+_LIT8( KControlAddr,"VideoControlAddress" );
+_LIT8( KPlayReply,
+       "RTSP/1.0 200 OK\r\n" "Cseq: %d\r\n"
+       "RTP-Info: url=rtsp://127.0.0.1/default.3gp/VideoControlAddress"
+       ";seq=%u;rtptime=%u,url=rtsp://127.0.0.1/default.3gp/AudioControlAddress;"
+       "seq=%u;rtptime=%u\r\n"
+       "Session: 42\r\n" );
+_LIT8( KPlayReplyAudioOnly,
+       "RTSP/1.0 200 OK\r\n" "Cseq: %d\r\n"
+       "RTP-Info: url=rtsp://127.0.0.1/default.3gp/AudioControlAddress;"
+       "seq=%u;rtptime=%u\r\n"
+       "Session: 42\r\n" );
+_LIT8( KPlayReplyVideoOnly,
+       "RTSP/1.0 200 OK\r\n" "Cseq: %d\r\n"
+       "RTP-Info: url=rtsp://127.0.0.1/default.3gp/VideoControlAddress"
+       ";seq=%u;rtptime=%u\r\n"
+       "Session: 42\r\n" );
+
+_LIT8( KPauseReply, "RTSP/1.0 %d OK\r\nCseq: %d\r\nSession: 42\r\n\r\n" );
+_LIT8( KTearDownReply, "RTSP/1.0 200 OK\r\nCseq: %d\r\nSession: 42\r\n\r\n" );
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::NewL
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+//  
+CCRRtspSink* CCRRtspSink::NewL(
+    CCRConnection& aConnection,
+    RSocketServ& aSockServer,
+    CCRStreamingSession::TCRSinkId aSinkId,
+    const TInt& aLoopbackPort,
+    CCRStreamingSession& aOwningSession )
+    {
+    CCRRtspSink* self = new( ELeave ) 
+        CCRRtspSink( aConnection, aSockServer, aSinkId, aOwningSession );
+    CleanupStack::PushL( self );
+    self->ConstructL( aLoopbackPort );
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::CCRRtspSink
+// C++ default constructor can NOT contain any code, that might leave.
+// -----------------------------------------------------------------------------
+//  
+CCRRtspSink::CCRRtspSink(
+    CCRConnection& aConnection,
+    RSocketServ& aSockServer,
+    CCRStreamingSession::TCRSinkId aSinkId,
+    CCRStreamingSession& aOwningSession )
+  : CCRPacketSinkBase( aOwningSession, aSinkId ), 
+    iConnection( aConnection ),
+    iSockServer( aSockServer ),
+    iStage( ERTSPInit ),
+    iSetupReceived( 0 ),
+    iAudioSeq( KMaxTUint32 ),
+    iAudioTS( KMaxTUint32 ),
+    iVideoSeq( KMaxTUint32 ),
+    iVideoTS( KMaxTUint32 ),
+    iLowerRange( KRealZero ),
+    iUpperRange( KRealMinusOne )
+    {
+    // None
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::ConstructL
+// 2nd phase. 
+// -----------------------------------------------------------------------------
+//  
+void CCRRtspSink::ConstructL( const TInt& aLoopbackPort )
+    {
+    iReceivedData = HBufC8::NewL( 0 );
+    iRopResponse = HBufC8::NewL( 0 );
+    iSockArr[EROPControl] = CCRSock::NewL(
+         *this, EROPControl, iConnection.Connection(), iSockServer, ETrue, ETrue );
+    TInt err( iSockArr[EROPControl]->ListenPort( aLoopbackPort ) );
+    LOG2( "CCRRtspSink::ConstructL(), aLoopbackPort: %d, err: %d", aLoopbackPort, err );      
+    User::LeaveIfError( err );
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::~CCRRtspSink
+// Destructor.
+// -----------------------------------------------------------------------------
+//
+CCRRtspSink::~CCRRtspSink()
+    {    
+    LOG( "CCRRtspSink::~CCRRtspSink()" );
+
+    for ( TInt i( 0 ); i < EROPMaxSockets; i++ )
+        {
+        delete iSockArr[i]; iSockArr[i] = NULL;
+        }
+    for ( TInt i( 0 ); i < CCRRtspCommand::ERTSPCommandNOCOMMAND; i++ )
+        {
+        delete iCommands[i]; iCommands[i] = NULL; 
+        }       
+        
+    delete iSdpForRop; 
+    delete iSdpParser;
+    delete iRopResponse;
+    delete iReceivedData; 
+    }
+    
+// -----------------------------------------------------------------------------
+// CCRRtspSink::ProduceSDPForRopL
+// 
+// -----------------------------------------------------------------------------
+//      
+void CCRRtspSink::ProduceSDPForRopL() 
+    {
+    if ( !iSdpParser )
+        {
+        User::Leave( KErrNotReady ); 
+        }
+    
+    delete iSdpForRop; iSdpForRop = NULL;
+    iSdpForRop = HBufC8::NewL( KMaxName );
+        
+    iSdpForRop->Des().Zero();
+    AppendL( iSdpForRop, KVis0 ); 
+    AppendL( iSdpForRop, KSdpOLine ); 
+    AppendL( iSdpForRop, KSdpSLine ); 
+    AppendL( iSdpForRop, KSdpCLine ); 
+    AppendL( iSdpForRop, KSdpTLine );
+    if ( ( iSdpParser->AudioBitrate() + iSdpParser->VideoBitrate() ) > 0 )
+    	{
+	    AppendL( iSdpForRop, KSdpBLine ); 
+    	AppendNumL( iSdpForRop, iSdpParser->AudioBitrate() +
+    	                        iSdpParser->VideoBitrate() );
+    	AppendL( iSdpForRop, KCRNewLine );
+    	}
+    
+    RArray<TPtrC8> &sessionAttributes = iSdpParser->SessionAttributes();
+    for ( TInt i( 0 ); i < sessionAttributes.Count(); i++ )
+        {
+        AppendL( iSdpForRop, sessionAttributes[i] );
+        AppendL( iSdpForRop, KCRNewLine );
+        }
+    
+    // Check whether audio exist.
+    if ( iSdpParser->AudioControlAddr().Length() )
+        { 
+        AppendFormatL( iSdpForRop, KSdpAudioMLine, iSdpParser->MediaIdentifierAudio() );
+        if ( iSdpParser->AudioBitrate() > 0 ) 
+        	{
+	        AppendL( iSdpForRop, KSdpBLine ); 
+    	    AppendNumL( iSdpForRop, iSdpParser->AudioBitrate() );
+       	 	AppendL( iSdpForRop, KCRNewLine );
+       	 	}
+
+        AppendL( iSdpForRop, KSdpAudioAControlLine );
+        
+        RArray<TPtrC8> &audioAttributes = iSdpParser->AudioAttributes();
+        for ( TInt i( 0 ); i < audioAttributes.Count(); i++ )
+            {
+            AppendL( iSdpForRop, audioAttributes[i] );
+            AppendL( iSdpForRop, KCRNewLine );
+            }
+        }
+
+    // Check whether Video exist.
+    if ( iSdpParser->VideoControlAddr().Length() )
+        {
+        AppendFormatL( iSdpForRop, KSdpvideoMLine, iSdpParser->MediaIdentifierVideo() );
+		if ( iSdpParser->VideoBitrate() > 0 ) 
+			{             
+        	AppendL( iSdpForRop, KSdpBLine ); 
+        	AppendNumL( iSdpForRop, iSdpParser->VideoBitrate() );
+        	AppendL( iSdpForRop, KCRNewLine );
+        	}
+        	
+        AppendL( iSdpForRop, KSdpvideoAControlLine );
+        
+        RArray<TPtrC8> &videoAttributes = iSdpParser->VideoAttributes();
+        for ( TInt i( 0 ); i < videoAttributes.Count(); i++ )
+            {
+            AppendL( iSdpForRop, videoAttributes[i] );
+            AppendL( iSdpForRop, KCRNewLine );
+            }
+        }       
+    }
+        
+// -----------------------------------------------------------------------------
+// CCRRtspSink::SetSdpL
+// as a side-effect causes parsing of the sdp
+// -----------------------------------------------------------------------------
+//  
+void CCRRtspSink::SetSdpL( const TDesC8& aSdp )
+    {
+    LOG1( "CCRRtspSink::SetSdpL(), aSdp len: %d", aSdp.Length() );
+    
+    // Create SDP parser
+    delete iSdpParser; iSdpParser = NULL;
+    iSdpParser = CDvrSdpParser::NewL();
+    iSdpParser->TryParseL( aSdp, KBaseUrl );
+    ProduceSDPForRopL();
+
+    if ( iStage == ERTSPDescSent )
+        {
+        ReplyToDescribeL();
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::NewPacketAvailable
+//
+// -----------------------------------------------------------------------------
+//      
+void CCRRtspSink::NewPacketAvailable()
+    {
+    if ( iBuffer )
+        {
+        // Stream of next packet
+        MCRPacketSource::TCRPacketStreamId stream( 
+            MCRPacketSource::EStreamIdCount );
+        const TInt bookKeeping( iBuffer->GetStream( iSinkId, stream ) ); 
+        
+        // Packets in buffer.
+        if ( stream != MCRPacketSource::EStreamIdCount )
+            {
+            TCRROPSockId socket( SocketFromStream( stream ) );
+
+            // Is previous packet send ready.
+
+            if ( iSockArr[socket] && !iSockArr[socket]->IsActive() )
+                {
+                // Get packet
+                TPtr8 packet( NULL, 0 );
+                iBuffer->GetPacket( bookKeeping, packet ); 
+                
+                // Now we have the packet, send it to rop:
+                iSockArr[socket]->SendData( packet ); 
+
+    			if ( iStage == ERTSPPlaySent )
+    				{
+    				iStage = ERTSPPlaying;
+    				}
+
+                }
+            else
+                {
+                iPacketPendingInBuffer = ETrue; 
+                }
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::SetSeqAndTS
+// 
+// -----------------------------------------------------------------------------
+//      
+void CCRRtspSink::SetSeqAndTS(
+    TUint& aAudioSeq,
+    TUint& aAudioTS,
+    TUint& aVideoSeq,
+    TUint& aVideoTS )
+    {
+    LOG1( "CRE ropsink SetSeqAndTS aseq=%u ", aAudioSeq );
+    
+    iAudioSeq = aAudioSeq;
+    iAudioTS  = aAudioTS;
+    iVideoSeq = aVideoSeq;
+    iVideoTS  = aVideoTS;
+    iSeqAndTSSet = ETrue; 
+    
+    if ( iStage == ERTSPReadyToPlay )
+        {
+        TRAPD( err,ReplyToPlayL() );
+        if ( err != KErrNone ) 
+            {
+            LOG1( "CRE ropsink ReplyToPlayL L=%d", err );
+            iOwningSession.SinkStops( Id() ); 
+            }
+        }   
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::SetRange
+// 
+// -----------------------------------------------------------------------------
+//      
+void CCRRtspSink::SetRange( TReal aLower, TReal aUpper )
+    {
+    LOG2( "CRE CCRRtspSink SetRange (%f - %f)", aLower, aUpper );                     
+    iLowerRange = aLower;
+    iUpperRange = aUpper;
+    }
+    
+// -----------------------------------------------------------------------------
+// CCRRtspSink::StatusChanged
+// This is used currently for getting to know if we're in playing state or not
+// -----------------------------------------------------------------------------
+//      
+void CCRRtspSink::StatusChanged( MCRPacketSource::TCRPacketSourceState aNewState )
+    {
+    LOG2( "CCRRtspSink::StatusChanged(), iStage: %d, aNewState: %d", iStage, aNewState );    
+    
+    if ( aNewState == MCRPacketSource::ERtpStateSetupRepply )
+        {
+        if ( iStage == ERTSPDelayedSetup && iRopResponse->Length() > 0 )
+            {
+            SendControlData();
+            }
+
+        iSetupReceived++; // SETUP repply received
+        }
+    else if ( aNewState == MCRPacketSource::ERtpStatePlaying )
+        {
+        if ( iStage == ERTSPPlaySent || iStage == ERTSPReadyToPlay ) 
+            {
+            iStage = ERTSPPlaying;
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::DataReceived
+//
+// This is called when data is received from socket.
+// -----------------------------------------------------------------------------
+//
+void CCRRtspSink::DataReceived( TInt aSockId, const TDesC8 &aData ) 
+    {   
+#if defined ( LIVE_TV_FILE_TRACE ) || defined ( LIVE_TV_RDEBUG_TRACE ) 
+    // Debug output follows
+    if ( aSockId == EROPControl )
+        {
+        LOG2( "CCRRtspSink::DataReceived(), aSockId: %d, len: %d", 
+                                            aSockId, 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
+        
+    switch ( aSockId )
+        {
+        case EROPControl: // RTSP is spoken in this sock
+            {
+            TRAPD( err, HandleReceivedEROPControlL( aData ) );
+            if ( KErrNone != err )
+                {
+                LOG1( "ROPSink ProcessRtspCommandL leave %d", err );
+                iOwningSession.SinkStops( Id() ); 
+                }
+            }
+            break;
+            
+        case EROPVideoSend1:
+        case EROPVideoSend2:
+        case EROPAudioSend1:
+        case EROPAudioSend2:
+            {
+            // Those packets that rop sends to us we do not need actions
+            }
+            break; 
+        
+        default:
+            {
+            LOG1( "default: Unknown aSockId: %d", aSockId );
+            }
+            break; 
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::HandleReceivedEROPControlL
+//
+// This is called after received data from EROPControl socket.
+// -----------------------------------------------------------------------------
+//
+void CCRRtspSink::HandleReceivedEROPControlL( const TDesC8& aData )
+    {
+    AppendL( iReceivedData, aData );
+    ProcessRtspCommandL();
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::SockStatusChange
+//
+// When socket status changes to something
+// -----------------------------------------------------------------------------
+//
+void CCRRtspSink::SockStatusChange(
+    TInt aSockId,
+    CCRSock::TCRSockStatus aStatus,
+    TInt aError )
+    {
+    if ( aStatus == CCRSock::EFailed )
+        {
+        LOG3( "CCRRtspSink::SockStatusChange, id: %d, failure: %d, aError: %d",
+            aSockId, ( TInt )aStatus, aError );
+        // here do DoCleanup()
+        iOwningSession.SinkStops( Id() ); 
+        }
+
+	if ( aSockId != EROPControl )
+		{
+	    // Delete used packet from buffer if the socket was udp packet socket
+	    iBuffer->HandleBufferSize();
+
+	    // Is there more packets to send.
+	    if ( iPacketPendingInBuffer )
+	        {
+	        NewPacketAvailable(); 
+	        iPacketPendingInBuffer =
+	            ( iBuffer->PacketsCount( iSinkId ) > KErrNotFound );
+	        }           
+        }
+     else
+        {
+        LOG3( "CCRRtspSink::SockStatusChange(), aSockId: %d, aStatus: %d, aError: %d",
+            aSockId, ( TInt )aStatus, aError );
+        }
+
+#if !defined LIVE_TV_FILE_TRACE && !defined LIVE_TV_RDEBUG_TRACE
+    ( void )aError;
+#endif
+    }
+    
+// -----------------------------------------------------------------------------
+// CCRRtspSink::ProcessRtspCommandL
+//
+// Causes parsing of command
+// -----------------------------------------------------------------------------
+//
+void CCRRtspSink::ProcessRtspCommandL() 
+    {
+    LOG1( "CCRRtspSink::ProcessRtspCommandL(), iStage: %d", iStage );  
+    
+    CCRRtspCommand *command = CCRRtspCommand::NewL();
+    CleanupStack::PushL( command );
+    command->TryParseL( *iReceivedData ); 
+    delete iCommands[command->Command()]; 
+    iCommands[command->Command()] = command; 
+    CleanupStack::Pop( command ); // it is now safely in instance variable
+    ProduceRtspReplyL( command->Command() ); 
+    iReceivedData->Des().Zero();
+    }
+    
+// -----------------------------------------------------------------------------
+// CCRRtspSink::ProduceRtspReplyL
+//
+// Causes sending of reply to rop
+// -----------------------------------------------------------------------------
+//
+void CCRRtspSink::ProduceRtspReplyL( CCRRtspCommand::TCommand aLastCommand )    
+    {
+    LOG2( "CCRRtspSink::ProduceRtspReplyL(), iStage: %d, aLastCommand: %d",
+                                             iStage, aLastCommand ); 
+    
+    switch ( aLastCommand ) 
+        {
+        case CCRRtspCommand::ERTSPCommandOPTIONS:
+            ReplyToOptionsL(); 
+            break; 
+        
+        case CCRRtspCommand::ERTSPCommandDESCRIBE:
+            if ( iSdpForRop ) 
+                {
+                ReplyToDescribeL();
+                }
+            
+            iStage = ERTSPDescSent;
+            break;
+        
+        case CCRRtspCommand::ERTSPCommandSETUP:
+            ReplyToSetupL();
+            break; 
+        
+        case CCRRtspCommand::ERTSPCommandPLAY:
+            if ( iSeqAndTSSet )
+                {
+                // we've either audio or video seq set, we can  proceed with play: 
+                ReplyToPlayL();
+                iStage = ERTSPPlaySent;
+                }
+            else
+                {
+                TReal startPos( KRealZero ); 
+                TReal endPos( KRealZero );
+                iCommands[CCRRtspCommand::ERTSPCommandPLAY]->GetRange( startPos, endPos );
+                iOwningSession.PlayCommand( startPos, endPos );
+                iStage = ERTSPReadyToPlay;
+                }
+            iSetupReceived = 0;
+            break; 
+        
+        case CCRRtspCommand::ERTSPCommandPAUSE: 
+            ReplyToPauseL( iStage != ERTSPPlaying ? KErrNotReady : iOwningSession.PauseCommand() );
+            iSeqAndTSSet = EFalse;
+            break;
+        
+        case CCRRtspCommand::ERTSPCommandTEARDOWN:
+            iOwningSession.StopCommand();
+            ReplyToTearDownL();
+            break;
+        
+        default:
+            // None
+            break;
+        }   
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::ReplyToOptionsL
+//
+// Causes sending of reply to rop for options
+// -----------------------------------------------------------------------------
+//
+void CCRRtspSink::ReplyToOptionsL()
+    {
+    LOG( "CCRRtspSink::ReplyToOptionsL()" );
+
+    iRopResponse->Des().Zero();
+    AppendFormatL( iRopResponse, KCROptionsReply, 
+        iCommands[CCRRtspCommand::ERTSPCommandOPTIONS]->CSeq() );
+    SendControlData();
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::ReplyToDescribeL
+//
+// Causes sending of reply to rop for describe
+// -----------------------------------------------------------------------------
+//
+void CCRRtspSink::ReplyToDescribeL()
+    {
+    LOG( "CCRRtspSink::ReplyToDescribeL()" );
+    
+    User::LeaveIfNull( iSdpForRop );
+    iRopResponse->Des().Zero();
+    AppendFormatL( iRopResponse, KDescribeReply, 
+        iCommands[CCRRtspCommand::ERTSPCommandDESCRIBE]->CSeq(),
+        iSdpForRop->Des().Length(), &*iSdpForRop );
+    SendControlData();
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::ReplyToSetupL
+//
+// Causes sending of reply to rop for setup, either audio or video
+// -----------------------------------------------------------------------------
+//
+void CCRRtspSink::ReplyToSetupL()
+    {
+    LOG( "CCRRtspSink::ReplyToSetupL()" );
+    if ( !iSdpParser )
+        {
+        User::Leave( KErrNotReady ); 
+        }
+    
+    TPtrC8 url( NULL, 0 ); 
+    iCommands[CCRRtspCommand::ERTSPCommandSETUP]->URL( url );
+    if ( url.Find( KControlAddr) != KErrNotFound )
+        { 
+        // ROP is setting up video
+        TInt videoPort(
+            iCommands[CCRRtspCommand::ERTSPCommandSETUP]->ClientPort() );       
+        LOG1( "CCRRtspSink::ReplyToSetupL  video port  %d", videoPort );
+        iStage = ERTSPSetupVideoSent;
+        
+        // Setup sockets:           
+        iSockArr[EROPVideoSend1] = CCRSock::NewL( *this, EROPVideoSend1,
+            iConnection.Connection(), iSockServer, EFalse, ETrue );
+        User::LeaveIfError( iSockArr[EROPVideoSend1]->ConnectSock(
+            KCRLocalIPAddr, videoPort,
+            KCCRRtspSinkDefaultServerPort ) );
+
+        iSockArr[EROPVideoSend2] = CCRSock::NewL( *this, EROPVideoSend2,
+            iConnection.Connection(), iSockServer, EFalse, ETrue );
+        User::LeaveIfError( iSockArr[EROPVideoSend2]->ConnectSock(
+            KCRLocalIPAddr, videoPort + 1,
+            KCCRRtspSinkDefaultServerPort + 1 ) );
+        }
+    else
+        { 
+        // ROP is setting up audio
+        TInt audioPort( 
+            iCommands[CCRRtspCommand::ERTSPCommandSETUP]->ClientPort() );
+        LOG1( "CCRRtspSink::ReplyToSetupL audio port: %d", audioPort );    
+        iStage = ERTSPSetupAudioSent;      
+        
+        // Setup sockets:           
+        iSockArr[EROPAudioSend1] = CCRSock::NewL( *this, EROPAudioSend1,
+            iConnection.Connection(), iSockServer, EFalse, ETrue );
+        User::LeaveIfError( iSockArr[EROPAudioSend1]->ConnectSock( 
+            KCRLocalIPAddr, audioPort,
+            KCCRRtspSinkDefaultServerPort + 2 ) );
+        
+        iSockArr[EROPAudioSend2] = CCRSock::NewL( *this, EROPAudioSend2,
+            iConnection.Connection(), iSockServer, EFalse, ETrue );
+        User::LeaveIfError( iSockArr[EROPAudioSend2]->ConnectSock( 
+            KCRLocalIPAddr, audioPort + 1,
+            KCCRRtspSinkDefaultServerPort + 3 ) );
+        }
+
+    iRopResponse->Des().Zero();
+    AppendFormatL( iRopResponse, KSetupReply, 
+        iCommands[CCRRtspCommand::ERTSPCommandSETUP]->CSeq(),
+        iCommands[CCRRtspCommand::ERTSPCommandSETUP]->ClientPort(),
+        iCommands[CCRRtspCommand::ERTSPCommandSETUP]->ClientPort() + 1,
+        ( iStage == ERTSPSetupVideoSent )? KCCRRtspSinkDefaultServerPort:
+                                           KCCRRtspSinkDefaultServerPort + 2,
+        ( iStage == ERTSPSetupVideoSent )? KCCRRtspSinkDefaultServerPort + 1:
+                                           KCCRRtspSinkDefaultServerPort + 3 ); 
+    
+    // If last setup, delay player response. Otherwise Helix will get prepare completed
+    // and sends automatically PLAY command which ruins the state machine
+    if ( iSetupReceived < 2 )
+        {
+        CDvrSdpParser::TDvrPacketProvidings content( iSdpParser->SupportedContent() );
+        if ( iStage == ERTSPSetupVideoSent )
+            {
+            if ( ( iSetupReceived == 0 && content == CDvrSdpParser::EDvrVideoOnly ) ||
+                 ( iSetupReceived <= 1 && content == CDvrSdpParser::EDvrBothAudioAndVideo ) )
+                {
+                iStage = ERTSPDelayedSetup;
+                LOG( "CCRRtspSink::ReplyToSetupL(), Video SETUP repply delayed.." );
+                }
+            }
+        else
+            {
+            if ( ( iSetupReceived == 0 && content == CDvrSdpParser::EDvrAudioOnly ) ||
+                 ( iSetupReceived <= 1 && content == CDvrSdpParser::EDvrBothAudioAndVideo ) )
+                {
+                iStage = ERTSPDelayedSetup;
+                LOG( "CCRRtspSink::ReplyToSetupL(), Audio SETUP repply delayed.." );
+                }
+            }
+        }
+
+    // Repply now or later
+    if ( iStage != ERTSPDelayedSetup )
+        {
+        SendControlData();
+        }
+    }
+
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::ReplyToPlayL
+//
+// 
+// -----------------------------------------------------------------------------
+//
+void CCRRtspSink::ReplyToPlayL()
+    {
+    LOG( "CCRRtspSink::ReplyToPlayL()" );
+
+    iRopResponse->Des().Zero();
+    if ( iSdpParser->AudioControlAddr().Length() && 
+         iSdpParser->VideoControlAddr().Length() )
+        {
+        AppendFormatL( iRopResponse, KPlayReply, 
+            iCommands[CCRRtspCommand::ERTSPCommandPLAY]->CSeq(),
+            iVideoSeq, iVideoTS, iAudioSeq, iAudioTS );
+        } 
+    else if ( iSdpParser->AudioControlAddr().Length() &&
+             !iSdpParser->VideoControlAddr().Length() )
+        {
+        AppendFormatL( iRopResponse, KPlayReplyAudioOnly, 
+            iCommands[CCRRtspCommand::ERTSPCommandPLAY]->CSeq(),
+            iAudioSeq, iAudioTS );
+        }
+    else if ( !iSdpParser->AudioControlAddr().Length() && 
+               iSdpParser->VideoControlAddr().Length() )
+        {
+        AppendFormatL( iRopResponse, KPlayReplyVideoOnly,
+            iCommands[CCRRtspCommand::ERTSPCommandPLAY]->CSeq(),
+            iVideoSeq, iVideoTS );
+        } 
+    else
+        { // no audio, no video.
+        iOwningSession.SinkStops( Id() ); 
+        return; 
+        }
+        
+    if ( !( iLowerRange == KRealZero && iUpperRange == KRealMinusOne ) ) 
+        {
+		TBuf8<KMaxName> buf( KCRRangeHeader );
+        TRealFormat format( 10, 3 );
+        format.iTriLen = 0; 
+        buf.AppendNum( iLowerRange, format );
+        buf.Append( '-' ); 
+        buf.AppendNum( iUpperRange, format );
+        buf.Append( KCRNewLine );
+        AppendFormatL( iRopResponse, buf );
+        }
+    
+    AppendL( iRopResponse, KCRNewLine );
+    SendControlData();
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::ReplyToPlayL
+//
+// 
+// -----------------------------------------------------------------------------
+//
+void CCRRtspSink::ReplyToPauseL( TInt aErrorCode ) 
+    {
+    LOG1( "CCRRtspSink::ReplyToPauseL(), aErrorCode: %d", aErrorCode );
+
+    iRopResponse->Des().Zero();
+
+    switch ( aErrorCode )
+        {
+        case KErrNone:
+            AppendFormatL( iRopResponse, KPauseReply,
+                CCRRtspResponse::ERTSPRespOK,  
+                iCommands[CCRRtspCommand::ERTSPCommandPAUSE]->CSeq() );
+            iStage = ERTSPPauseSent;
+            break; 
+        
+        case KErrNotReady:
+            AppendFormatL( iRopResponse, KPauseReply,
+                CCRRtspResponse::ERTSPRespMethodNotValidInThisState,  
+                iCommands[CCRRtspCommand::ERTSPCommandPAUSE]->CSeq() );
+            break; 
+        
+        default:
+            AppendFormatL( iRopResponse, KPauseReply,
+                CCRRtspResponse::ERTSPRespMethodNotAllowed,  
+                iCommands[CCRRtspCommand::ERTSPCommandPAUSE]->CSeq() );
+            break; 
+        }
+ 
+    SendControlData();
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::ReplyToTearDownL
+//
+// 
+// -----------------------------------------------------------------------------
+//
+void CCRRtspSink::ReplyToTearDownL() 
+    {
+    LOG( "CCRRtspSink::ReplyToTearDownL()" );
+
+    iRopResponse->Des().Zero();
+    AppendFormatL( iRopResponse, KTearDownReply,
+        iCommands[CCRRtspCommand::ERTSPCommandTEARDOWN]->CSeq() );
+    SendControlData();
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::SocketFromStream
+//
+// -----------------------------------------------------------------------------
+//      
+CCRRtspSink::TCRROPSockId CCRRtspSink::SocketFromStream(
+    MCRPacketSource::TCRPacketStreamId aStreamId )
+    {
+    switch ( aStreamId ) 
+        {
+        case MCRPacketSource::EAudioStream:
+            return EROPAudioSend1;
+        
+        case MCRPacketSource::EAudioControlStream:
+            return EROPAudioSend2;
+        
+        case MCRPacketSource::EVideoStream:
+            return EROPVideoSend1;
+        
+        case MCRPacketSource::EVideoControlStream:
+            return EROPVideoSend2;
+        
+        default:
+            __ASSERT_ALWAYS( 1!=2, User::Panic( KCCRRtspSink, KErrArgument ) );
+            break;
+        }
+
+    return EROPMaxSockets; // this is never reached
+    }
+
+// -----------------------------------------------------------------------------
+// CCRRtspSink::AppendL
+//
+// -----------------------------------------------------------------------------
+//      
+void CCRRtspSink::AppendL( HBufC8*& aBuffer, const TDesC8& aStr )
+    {
+    TPtr8 ptr( aBuffer->Des() );
+    if ( ( ptr.Length() + aStr.Length() ) >= ptr.MaxLength() )
+        {
+        const TInt newLength( ptr.Length() + aStr.Length() + KMaxName );
+        aBuffer = aBuffer->ReAllocL( newLength );
+        ptr.Set( aBuffer->Des() );
+        }
+    
+    ptr.Append( aStr );
+    }
+    
+// -----------------------------------------------------------------------------
+// CCRRtspSink::AppendNumL
+//
+// -----------------------------------------------------------------------------
+//      
+void CCRRtspSink::AppendNumL( HBufC8*& aBuffer, const TInt aNum )
+    {
+    TPtr8 ptr( aBuffer->Des() );
+    if ( ( ptr.Length() + KMaxInfoName ) >= ptr.MaxLength() )
+        {
+        const TInt newLength( ptr.Length() + KMaxInfoName + KMaxName );
+        aBuffer = aBuffer->ReAllocL( newLength );
+        ptr.Set( aBuffer->Des() );
+        }
+    
+    ptr.AppendNum( aNum );
+    }
+    
+// -----------------------------------------------------------------------------
+// CCRRtspSink::AppendFormatL
+//
+// -----------------------------------------------------------------------------
+//      
+void CCRRtspSink::AppendFormatL(
+    HBufC8*& aBuffer,
+    TRefByValue<const TDesC8> aFmt, ... )
+    {
+    VA_LIST list;
+    VA_START( list, aFmt );
+    HBufC8* buf = HBufC8::NewLC( KMaxDataSize );
+    buf->Des().FormatList( aFmt, list );
+    VA_END( list );
+
+    TPtr8 ptr( aBuffer->Des() );
+    if ( ( ptr.Length() + buf->Length() ) >= ptr.MaxLength() )
+        {
+        const TInt newLength( ptr.Length() + buf->Length() + KMaxName );
+        aBuffer = aBuffer->ReAllocL( newLength );
+        ptr.Set( aBuffer->Des() );
+        }
+    
+    ptr.Append( *buf );
+    CleanupStack::PopAndDestroy( buf );
+    }
+    
+// -----------------------------------------------------------------------------
+// CCRRtspSink::SendControlData
+//
+// -----------------------------------------------------------------------------
+//
+void CCRRtspSink::SendControlData()
+    {
+    iSockArr[EROPControl]->SendData( *iRopResponse );
+    iRopResponse->Des().Zero();
+    }
+
+//  End of File