dvrengine/CommonRecordingEngine/src/CCRRtspSink.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:20:37 +0100
branchRCL_3
changeset 48 13a33d82ad98
parent 0 822a42b6c3f1
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201029 Kit: 201035

/*
* 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