/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: rtcpmisc.cpp,v 1.2.36.1 2004/07/19 21:04:16 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include <stdlib.h>
#include "hxtypes.h"
#include "hxresult.h"	// for HX_RESULT
#include "hxcom.h"	// for REF() macro
#include "rtppkt.h"	// for SDESItem
#include "rtpwrap.h"	// for RTCPPacket
#include "ihxpckts.h"	// for IHXBuffer
#include "chxpckts.h"	// for CHXHeader
#include "asmrulep.h"	// for ASMRuleBook

#include "rtcpmisc.h"

#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE		
static char HX_THIS_FILE[] = __FILE__;
#endif


///////////////////////////////////////////////////////////////////////////////
//  Class to calculate RTCP interval 
//
CRTCPInterval::CRTCPInterval()
    : m_rtcp_bw(1)
    , m_avg_rtcp_size(128)
{
    /* not used */
    HX_ASSERT(FALSE);
}

CRTCPInterval::CRTCPInterval(UINT32 ulSessionBW) 
    : m_rtcp_bw(ulSessionBW * 0.05)    
    , m_avg_rtcp_size(128)    
    , m_ulLastRTCPTime(0)
    , m_ulNextRTCPTime(0)
    , m_ulLastNumMembers(1)
    , m_ulCurNumMembers(1)
    , m_ulCurNumSenders(0)
    , m_bInitial(TRUE)
    , m_bWeSent(FALSE)
{
    HX_ASSERT(0 != m_rtcp_bw);
    if (0 == m_rtcp_bw)
    {
	m_rtcp_bw = 1;
    }
}

CRTCPInterval::~CRTCPInterval()
{
    /* empty */
}

double
CRTCPInterval::GetRTCPInterval(double rtcp_bw, INT32 nMembers, INT32 nSenders)
{
    // if rtcp_bw is passed as 0.0, then just use the rtcp_bw we had.
    if (0.0 == rtcp_bw)
    {
	rtcp_bw = m_rtcp_bw;
    }
/*
* Taken from RFC 1889 Appendix A.7
    */
    
    /*
    * Minimum time between RTCP packets from this site (in seconds).
    * This time prevents the reports from `clumping' when sessions
    * are small and the law of large numbers isn't helping to smooth
    * out the traffic.  It also keeps the report interval from
    * becoming ridiculously small during transient outages like a
    * network partition.
    */
    double const RTCP_MIN_TIME = 5.0;
    /*
    * Fraction of the RTCP bandwidth to be shared among active
    * senders.  (This fraction was chosen so that in a typical
    * session with one or two active senders, the computed report
    * time would be roughly equal to the minimum report time so that
    * we don't unnecessarily slow down receiver reports.) The
    * receiver fraction must be 1 - the sender fraction.
    */
    double const RTCP_SENDER_BW_FRACTION = 0.25;
    double const RTCP_RCVR_BW_FRACTION = (1-RTCP_SENDER_BW_FRACTION);
    /*
    * Gain (smoothing constant) for the low-pass filter that
    * estimates the average RTCP packet size (see Cadzow reference).
    */
    double const RTCP_SIZE_GAIN = (1.0/16.0);
    
    double t;                   /* interval */
    double rtcp_min_time = RTCP_MIN_TIME;
//    double rtcp_bw = ((double)m_ulSessionBW * 1000. * 0.05);
    int n = nMembers + nSenders;/* no. of members for computation */
            
    /*
    * Very first call at application start-up uses half the min
    * delay for quicker notification while still allowing some time
    * before reporting for randomization and to learn about other
    * sources so the report interval will converge to the correct
    * interval more quickly.  The average RTCP size is initialized
    * to 128 octets which is conservative (it assumes everyone else
    * is generating SRs instead of RRs: 20 IP + 8 UDP + 52 SR + 48
    * SDES CNAME).
    */
    if (m_bInitial) 
    {
	rtcp_min_time /= 2;
//	m_avg_rtcp_size = 128;
    }
    
    /*
    * If there were active senders, give them at least a minimum
    * share of the RTCP bandwidth.  Otherwise all participants share
    * the RTCP bandwidth equally.
    */
    if (nSenders > 0 && 
        nSenders < n * RTCP_SENDER_BW_FRACTION) 
    {
	rtcp_bw *= RTCP_RCVR_BW_FRACTION;
	n -= nSenders;
    }
    
    /*
    * Update the average size estimate by the size of the report
    * packet we just sent.
    */
//    m_avg_rtcp_size += (cbLastPacket - m_avg_rtcp_size)*RTCP_SIZE_GAIN;
    
    /*
    * The effective number of sites times the average packet size is
    * the total number of octets sent when each site sends a report.
    * Dividing this by the effective bandwidth gives the time
    * interval over which those packets must be sent in order to
    * meet the bandwidth target, with a minimum enforced.  In that
    * time interval we send one report so this time is also our
    * average time between reports.
    */
    t = m_avg_rtcp_size * n / rtcp_bw;
    if (t < rtcp_min_time) 
	t = rtcp_min_time;
    
	/*
        * To avoid traffic bursts from unintended synchronization with
        * other sites, we then pick our actual next report interval as a
        * random number uniformly distributed between 0.5*t and 1.5*t.
    */
    return t * (((double)rand() / (double)RAND_MAX) + 0.5);
}



///////////////////////////////////////////////////////////////////////////////
//  Class for SDES
//
CSDES::CSDES()
  : m_pCName(NULL)
  , m_pName(NULL)
  , m_pTool(NULL)
  , m_pEmail(NULL)
{
}

CSDES::CSDES(REF(IHXBuffer*)pCName, 
    	      REF(IHXBuffer*)pName, 
    	      REF(IHXBuffer*)pTool, 
    	      REF(IHXBuffer*)pEmail)
  : m_pCName(pCName)
  , m_pName(pName)
  , m_pTool(pTool)
  , m_pEmail(pEmail)
{
    if (m_pCName)m_pCName->AddRef();
    if (m_pName) m_pName->AddRef();
    if (m_pTool) m_pTool->AddRef();
    if (m_pEmail) m_pEmail->AddRef();
}

CSDES::~CSDES()
{
    HX_RELEASE(m_pCName);
    HX_RELEASE(m_pName);
    HX_RELEASE(m_pTool);
    HX_RELEASE(m_pEmail); 
}

HX_RESULT
CSDES::MakeSDES(REF(RTCPPacket) pktSDES, UINT32 ulSrcID)
{
    pktSDES.version_flag = 0x02;
    pktSDES.padding_flag = 0;
    pktSDES.count = 1;
    pktSDES.packet_type = RTCP_SDES;
    pktSDES.rr_ssrc = ulSrcID;
    
    UINT16	    cbItems = 0;
    if (m_pCName != NULL)
    {
	SDESItem item;
	item.sdes_type = SDES_CNAME;
	item.length = m_pCName->GetSize() <= UCHAR_MAX ? (UINT8)m_pCName->GetSize() -1: UCHAR_MAX;
	item.data = m_pCName->GetBuffer();
	pktSDES.AddSDESItem(ulSrcID, item);
	cbItems += item.length + 2;
    }
    else
    {
	HX_ASSERT(FALSE && "Need CNAME to send reception report");
	return HXR_FAIL;
    }
    
    if (m_pName != NULL)
    {
	SDESItem item;
	item.sdes_type = SDES_NAME;
	item.length = m_pName->GetSize() <= UCHAR_MAX ? (UINT8)m_pName->GetSize() -1: UCHAR_MAX;
	item.data = m_pName->GetBuffer();
	pktSDES.AddSDESItem(ulSrcID, item);
	cbItems += item.length + 2;
    }
    
    if (m_pTool != NULL)
    {
	SDESItem item;
	item.sdes_type = SDES_TOOL;
	item.length = m_pTool->GetSize() <= UCHAR_MAX ? (UINT8)m_pTool->GetSize() -1: UCHAR_MAX;
	item.data = m_pTool->GetBuffer();
	pktSDES.AddSDESItem(ulSrcID, item);
	cbItems += item.length + 2;
    }
    
    if (m_pEmail != NULL)
    {
	SDESItem item;
	item.sdes_type = SDES_EMAIL;
	item.length = m_pEmail->GetSize() <= UCHAR_MAX ? (UINT8)m_pEmail->GetSize() -1: UCHAR_MAX;
	item.data = m_pEmail->GetBuffer();
	pktSDES.AddSDESItem(ulSrcID, item);
	cbItems += item.length + 2;
    }
    
    /*
    * Increment item byte count for null termination
    */
    cbItems++;
    
    // Align on word boundary
    // RTCP pkt length is in 32-bits word!
    cbItems += (cbItems % 4) ? 4 - (cbItems % 4) : 0;

    HX_ASSERT(cbItems % 4 == 0);
    // I am counting 32-bits word
    pktSDES.length = (cbItems / 4);	    // count of words - 1

    //one more 32-bits for SSRC
    pktSDES.length++;
    return HXR_OK;

}



/*
 *  Functions
 */
HX_RESULT
GetThresholdInfo(REF(ASMRuleBook) rules, 
		 float* pThreshold, 
		 REF(UINT32) ulNumThreshold)
{
    HX_ASSERT(NULL != pThreshold);


    IHXValues* pValues = new CHXHeader();
    IHXBuffer* pBuffer  = new CHXBuffer();
    pValues->AddRef();
    pBuffer->AddRef();
    


    // we have to send "Bandwidth" to be 0 in pValues
    UINT8 pBandwidth[128];
    sprintf ((char *)pBandwidth, "%ld", 0); /* Flawfinder: ignore */
    
    pBuffer->Set(pBandwidth, strlen((char *)pBandwidth) + 1);
    pValues->SetPropertyCString("Bandwidth",  pBuffer);

    rules.GetPreEvaluate(pThreshold, ulNumThreshold,
	    pValues, "Bandwidth");

    pValues->Release();
    pBuffer->Release();


    return HXR_OK;
}
 
