rtp/rtpstack/src/rtptranstream.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:27:36 +0100
branchRCL_3
changeset 44 0dcb073356a5
parent 43 b5e99d8877c7
child 33 b8a7e07b2677
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201033 Kit: 201035

/*
* Copyright (c) 2002-2003 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "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:    
*
*/




#include "rtptranstream.h"

// ---------------------------------------------------------------------------
// C++ default constructor can NOT contain any code, that
// might leave.
// ---------------------------------------------------------------------------
//
CRtpTranStream::CRtpTranStream( const TRtpPayloadType aPayloadType,
                                const TRtpId aSessionId,
                                const TRtpId aStreamId,
                                const TRtpSSRC aSSRC,
                                MRtcpObserver* aRtcpObserver,
                                const TUint32* aProfileRTPTimeRates )
    :
    CRtpStream( aStreamId,
                aSessionId,
                aProfileRTPTimeRates,
                aRtcpObserver,
                aPayloadType ),
    iFlagSentRTPPackets( EFalse ),
    iPreviousTime( 0 ),
    iPreviousRemoteSN( 0 ),
    iPrevRemoteTime( 0 ),
    iCumNumOctetsSent( 0 ),
    iCumNumOctetsReceived( 0 ),
    iCumNumOctetsSent_last( 0 ),
    iFSentRtcpReport( EFalse )
    {
    iLocalSSRC = aSSRC;
    iTimeStamp = 0;
    iSeqNumCycles = 0;

    for ( TUint i = 0; i < KSNMaxArray; i++ )
        {
        iSN_size[i] = 0;
        }
    // If the client application does not specify sequence numbers, we
    // randomize the first one.
    TTime tmp_time;
    tmp_time.HomeTime();
    TInt64 seed = tmp_time.Int64();
    iBaseSeqNum = static_cast<TRtpSequence>( TRtpUtil::Random( seed )
                                             & 0xFFFF ); // Use 16-LSB only
    iSeqNum = iBaseSeqNum;
    }

// ---------------------------------------------------------------------------
// Symbian 2nd phase constructor can leave.
// ---------------------------------------------------------------------------
//
void CRtpTranStream::ConstructL()
    {
    }


// ---------------------------------------------------------------------------
// Two-phased constructor.
// ---------------------------------------------------------------------------
//
CRtpTranStream* CRtpTranStream::NewL( const TRtpPayloadType aPayloadType,
                                      const TRtpId aSessionId,
                                      const TRtpId aTranStreamId,
                                      const TRtpSSRC aSSRC,
                                      MRtcpObserver* aRtcpObserver,
                                      const TUint32* aProfileRTPTimeRates )
    {
    CRtpTranStream* self =
        new ( ELeave ) CRtpTranStream( aPayloadType,
                                       aSessionId,
                                       aTranStreamId,
                                       aSSRC,
                                       aRtcpObserver,
                                       aProfileRTPTimeRates );
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop(); // self 
    return self;
    }

// ---------------------------------------------------------------------------
// Destructor
// ---------------------------------------------------------------------------
//
CRtpTranStream::~CRtpTranStream()
    {
    }


// ---------------------------------------------------------------------------
// TInt CRtpTranStream::ResetStreamStat()
// 
// ---------------------------------------------------------------------------
//
TInt CRtpTranStream::ResetStreamStat()
    {
    TRtcpStats rtcpStat;

    RtcpStats( rtcpStat );
    
    rtcpStat.iRtcpSenderStats.iNumPacketsSent = 0;
    rtcpStat.iRtcpSenderStats.iCumNumOctetsSent = 0;
    rtcpStat.iRtcpSenderStats.iNTPTimeStampSec = 0;    // NTP seconds
    rtcpStat.iRtcpSenderStats.iNTPTimeStampFrac = 0;   // NTPfraction
    rtcpStat.iRtcpSenderStats.iTimeStamp = 0; 

    // The receiver stats are updated when receiving RR packets, so reset
    rtcpStat.iRtcpReceiverStats.iChannelBufferSize = 0;
    rtcpStat.iRtcpReceiverStats.iRoundTripDelay = 0;
    rtcpStat.iRtcpReceiverStats.iFractionLost = 0; 
    rtcpStat.iRtcpReceiverStats.iCumNumPacketsLost = 0;
    rtcpStat.iRtcpReceiverStats.iSeqNumReceived = 0;
    rtcpStat.iRtcpReceiverStats.iSSRC = 0;

    iRtcpStats = rtcpStat;
    
    iFlagSentRTPPackets = EFalse;
    iFSentRtcpReport = EFalse;

    iCumNumOctetsSent = 0;
    iCumNumOctetsSent_last = 0;
    iPreviousTime = 0;
    iPreviousRemoteSN = 0;
    iSeqNumCycles = 0;

    for ( TUint i = 0; i < KSNMaxArray; i++ )
        {
        iSN_size[i] = 0;
        }
        
    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TInt CRtpTranStream::GetStreamStat()
// 
// ---------------------------------------------------------------------------
//
TInt CRtpTranStream::GetStreamStat( TRtpPeerStat& aStat )
    {
    TRtcpStats rtcpStat;

    RtcpStats( rtcpStat );

    aStat.iCumNumOctetsSent = rtcpStat.iRtcpSenderStats.iCumNumOctetsSent;
    aStat.iNumPacketsSent = rtcpStat.iRtcpSenderStats.iNumPacketsSent;

    aStat.iTxBandwidth = rtcpStat.iRtcpReceiverStats.iTxBandwidth;
    aStat.iArrivalJitter = rtcpStat.iRtcpReceiverStats.iArrivalJitter;
    aStat.iCumNumPacketsLost = rtcpStat.iRtcpReceiverStats.iCumNumPacketsLost;
    aStat.iFractionLost = rtcpStat.iRtcpReceiverStats.iFractionLost;
    aStat.iRoundTripDelay = rtcpStat.iRtcpReceiverStats.iRoundTripDelay;
    aStat.iRxBandwidth = rtcpStat.iRtcpReceiverStats.iBandwidth;
    aStat.iChannelBufferSize = rtcpStat.iRtcpReceiverStats.iChannelBufferSize;
    aStat.iNTPTimeStampSec = rtcpStat.iRtcpSenderStats.iNTPTimeStampSec;
    aStat.iNTPTimeStampFrac = rtcpStat.iRtcpSenderStats.iNTPTimeStampFrac;
    aStat.iTimeStamp = rtcpStat.iRtcpSenderStats.iTimeStamp;

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TInt CRtpTranStream::BuildRtpPacket()
// 
// ---------------------------------------------------------------------------
//
TInt CRtpTranStream::BuildRtpPacket( const TRtpSendHeader& aHeaderInfo,
                                     const TDesC8& aPayloadData,
                                     TRtpSequence aSeqNum,
                                     TBool aSetSeqNum,
                                     CRtpPacket* aPktSnd )
    {
    TRtpPacketStreamParam streamParam;
    TRtpPacketIOParam inParam;

    if ( FirstPkg() )
        {
        RtpStreamSyncInit( aHeaderInfo.iTimestamp );
        SetFirstPkg( EFalse );
        }

    //
    // The interpretation of the marker is defined by a profile.
    // It is used for marking significant events such as frame boundaries or
    // talkspurts in the packet stream.

    // Timestamp
    // -- random initial value
    // -- monotonically increasing 
    // -- increases by one sampling period for fixed-rate audio
    // -- increases for every sample independent of whether the block is
    //    transmitted or dropped as silent.
    // -- used for playout and inter-media synchronization.
    //

    //
    // construct RTP packet
    //

    inParam.TRTP.padding = aHeaderInfo.iPadding;

    if ( aHeaderInfo.iHeaderExtension )
        {
        inParam.TRTP.fHeaderExtension = 1;
        inParam.TRTP.extension.type = aHeaderInfo.iHeaderExtension->iType;
        inParam.TRTP.extension.length = aHeaderInfo.iHeaderExtension->iLength;
        inParam.TRTP.extension.data = aHeaderInfo.iHeaderExtension->iData;
        }
    else
        {
        inParam.TRTP.extension.data = NULL;
        }

    inParam.TRTP.marker = aHeaderInfo.iMarker; 

    inParam.TRTP.payloadData = const_cast<TUint8*>( aPayloadData.Ptr() );
    inParam.TRTP.payloadDataLen = aPayloadData.Length();

    streamParam.TRTP.payload = aHeaderInfo.iPayloadType;

    if ( aSetSeqNum )
        {
        iSeqNum = aSeqNum;
        }
    streamParam.TRTP.seqNum = iSeqNum;

    streamParam.TRTP.SSRC = iLocalSSRC;
    streamParam.TRTP.timeStamp = aHeaderInfo.iTimestamp;
    
    //
    // build and send the packet
    //
    aPktSnd->RtpPacketBuild( &streamParam, &inParam ); 

    
    RTP_DEBUG_DETAIL_DVALUE( "CRtpTranStream::BuildRtpPacket, Stream ID = ",
                  iStreamId );
    RTP_DEBUG_DETAIL_DVALUE( "CRtpTranStream::BuildRtpPacket, SSRC = ",
                  streamParam.TRTP.SSRC );
    RTP_DEBUG_DETAIL_DVALUE( "CRtpTranStream::BuildRtpPacket, Seq Num = ",
                  static_cast<TInt>( streamParam.TRTP.seqNum ) );
    RTP_DEBUG_DETAIL_DVALUE( "CRtpTranStream::BuildRtpPacket, Time Stamp = ",
                  aHeaderInfo.iTimestamp );
    
    
    // update sent octets and remote bandwidth parameters 
    IncCumNumOctetsSent( aPayloadData.Length() );

    // update stream parameters
    iTimeStamp = aHeaderInfo.iTimestamp;
    iSeqNum++;
    if ( iSeqNum == 0 )
        {
        iSeqNumCycles++;
        }

    SetSentRTPPackets( ETrue );

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// TInt CRtpTranStream::BuildRtcpBYEPacket()
// 
// ---------------------------------------------------------------------------
//
TInt CRtpTranStream::BuildRtcpBYEPacket( const TDesC8& aReason,
                                         CRtpPacket* aPktRtcpSnd )
    {
    TInt paddingSize = 0;
    TRtpPacketStreamParam streamParam;
    TRtpPacketIOParam initParam;
    TInt ret = KErrNone;

    TInt byeSize = aReason.Size();

    // build RTCP BYE packet header
    TInt sourceCount = 1; 
    TInt length = 0;

    initParam.TRTCP_HEADER.pt = ERTCP_BYE;

    if ( ( 1 + byeSize ) % 4 == 0 )
        {
        // multiples of 4 bytes, no padding is needed
        // calculate total number of 32 bit words in SDES packet
        length = ( 1 + byeSize ) / 4 + 1;   
        }
    else
        {
        // not multiples of 4 bytes, padding is needed
        paddingSize = 4 - ( 1 + byeSize ) % 4;
        length = ( 1 + byeSize ) / 4 + 2;
        }

    initParam.TRTCP_HEADER.sourceCount = sourceCount;
    initParam.TRTCP_HEADER.length = length;
    aPktRtcpSnd->SetType( ERTCP_HEADER );

    aPktRtcpSnd->RtpPacketBuild( &streamParam, &initParam );

    streamParam.TRTCP_BYE.SSRC = iLocalSSRC;

    initParam.TRTCP_BYE.reason = ( TUint8 * ) aReason.Ptr();
    initParam.TRTCP_BYE.reasonSize = byeSize;
    initParam.TRTCP_BYE.paddingSize = paddingSize;

    aPktRtcpSnd->SetType( ERTCP_BYE );
    aPktRtcpSnd->RtpPacketBuild( &streamParam, &initParam ); 
    

    RTCP_DEBUG_DETAIL_DVALUE( "SEND: Send BYE Packet TX Stream ID = ",
              iStreamId );
    RTCP_DEBUG_DETAIL_DVALUE( "SEND: Send BYE Packet SSRC = ",
              iLocalSSRC );
    RTCP_DEBUG_DETAIL( "SEND: Sending RTCP BYE message as" );
    RTCP_DEBUG_DETAIL( initParam.TRTCP_BYE.reason );

    return ret;
    }

// ---------------------------------------------------------------------------
// TInt CRtpTranStream::BuildRtcpAPPPacket()
// 
// ---------------------------------------------------------------------------
//
TInt CRtpTranStream::BuildRtcpAPPPacket( const TRtcpApp& aApp,
                                         CRtpPacket* aPktRtcpSnd )
    {
    TRtpPacketStreamParam streamParam;
    TRtpPacketIOParam initParam;

    initParam.TRTCP_HEADER.pt = ERTCP_APP;

    initParam.TRTCP_HEADER.sourceCount = aApp.iSubType;

    initParam.TRTCP_HEADER.length = aApp.iAppDataLen / 4 + 3 - 1;

    aPktRtcpSnd->SetType( ERTCP_HEADER );

    aPktRtcpSnd->RtpPacketBuild( &streamParam, &initParam );

    streamParam.TRTCP_APP.SSRC = iLocalSSRC;

    Mem::Copy( initParam.TRTCP_APP.name, aApp.iName, 4 );
    initParam.TRTCP_APP.appData = const_cast<TUint8*>( aApp.iAppData );
    initParam.TRTCP_APP.appDataLen = aApp.iAppDataLen;

    aPktRtcpSnd->SetType( ERTCP_APP );

    aPktRtcpSnd->RtpPacketBuild( &streamParam, &initParam ) ; 

    RTP_DEBUG_DETAIL_DVALUE( "SEND: Send APP Packet TX Stream ID = ", iStreamId );
    RTP_DEBUG_DETAIL_DVALUE( "SEND: Send APP Packet SSRC = " , iLocalSSRC );
    RTCP_DEBUG_DETAIL( "SEND: Sending RTCP APP packet as " );
    RTCP_DEBUG_DETAIL( "InitParam.TRTCP_APP.appData " );

    return KErrNone;
    }

// ---------------------------------------------------------------------------
// CRtpTranStream::RtpStreamSyncInit()
// For transmission stream, iSyncInfo is initialized by the
// first packet sent.
// ---------------------------------------------------------------------------
//
void CRtpTranStream::RtpStreamSyncInit( TRtpTimeStamp aInitTimeStamp )
    {
    TUint32 gtTime = TRtpUtil::GtGetTime();

    TTime time1;
    TTime time2;
    TTimeIntervalSeconds interval;
    time1.HomeTime();
    _LIT( KTime, "19700101:000000.000000" );
    time2.Set( KTime ); // microsecond from Jan. 1 1970
    time1.SecondsFrom( time2, interval );

    iSyncInfo.iNTPTimeStampSec = interval.Int() + KGetTimeOfDayToNTPOffset;


    iSyncInfo.iNTPTimeStampFrac = static_cast<TUint32>(
        ( ( ( ( TUint32 ) ( gtTime % KTenthOfmsPerSecond ) ) << 16 )
        / KTenthOfmsPerSecond ) << 16 );

    iSyncInfo.iTimeStamp = aInitTimeStamp;
    iSyncInfo.iLastUpdateLocalTime = gtTime;
    }

// ---------------------------------------------------------------------------
// CRtpTranStream::RtpStreamSyncCurrent()
// iSyncInfo : NTP timestamp of initialization as a reference
//             to update the current NTP timestamp
// aSyncInfoCurrent : Bring back current NTP Time stamp
// ---------------------------------------------------------------------------
//
void CRtpTranStream::RtpStreamSyncCurrent( TRtpTimeSync* aSyncInfoCurrent )
    {
    if ( !aSyncInfoCurrent )
        {
        return;
        }
    TUint32 gtTime = TRtpUtil::GtGetTime();
    TUint32 diffFraction = ( gtTime - iSyncInfo.iLastUpdateLocalTime )
                           % KTenthOfmsPerSecond;

    aSyncInfoCurrent->iNTPTimeStampSec = iSyncInfo.iNTPTimeStampSec +
          static_cast<TUint32>( gtTime - iSyncInfo.iLastUpdateLocalTime )
          / KTenthOfmsPerSecond;
    aSyncInfoCurrent->iNTPTimeStampFrac = ( static_cast<TUint32>(
        ( diffFraction << 16 ) / KTenthOfmsPerSecond ) << 16 );
    }

// ---------------------------------------------------------------------------
// CRtpTranStream::RtpStreamCreateRtcpReportSection()
// For transmission stream, it only generates SR packet.
// ---------------------------------------------------------------------------
//
void CRtpTranStream::RtpStreamCreateRtcpReportSection( CRtpPacket* aPkt )
    {
    TRtpPacketStreamParam streamParam;
    TRtpPacketIOParam inParam;

    TRtpTimeSync syncInfoCurrent;

    streamParam.TRTCP_SR.SSRC = iLocalSSRC;

    streamParam.TRTCP_SR.numPacketsSent = iSeqNum + ( iSeqNumCycles << 16 )
                                          - iBaseSeqNum;
    streamParam.TRTCP_SR.cumNumOctetsSent = iCumNumOctetsSent;

    iRtcpStats.iRtcpSenderStats.iNumPacketsSent =
        streamParam.TRTCP_SR.numPacketsSent;
    iRtcpStats.iRtcpSenderStats.iCumNumOctetsSent =
        streamParam.TRTCP_SR.cumNumOctetsSent;
    iRtcpStats.iRtcpSenderStats.iSSRC = streamParam.TRTCP_SR.SSRC;

    RtpStreamSyncCurrent( &syncInfoCurrent );

    inParam.TRTCP_SR.NTPTimeStampSec = syncInfoCurrent.iNTPTimeStampSec;
    inParam.TRTCP_SR.NTPTimeStampFrac = syncInfoCurrent.iNTPTimeStampFrac;
    inParam.TRTCP_SR.timeStamp = iTimeStamp;

    iRtcpStats.iRtcpSenderStats.iNTPTimeStampSec =
        inParam.TRTCP_SR.NTPTimeStampSec;
    iRtcpStats.iRtcpSenderStats.iNTPTimeStampFrac =
        inParam.TRTCP_SR.NTPTimeStampFrac;
    iRtcpStats.iRtcpSenderStats.iTimeStamp = inParam.TRTCP_SR.timeStamp;
    

    RTP_DEBUG_STAT( "----- TX: Create RTCP Report Statistics -----" );
    RTP_DEBUG_STAT_DVALUE( "TX: numPacketsSent = ",
              iRtcpStats.iRtcpSenderStats.iNumPacketsSent );
    RTP_DEBUG_STAT_DVALUE( "TX: cumNumOctetsSent = ",
              iRtcpStats.iRtcpSenderStats.iCumNumOctetsSent );
    RTP_DEBUG_STAT_DVALUE( "TX: SSRC (Tx) = ",
              iRtcpStats.iRtcpSenderStats.iSSRC );
    RTP_DEBUG_STAT_DVALUE( "TX: NTPTimeStampSec = ",
              iRtcpStats.iRtcpSenderStats.iNTPTimeStampSec );
    RTP_DEBUG_STAT_DVALUE( "TX: NTPTimeStampFrac = ",
              iRtcpStats.iRtcpSenderStats.iNTPTimeStampFrac );
    RTP_DEBUG_STAT_DVALUE( "TX: timeStamp = ",
              iRtcpStats.iRtcpSenderStats.iTimeStamp );


    // For transmission stream, it only generates SR packet.
    aPkt->SetType( ERTCP_SR );
    aPkt->RtpPacketBuild( &streamParam, &inParam );
    }


// ---------------------------------------------------------------------------
// TRtpRtcpEnum CRtpTranStream::RtpStreamProcessRtcpReportSectionL()
// For transmission stream, it only processes RR packet.
// ---------------------------------------------------------------------------
//
TRtpRtcpEnum CRtpTranStream::RtpStreamProcessRtcpReportSectionL(
                                 CRtpPacket* aPkt )
    {
    TRtpPacketStreamParam streamParam;
    TRtpPacketIOParam extractParam;
    TRtpRtcpEnum parseResult = ERTCP_NO_ERROR;
    TUint32 gtTime = TRtpUtil::GtGetTime();

    parseResult = aPkt->RtpPacketProcessL( &streamParam, &extractParam );
    if ( parseResult == ERTCP_PACKET_ERROR )
        {
        return ERTCP_PACKET_ERROR;
        }

    // For transmission stream, it only processes RR packet.
    if ( aPkt->Type() == ERTCP_RR )
        {
        TInt roundTripDelay;

        roundTripDelay = RtpStreamSyncGetRoundTripDelay(
                             extractParam.TRTCP_RR.lastSRTimeStamp,
                             extractParam.TRTCP_RR.delaySinceLSR );

        iCumNumOctetsReceived += iSN_size[streamParam.TRTCP_RR.seqNumReceived
                                     % KSNMaxArray] -
                                 iSN_size[iPreviousRemoteSN % KSNMaxArray];

        if ( iCumNumOctetsSent > iCumNumOctetsReceived )
            {
            iRtcpStats.iRtcpReceiverStats.iChannelBufferSize =
                8 * ( iCumNumOctetsSent - iCumNumOctetsReceived );
            }
        else
            {
            iRtcpStats.iRtcpReceiverStats.iChannelBufferSize = 0;
            }

        iRtcpStats.iRtcpReceiverStats.iRoundTripDelay = roundTripDelay;
        iRtcpStats.iRtcpReceiverStats.iFractionLost =
            streamParam.TRTCP_RR.fractionLost;
        iRtcpStats.iRtcpReceiverStats.iCumNumPacketsLost =
            streamParam.TRTCP_RR.cumNumPacketsLost;
        iRtcpStats.iRtcpReceiverStats.iSeqNumReceived =
            streamParam.TRTCP_RR.seqNumReceived;

        // SSRC coming from the source 
        iRtcpStats.iRtcpReceiverStats.iSSRC = streamParam.TRTCP_RR.SSRC;

        EstimateBandWidths( gtTime );

        // Remember some things for next time we receive an RR
        iPreviousRemoteSN = streamParam.TRTCP_RR.seqNumReceived;
        iPrevRemoteTime = gtTime;
        iPreviousTime = gtTime;
        iCumNumOctetsSent_last = iCumNumOctetsSent;
        
        
        RTP_DEBUG_STAT( "----- TX: Process RTCP Report Statistics -----" );
        RTP_DEBUG_STAT_DVALUE( "TX: bandwidth (Tx) = ",
                  iRtcpStats.iRtcpReceiverStats.iTxBandwidth );
        RTP_DEBUG_STAT_DVALUE( "TX: roundTripDelay = ",
                  iRtcpStats.iRtcpReceiverStats.iRoundTripDelay );
        RTP_DEBUG_STAT_DVALUE( "TX: fractionLost = ",
                  static_cast<TInt>(
                      iRtcpStats.iRtcpReceiverStats.iFractionLost ) );
        RTP_DEBUG_STAT_DVALUE( "TX: cumNumPacketsLost = ",
                  iRtcpStats.iRtcpReceiverStats.iCumNumPacketsLost );
        RTP_DEBUG_STAT_DVALUE( "TX: seqNumReceived = ",
                  iRtcpStats.iRtcpReceiverStats.iSeqNumReceived );
        RTP_DEBUG_STAT_DVALUE( "TX: SSRC (Rx) = ",
                  iRtcpStats.iRtcpReceiverStats.iSSRC );
        
        
        if ( iRtcpObserver )
            {
            iRtcpObserver->RrReceived( iStreamId,
                                       iRtcpStats.iRtcpReceiverStats.iSSRC );
            }
        }

    return parseResult;
    }

// ---------------------------------------------------------------------------
// TUint32 CRtpTranStream::EstimateBandWidths()
// Make an estimate of the Tx and Rx bandwidths.
// ---------------------------------------------------------------------------
// 
void CRtpTranStream::EstimateBandWidths( TUint32 aCurrentTime )
    {
    /* NOTE: This is just a simple estimate, which assumes the following:
     * - this is an end-to-end session
     * - the sessions and streams are set up before traffic starts
     * - the payload bitrate is constant
     * The result of this estimation should not be used in any other contexts.
     */

    // Tx bandwidth
    TReal B_A( 0 );

    // Rx bandwidth
    TReal B_B( 0 );

    // number of received packets between the current and previous RR's
    TInt N_B( 0 );

    TReal avgPacketSize( 0 );
    TUint8 fracLost_i( iRtcpStats.iRtcpReceiverStats.iFractionLost );
    TUint32 lastRecvd_i( iRtcpStats.iRtcpReceiverStats.iSeqNumReceived ); 
    TUint32 lastRecvd_k( iPreviousRemoteSN );
    TUint packetsSent( iRtcpStats.iRtcpSenderStats.iNumPacketsSent ); 

    TReal timeBetweenRRs = static_cast<TReal>( aCurrentTime - iPreviousTime )
                           / KTenthOfmsPerSecond;

    // Continue only if two RR's have been received and numbers are valid
    if ( iCumNumOctetsSent_last == 0 ||
         iPreviousRemoteSN == 0 ||
         iPrevRemoteTime == 0 ||
         iPreviousTime == 0 ||
         timeBetweenRRs <= 0 ||
         packetsSent == 0 ||
         fracLost_i == 255 /* Would result in divide by zero and B_B = 0 */ )
        {
        // If this is the first RR to be received, this returns 0,
        // otherwise this returns the result of the previous calculation
        iRtcpStats.iRtcpReceiverStats.iBandwidth = 0;
        iRtcpStats.iRtcpReceiverStats.iTxBandwidth = 0;
        return;
        }

    B_A = 8 * static_cast<TReal>( ( iCumNumOctetsSent -
              iCumNumOctetsSent_last ) ) / timeBetweenRRs;
    iRtcpStats.iRtcpReceiverStats.iTxBandwidth = static_cast<TUint32>( B_A );

    avgPacketSize = static_cast<TReal>( iCumNumOctetsSent )/ packetsSent;

    /* The fraction lost is the fractional part of a decimal value between 0 
     * and 1 (the fixed point is at the left edge of this value. The decimal
     * equivalent can be computed by dividing by 255. */
    N_B = static_cast<TInt>( 
              ( lastRecvd_i - lastRecvd_k ) * ( 1 - ( fracLost_i / 255.0 ) ) );

    B_B = 8 * ( avgPacketSize * N_B ) / timeBetweenRRs;
    
    iRtcpStats.iRtcpReceiverStats.iBandwidth = static_cast<TUint32>( B_B );
    }

// ---------------------------------------------------------------------------
// TUint32 CRtpTranStream::RtpStreamSyncGetRoundTripDelay()
// It is transmission stream's duty to calculate round trip delay.
// ---------------------------------------------------------------------------
// 
TUint32 CRtpTranStream::RtpStreamSyncGetRoundTripDelay(
                            TUint32 aLastSRTimeStamp, TUint32 aDelaySinceLSR )
    {
    TInt roundTripDelay = 0;
    TRtpTimeStamp currentNTP;
    TUint32 gtTime = TRtpUtil::GtGetTime();
    TUint32 diffFraction = ( gtTime - iSyncInfo.iLastUpdateLocalTime )
                           % KTenthOfmsPerSecond;

    if ( aLastSRTimeStamp != 0 )
        {
        // calculate current NTP in a 16+16 bit precision
        // (same format as lastSRTimeStamp)
        currentNTP = ( ( iSyncInfo.iNTPTimeStampSec +
                       ( gtTime - iSyncInfo.iLastUpdateLocalTime )
                       / KTenthOfmsPerSecond ) << 16 );    
        currentNTP += static_cast<TUint32>( ( diffFraction << 16 )
                      / KTenthOfmsPerSecond );

        roundTripDelay = currentNTP - aLastSRTimeStamp - aDelaySinceLSR;
        if ( roundTripDelay > 0 )
            {
            // 16-MSB of roundTripDelay are seconds, 16-LSB of roundTripDelay
            // is the fraction part. Transform this to a decimal value
            // representing the round trip delay in tenths of millisecons
            // ( 10^4 ). See RFC 3550 page 34 for details.
            roundTripDelay = ( static_cast<TUint32>( roundTripDelay ) >> 16 ) *
                             KTenthOfmsPerSecond +
                             ( ( ( roundTripDelay & 0xFFFF ) *
                                 KTenthOfmsPerSecond ) >> 16 );
            }
        else
            {
            // here we assume the delay is so small that we cannot get
            // an accurate reading
            roundTripDelay = 0;     
            }
        }

    return static_cast<TUint32>( roundTripDelay );
    }

//end of file