/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000 Vovida Networks, Inc.  All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * 3. The names "VOCAL", "Vovida Open Communication Application Library",
 *    and "Vovida Open Communication Application Library (VOCAL)" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact vocal@vovida.org.
 *
 * 4. Products derived from this software may not be called "VOCAL", nor
 *    may "VOCAL" appear in their name, without prior written
 *    permission of Vovida Networks, Inc.
 * 
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND
 * NON-INFRINGEMENT ARE DISCLAIMED.  IN NO EVENT SHALL VOVIDA
 * NETWORKS, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT DAMAGES
 * IN EXCESS OF $1,000, NOR FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 * 
 * ====================================================================
 * 
 * This software consists of voluntary contributions made by Vovida
 * Networks, Inc. and many individuals on behalf of Vovida Networks,
 * Inc.  For more information on Vovida Networks, Inc., please see
 * <http://www.vovida.org/>.
 *
 */

static const char* const RtpReceiver_cxx_Version =
    "$Id: RtpReceiver.cxx,v 1.39.2.1 2003/02/26 22:04:31 sprajpat Exp $";


#include "global.h"
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <time.h>
#include <sys/types.h>
#include "vtypes.h"
#include <unistd.h>
#include <string.h>

#include "cpLog.h"
#include "vsock.hxx"

#include "NetworkAddress.h"
#include "NtpTime.hxx"
#include "rtpTypes.h"
#include "rtpTools.hxx"
#include "Rtp.hxx"
#include "Rtcp.hxx"
#include "rtpCodec.h"


const int RtpReceiver::IN_BUFFER_SIZE = 24576;
const int RtpReceiver::RECV_BUF = 1012;
const int RtpReceiver::MISORDER = 6;
const int RtpReceiver::PROB_MAX = 100;


static NtpTime nowTime, pastTime;
/* ----------------------------------------------------------------- */
/* --- RtpReceiver Constructor ------------------------------------- */
/* ----------------------------------------------------------------- */

RtpReceiver::RtpReceiver (int localMinPort, int localMaxPort,
                          RtpPayloadType newApiFormat,
                          RtpPayloadType newNetworkFormat, int jitterNew)
{
    /// udp stack is a sendrecv stack
    myStack = new UdpStack (NULL, localMinPort, localMaxPort) ;
    freeStack = true;

    constructRtpReceiver (newApiFormat, newNetworkFormat, jitterNew);
}

RtpReceiver::RtpReceiver (int localPort, RtpPayloadType newApiFormat,
                          RtpPayloadType newNetworkFormat,
                          int jitterNew)
{
    /// udp stack is a sendrecv stack
    myStack = new UdpStack (NULL, localPort) ;
    freeStack = true;

    constructRtpReceiver (newApiFormat, newNetworkFormat, jitterNew);
}

RtpReceiver::RtpReceiver (UdpStack* udp, RtpPayloadType newApiFormat,
                          RtpPayloadType newNetworkFormat, int jitterNew)
{
    /// udp stack is a sendrecv stack
    myStack = udp;
    freeStack = false;

    constructRtpReceiver (newApiFormat, newNetworkFormat, jitterNew);
}


void RtpReceiver::constructRtpReceiver (RtpPayloadType newApiFormat,
                                        RtpPayloadType newNetworkFormat,
                                        int jitterNew)
{
    inPos = 0;
    playPos = 0;
    memset (inBuff, 0, IN_BUFFER_SIZE);

    // set format and baseSampleRate
    setApiFormat(newApiFormat, 160, 0, NULL, false);
    setNetworkFormat(newNetworkFormat, 160, 0, NULL, false);

    // set private variables
    jitterSeed = jitterNew;

    // no transmitter
    sourceSet = false;
    ssrc = 0;
    probationSet = false;
    srcProbation = 0;
    probation = -2;
    prevPacket = NULL;
    rtcpRecv = NULL;
    silenceCodec = 0;
    codecString[0] = '\0';

    cpLog(LOG_DEBUG_STACK, "Constructed receiver");
}


RtpReceiver::~RtpReceiver ()
{
    if (freeStack)
    {
        delete myStack;
        myStack = NULL;
    }
    rtcpRecv = NULL;

    cpLog (LOG_DEBUG_STACK, "Close receiver");
}


/* --- receive packet functions ------------------------------------ */

RtpPacket* RtpReceiver::receive ()
{
    RtpPacket* p = NULL;
    int len = 0;
    int len1 = 0;
    int silencePatched = 0;
    bool faking = 0;


    // empty network que
    NtpTime arrival (0, 0);
    while (1) // network empty or time to play return packet
    {
        p = getPacket();
        if (p == NULL) break;

        // only play packets for valid sources
        if (probation < 0)
        {
            cpLog(LOG_ERR, "****Packet from invalid source");
            delete p;
            p = NULL;
            continue;
        }

        arrival = getNtpTime();
        int packetTransit = 0;
        int delay = 0;


        rtp_ntohl(p);

        // convert codec
        RtpPayloadType lType = p->getPayloadType();
        if ((lType != rtpPayloadDTMF_RFC2833) &&
            (lType != rtpPayloadCiscoRtp) &&
             lType != apiFormat)
        {
#ifndef __sparc
            // replace p with a new packet
            RtpPacket* oldp = p;
            p = convertRtpPacketCodec (apiFormat, oldp);
            assert (oldp->getSequence() == p->getSequence());
            delete oldp;
#endif
        }

        len = p->getPayloadUsage();
        if (len <= 0 || len > 1012)
        {
            cpLog(LOG_ERR, "Got an invalid packet size");
            delete p;
            p = NULL;
            continue;
        }

/*
        // drop SID packets
        if (len < ( networkFormat_payloadSize / 3 ) )
        {
            cpLog( LOG_DEBUG_STACK, "Dropping SID packet" );
            prevSeqRecv = p->getSequence();
            delete p;
            p = NULL;
            continue;
        }
*/

        // fix frame boundry
        if (len > networkFormat_payloadSize )
        {
            int lenold = len;
            len = ( len / networkFormat_payloadSize ) * networkFormat_payloadSize;
            p->setPayloadUsage( len );
            cpLog( LOG_DEBUG_STACK, "Fixing frame boundry to %d from %d", len, lenold );
            network_pktSampleSize = (lenold / networkFormat_payloadSize) * network_pktSampleSize;
        }

        // bypass jitterBuffer
        if (jitterTime == -1)
        {
            cpLog( LOG_DEBUG_STACK, "Skipping jitter buffer" );

            // update counters
            prevSeqRecv = p->getSequence();
            prevSeqPlay = p->getSequence();

            // update packet received
            packetReceived++;
            payloadReceived += p->getPayloadUsage();

            // update jitter calculation
            packetTransit = arrival - rtp2ntp(p->getRtpTime());
            delay = packetTransit - transit;
            transit = packetTransit;
            if (delay < 0) delay = -delay;
            jitter += delay - ((jitter + 8) >> 4);

            return p;
        }


        // reordering the packets according to the seq no
        // leave gaps and copy the next packet in all the gap
        // when the late packets come copy it into the correct pos

        if (RtpSeqGreater(p->getSequence(), prevSeqRecv))
        {
            // insert packet at end, repeat filling buffer if sequence skipping
            //bool cycleEnd = false;
//            int w=0;

            while (RtpSeqGreater(p->getSequence(), prevSeqRecv))
            {
                // silence patching
//                prevPacketRtpTime += network_pktSampleSize;
                silencePatched = 0;
                faking = 0;
                while( RtpTimeGreater( p->getRtpTime() - network_pktSampleSize, prevPacketRtpTime ) && ((p->getSequence() - 1) == prevSeqRecv))
                {
                    if( cpLogGetPriority() >= LOG_DEBUG_HB )
                    {
                        cerr<<"s"<<network_pktSampleSize;
                    }
                    if( silenceCodec == 0 )
                    {
                        cpLog( LOG_DEBUG_STACK, "Patching silence" );
                        if ((p->getPayloadType() >= rtpPayloadDynMin) &&
                            (p->getPayloadType() <= rtpPayloadDynMax) &&
                            (codecString[0] != '\0'))
                        {
                        silenceCodec = findSilenceCodecString(codecString, len);
                        }
                        else
                        {
                        silenceCodec = findSilenceCodec( p->getPayloadType(), len );
                        }
                        if( silenceCodec == 0 )
                        {
                            if( len > rtpCodecInfo[ numRtpCodecInfo - 1 ].length )
                            {
                                cpLog( LOG_ERR, "Requested codec too big to fake %d", len );
                                assert( 0 );
                            }
                            cpLog( LOG_DEBUG_STACK, "Faking silence packet with 0x00" );
                            silenceCodec = (char*)&rtpCodecInfo[ numRtpCodecInfo - 1 ].silence;
                            faking = 1;
                        }
                    }
                    assert( silenceCodec );

                    if ((inPos + len) < IN_BUFFER_SIZE)
                    {   
                        memcpy (inBuff + inPos, silenceCodec, len);
                        inPos += len;
//printf("inPos S=%d\n", inPos);
                        silencePatched++;
                    }
                    else
                    {
                        // circular memory copy 
                        len1 = IN_BUFFER_SIZE - inPos;
                        memcpy (inBuff + inPos, silenceCodec, len1);
                        memcpy (inBuff, silenceCodec + len1, len - len1);
                        inPos = len - len1;
//printf("inPos S=%d\n", inPos);
                        silencePatched++;
                    }
                    prevPacketRtpTime += network_pktSampleSize;
                }
                if( prevPacketRtpTime != p->getRtpTime() - network_pktSampleSize)
                {
                    cpLog( LOG_DEBUG_STACK, "Silent patching fail to correct rtptime" );
                    cpLog( LOG_DEBUG_STACK,
                           "prevPacketRtpTime(%u), p->getRtpTime()(%u), networkPacketSize(%d)",
                           prevPacketRtpTime, p->getRtpTime(), network_pktSampleSize );
                    prevPacketRtpTime = p->getRtpTime() - network_pktSampleSize;
                }

                if ((inPos + len) < IN_BUFFER_SIZE)
                {
                    memcpy (inBuff + inPos, p->getPayloadLoc(), len);
                    inPos += len;
//printf("inPos =%d\n", inPos);
                }
                else
                {
                    // circular memory copy
                    len1 = IN_BUFFER_SIZE - inPos;
                    memcpy (inBuff + inPos, p->getPayloadLoc(), len1);
                    memcpy (inBuff, p->getPayloadLoc() + len1, len - len1);
                    inPos = len - len1;
//printf("inPos =%d\n", inPos);
                }

                // update counters
                RtpSeqNumber tSeq = prevSeqRecv;
                prevSeqRecv++;
		if(prevSeqRecv > RTP_SEQ_MOD)
		{
		    prevSeqRecv = 0;
		}
                if (prevSeqRecv < tSeq)
                {
                    cpLog(LOG_DEBUG_STACK, "Recv cycle");
                    assert(prevSeqRecv == 0);
                    recvCycles += RTP_SEQ_MOD;
                }
            }
            prevPacketRtpTime = p->getRtpTime();
            if (silencePatched > 0)
                cpLog(LOG_DEBUG_STACK, "silencePatched = %d", silencePatched);
            if (faking) 
                silenceCodec = 0;
            if (p->getSequence() != prevSeqRecv)
            {
                cpLog(LOG_DEBUG_STACK, "Unequal packet:%d stack:%d",
                      prevSeqRecv, p->getSequence());
            }
        }
        else
        {
            // insert packet in middle
            cpLog(LOG_DEBUG_STACK, "insert middle packet: %d prevSeq %d",
                  p->getSequence(), prevSeqRecv);
            RtpSeqNumber base_prevSeqRecv = prevSeqRecv;
            int inSeqRecv = 1;
            while (RtpSeqGreater(base_prevSeqRecv, p->getSequence()))
            {
                inSeqRecv++;
                base_prevSeqRecv--;
            }
            int inPosTemp = inPos - inSeqRecv * len;
            if (inPosTemp < 0) inPosTemp = IN_BUFFER_SIZE + inPosTemp;

            if ((inPosTemp + len) < IN_BUFFER_SIZE)
            {
                memcpy (inBuff + inPosTemp, p->getPayloadLoc(), len);
            }
            else
            {
                // circular memory copy
                len1 = IN_BUFFER_SIZE - inPosTemp;
                memcpy (inBuff + inPosTemp, p->getPayloadLoc(), len1);
                memcpy (inBuff, (p->getPayloadLoc()) + len1, len - len1);
            }
        }

        // update packet received
        packetReceived++;
        payloadReceived += len;

        // update jitter calculation
        packetTransit = arrival - rtp2ntp(p->getRtpTime());
        delay = packetTransit - transit;
        transit = packetTransit;
        if (delay < 0) delay = -delay;
        jitter += delay - ((jitter + 8) >> 4);

        //    fractional
        // s->jitterTime += (1./16.) * ((double)deley - s->jitterTime);
        //    integer
        //jitterTime += delay - ((jitterTime+8) >> 4);


        if (p)
        {
            delete p;
            p = NULL;
        }
    }

/*
    // check if time to play packet
    if (getNtpTime() < gotime)
    {
//        cpLog(LOG_ERR,"wait");
        //cout <<"w";
        return NULL;
    }
*/

    int packetSize = apiFormat_payloadSize;


    // Only do catchup for the beginning packets  (is this needed? - kle)
/*
    if (! doneCatchup )
    {
        if ( ( inPos > packetSize * 2 )
                && ( playPos == 0 ) )
        {
            playPos = inPos - packetSize * 2;
            cpLog(LOG_ERR, "catch up");
        }
        doneCatchup = true;
    }
*/


    // deque next packet
    if ( (inPos == 0) && (playPos == 0) )
    {
        cpLog (LOG_ERR, "Recv buffer is empty");
        receiverError = recv_bufferEmpty;
        return NULL;
    }

    if (((inPos + IN_BUFFER_SIZE - playPos) % IN_BUFFER_SIZE) < packetSize)
    {
//        cpLog (LOG_ERR,"Not enough data for a api packet size %d", packetSize);
        receiverError = recv_bufferEmpty;
        return NULL;
    }


    // create next packect
    assert (!p);
    p = new RtpPacket (packetSize);
    if ( (playPos + packetSize) < IN_BUFFER_SIZE)
    {
        memcpy (p->getPayloadLoc(), inBuff + playPos, packetSize);
        playPos += packetSize;
//printf("playPos =%d\n", playPos);
    }
    else
    {
        // circular memory copy
        len1 = IN_BUFFER_SIZE - playPos;
        memcpy (p->getPayloadLoc(), inBuff + playPos, len1);
        memcpy (p->getPayloadLoc() + len1, inBuff, packetSize - len1);
        playPos = packetSize - len1;
//printf("playPos =%d\n", playPos);
    }

    // finish packet
    p->setSSRC (ssrc);
    p->setPayloadType (apiFormat);
    p->setPayloadUsage (packetSize);
    p->setRtpTime (prevRtpTime + api_pktSampleSize);
    p->setSequence (prevSeqPlay + 1);

    if (probation > 0) probation --;
    receiverError = recv_success;

/*
    if( ( prevRtpTime + network_pktSampleSize ) != p->getRtpTime() )
    {
        network_pktSampleSize = p->getRtpTime() - prevRtpTime;
        cpLog( LOG_DEBUG_STACK, "Fixing network_pktSampelSize to %d", network_pktSampleSize );
    }
*/
//    prevRtpTime += network_pktSampleSize;
    prevRtpTime = p->getRtpTime();
    prevNtpTime = getNtpTime();
    gotime = rtp2ntp (p->getRtpTime() + api_pktSampleSize) + jitterTime;


    // update counters
    RtpSeqNumber sSeq = prevSeqPlay;
    prevSeqPlay++;
    if (prevSeqPlay < sSeq)
    {
        cpLog(LOG_DEBUG_STACK, "Play cycle");
        assert (prevSeqPlay == 0);
        playCycles += RTP_SEQ_MOD;
    }

    return p;
}



RtpPacket* RtpReceiver::getPacket ()
{
    // check for network activity
    fd_set netFD;
    FD_ZERO (&netFD);
    FD_SET (myStack->getSocketFD(), &netFD);
    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;
    int selret = select (myStack->getSocketFD() + 1, &netFD, NULL, NULL, &timeout);
    if (selret <= 0)
    {
        // select error or no network activity
        if (selret < 0) cpLog(LOG_ERR, "Select loop error");
        else 
        {
//            cpLog(LOG_ERR, "RtpReceiver receives %d pkts", packetReceived);
//            cpLog(LOG_ERR, "UdpStack receives %d pkts", myStack->getPacketsReceived());
            return NULL;
        }
    }


    // create packet
    RtpPacket* p = new RtpPacket (RECV_BUF);
    assert (p);


    // receive packet
    int len;
    len = myStack->receiveFrom ((char*)p->getHeader(), p->getPacketAlloc(), 0);
    p->setTotalUsage (len);
//    cpLog(LOG_DEBUG_STACK, "RTP get packet len = %d", len);
//    cpLog(LOG_DEBUG_STACK, "RTP seq = %d", p->getSequence());
//    cpLog(LOG_DEBUG_STACK, "RTP rtptime = %d", p->getRtpTime());

    // check packet
    if( !p->isValid() )
    {
        cpLog(LOG_ERR, "****Packet is not valid");
        delete p; p = NULL;
        return NULL;
    }


    // check if rtp event
    if (p->getPayloadType() == rtpPayloadDTMF_RFC2833 ||
            p->getPayloadType() == rtpPayloadCiscoRtp)
    {
        if(getDTMFInterface() != 0)
        {
            //If a call-back is set, let the callback handle
            //the DTMF event
            recvEvent( p );
            delete p; p = NULL;
            return NULL;
        }
        else
        {
            //Treat it as any other packet
            return p;
        }
    }

    // update receiver info
    //   function may return 1, meaning packet
    //   out of seq tolr or source not valid
    if (updateSource(p))
    {
        cpLog(LOG_ERR, "****Packet is discarded or source not valid");
        delete p; p = NULL;
        return NULL;
    }

    return p;
}


int RtpReceiver::updateSource (RtpPacket* p)
{
    // check if ssrc in probation list
    if (sourceSet && p->getSSRC() == srcProbation && probationSet)
        // old probation packets still in que
        return 1;

    // drop SID packets when SID packets are beginning packets
    if ((p->getPayloadUsage() < ( networkFormat_payloadSize / 3 )) ||
        (p->getPayloadUsage() < 20) )
    {
        cpLog( LOG_DEBUG_STACK, "Dropping SID packet" );
        prevSeqRecv = p->getSequence();
        return 1;
    }

    // new source found or resync old source
    if (!sourceSet || p->getSSRC() != ssrc)
    {
        if (addSource(p))
            return 1;
    }


    // no vaild source yet
    assert (probation >= 0);


    // check if receiving new payload format
    /*
    if (p->getPayloadType() != networkFormat ||
            p->getPayloadUsage() != networkFormat_payloadSize)
    {
        cpLog(LOG_DEBUG, "Transmitter changing payload parameters(%d/%d) (%d/%d)",
            p->getPayloadType(), networkFormat,
            p->getPayloadUsage(), networkFormat_payloadSize );
        p->printPacket();
        initSource(p);
    }
    */

    // drop CN packets
    if ((p->getPayloadType() == rtpPayloadCN) ||
        (p->getPayloadType() == 13)) {
        cpLog(LOG_DEBUG_STACK, "drop 1 rtpPayloadCN packet");
        prevSeqRecv = p->getSequence();  // drop comfort noise packet
        return 1;
    }

    if (p->getPayloadType() != networkFormat )
    {
        cpLog(LOG_DEBUG, "Transmitter changing payload parameters(%d/%d) (%d/%d)",
            p->getPayloadType(), networkFormat,
            p->getPayloadUsage(), networkFormat_payloadSize );
        p->printPacket();
        initSource(p);
    }

/*
    cpLog(LOG_DEBUG_STACK, "prevPacketRtpTime = %d", prevPacketRtpTime);
    cpLog(LOG_DEBUG_STACK, "network_pktSampleSize = %d", network_pktSampleSize);
    cpLog(LOG_DEBUG_STACK, "p->getRtpTime() = %d", p->getRtpTime());
    cpLog(LOG_DEBUG_STACK, "it is %f", (( p->getRtpTime() - prevPacketRtpTime) * 1.0) / network_pktSampleSize);
*/
    // fix rtp time stamp boundry
    if ((( prevPacketRtpTime + network_pktSampleSize ) != p->getRtpTime()) &&
        ((( p->getRtpTime() - prevPacketRtpTime) * 1.0) / network_pktSampleSize < 1.5) &&
        ((( p->getRtpTime() - prevPacketRtpTime) * 1.0) / network_pktSampleSize
> 0.5))
    {
        network_pktSampleSize = p->getRtpTime() - prevPacketRtpTime;
        cpLog( LOG_DEBUG_STACK, "Fixing network_pktSampleSize to %d", network_pktSampleSize );
    }

    // check if valid sequence window
    RtpSeqNumber seq = p->getSequence();
//    cpLog(LOG_DEBUG, "seq = %d, prevSeqRecv = %d", seq, prevSeqRecv);
    if ( RtpSeqGreater(seq, prevSeqRecv) )  // future packet
    {
        if (seq > prevSeqRecv)
        {
            if ( (seq - prevSeqRecv) > MISORDER )
            {
                // large sequence jump forward, skip over packets less then seq
                cpLog(LOG_DEBUG_STACK, "jump to %d (%d jumps)", seq, seq - prevSeqRecv);
                cpLog(LOG_DEBUG_STACK, "rtpTime %u, prevPacketRtpTime %u", p->getRtpTime(), prevPacketRtpTime);
                prevSeqRecv = seq - 1;
                prevPacketRtpTime = p->getRtpTime() - network_pktSampleSize;
            }
        }
        else if ( (seq + RTP_SEQ_MOD - prevSeqRecv) > MISORDER)
        {
            cpLog(LOG_DEBUG_STACK, "jump cycle to %d (%d jumps)", seq, seq + RTP_SEQ_MOD - prevSeqRecv );
            cpLog(LOG_DEBUG_STACK, "rtpTime %u, prevPacketRtpTime %u", p->getRtpTime(), prevPacketRtpTime);
            prevSeqRecv = seq - 1;
            prevPacketRtpTime = p->getRtpTime() - network_pktSampleSize;
        }
    }
    else if ( RtpSeqGreater(prevSeqRecv, seq) )  // past packet
    {
        // check if the pkt is too late comparing to the pkt already
        // played. Since the prevSeqPlay is irrelavant due to diff
        // api_payload Size, use inPos and playPos to calculate.
        int size = networkFormat_payloadSize;
        int backNoOfSeq = (prevSeqRecv > seq) ?
                          (prevSeqRecv - seq) : (prevSeqRecv + RTP_SEQ_MOD - seq );
        if (inPos > playPos)
        {
            if ((inPos - (backNoOfSeq + 1) * size) < playPos)
            {
                cpLog(LOG_DEBUG_STACK, "too late now: discard seq %d", seq);
                return 1;
            }
        }
        else
        {
            if ((inPos + IN_BUFFER_SIZE - (backNoOfSeq + 1) * size) < playPos)
            {
                cpLog(LOG_DEBUG_STACK, "too late now: discard seq %d", seq);
                return 1;
            }
        }
    }
    else // (seq == prevSeqRecv)  // present packet
    {
        // duplicate with previous packet
        //cpLog(LOG_DEBUG_STACK,"duplicate packet %d", seq);
        //return 1;
    }


    /*
        // check late packets
        if (RtpSeqGreater(prevSeqPlay, p->getSequence()))
        {
            // packet arrived late
            cpLog(LOG_DEBUG_STACK,"late now:%d packet:%d", prevSeqPlay, p->getSequence());
            //return 1;
        }
    */
    return 0;
}




int RtpReceiver::addSource(RtpPacket* p)
{
    // don't allow ssrc changes without removing first
    if (sourceSet)
    {
        if (probation < 4)
        {
            probation ++;
            cpLog(LOG_ERR, "Rejecting new transmitter %u, keeping %u",
                  p->getSSRC(), ssrc);
            return 1;
        }
        else removeSource(ssrc);
    }

    // check if ssrc in probation list
    if (sourceSet && p->getSSRC() == srcProbation && probationSet)
        return 1;

    sourceSet = true;
    ssrc = p->getSSRC();
    cpLog(LOG_DEBUG_STACK, "Received ssrc = %u", ssrc);
    probation = 0;
    packetReceived = 0;
    payloadReceived = 0;


    // init SDES and RTCP fields
    if (rtcpRecv) rtcpRecv->addTranInfo(ssrc, this);

    initSource (p);

    return 0;
}



void RtpReceiver::initSource (RtpPacket* p)
{
    assert (ssrc == p->getSSRC());

    cpLog(LOG_DEBUG_STACK, "InitSource %u with sequence %d and rtp time %u",
          ssrc, p->getSequence(), p->getRtpTime());
    seedSeq = p->getSequence();
    seedNtpTime = getNtpTime();
    seedRtpTime = p->getRtpTime();

    // set receiving codec
    //If codec is G711, paylaod size cannot change, if it does it is
    //an error, just drop the packet
    if((p->getPayloadType() == 0) && 
       (p->getPayloadUsage() != networkFormat_payloadSize))
    {
        cpLog(LOG_ERR, "Incorrect payload size (%d) for payload type (%d), expected (%d) ignoring packet",
                               p->getPayloadUsage(), p->getPayloadType(), networkFormat_payloadSize);
    }
    else if ((p->getPayloadType() != networkFormat) ||
        (p->getPayloadUsage() != networkFormat_payloadSize))
    {
        int networkFormat_org = networkFormat;
        int network_pktSampleSize_org = network_pktSampleSize;
        int networkFormat_payloadSize_org = networkFormat_payloadSize;
        float multiplier = (p->getPayloadUsage() * 1.0) / (networkFormat_payloadSize * 1.0);
        float new_samples = multiplier * (network_pktSampleSize * 1.0);

        setNetworkFormat( p->getPayloadType(),
                          (int) new_samples,
                          0, p);

        if (networkFormat_perSampleSize)
            network_pktSampleSize = network_pktSampleSize / networkFormat_perSampleSize;
        else
            setNetworkFormatCodec ();

        if (networkFormat_org != networkFormat)
            cpLog (LOG_DEBUG, "  networkFormat corrected to %d", networkFormat);        if (network_pktSampleSize_org != network_pktSampleSize)
            cpLog (LOG_DEBUG, "  Number of network_pktSampleSize corrected to %d", network_pktSampleSize);
        if (networkFormat_payloadSize_org != networkFormat_payloadSize)
            cpLog (LOG_DEBUG, "  Number of networkFormat_payloadSize corrected to %d", networkFormat_payloadSize);
    }

    //For dynamic payload type, setApiFormat the same as NetworkFormat
    if ((apiFormat != p->getPayloadType()) &&
        (p->getPayloadType() >= rtpPayloadDynMin) &&
        (p->getPayloadType() <= rtpPayloadDynMax))
    {
        setApiFormat( p->getPayloadType(),
                      network_pktSampleSize,
                      0, p);
    }

    inPos = 0;
    playPos = 0;

    // set timing information
    prevRtpTime = p->getRtpTime() - api_pktSampleSize;
    prevPacketRtpTime = p->getRtpTime() - network_pktSampleSize;
    prevNtpTime = rtp2ntp(p->getRtpTime()) - network_pktSampleSize * 1000 / networkFormat_clockRate;
    prevSeqRecv = p->getSequence() - 1;
    prevSeqPlay = p->getSequence() - 1;
    recvCycles = 0;
    playCycles = 0;

    transit = 0;
    jitter = 0;
    jitterTime = jitterSeed;

    // set up next gotime
    gotime = rtp2ntp (p->getRtpTime()) + jitterTime;
}





void RtpReceiver::removeSource (RtpSrc s, int flag)
{
    if (s != ssrc)
        cpLog(LOG_DEBUG_STACK, "Removing non active source: %u", s);

    // no longer listen to this source
    probationSet = true;
    srcProbation = s;

    // no transmitter
    sourceSet = false;
    ssrc = 0;
    probation = -2;

    // remove from RTCP receiver
    if (rtcpRecv && !flag) rtcpRecv->removeTranInfo (s, 1);

    cpLog (LOG_DEBUG_STACK, "Removing source: %u", s);
}



NtpTime RtpReceiver::rtp2ntp (RtpTime rtpTime)
{
    NtpTime ntptime = seedNtpTime +
                      ((rtpTime - seedRtpTime) * 1000 / apiFormat_clockRate);
    return ntptime;
}


/*
void RtpReceiver::clearBuffer()
{
    map<RtpSeqNumber, RtpPacket*>::iterator i = jitterBuffer.begin();
    while (i != jitterBuffer.end())
    {
      cout << "jitter deleting: " << (i->second) << endl;
        delete (i->second);
        jitterBuffer.erase(i);
        i = jitterBuffer.begin();
    }
    assert (jitterBuffer.empty());
}
 
 
 
void RtpReceiver::printBuffer ()
{
    map<RtpSeqNumber, RtpPacket*>::iterator i = jitterBuffer.begin();
    while (i != jitterBuffer.end())
    {
        cerr << (i->second)->getSequence() <<" ";
        i++;
    }
    cerr <<endl;
}
*/

/* --- Session state functions ------------------------------------- */
void RtpReceiver::emptyNetwork ()
{
    // set up network activit
    fd_set netFD;
    FD_ZERO (&netFD);
    FD_SET (myStack->getSocketFD(), &netFD);
    struct timeval timeout;
    timeout.tv_sec = 0;
    timeout.tv_usec = 0;

    // create empty holder packet
    RtpPacket* p = new RtpPacket (RECV_BUF);
    assert (p);

    // receive packets until no more activity
    int len;
    int selret = select (myStack->getSocketFD() + 1,
                         &netFD, NULL, NULL, &timeout);
    while (selret > 0)
    {
        len = myStack->receive (p->getPacketData(), p->getPacketAlloc());
        if (len <= 0) break;
        FD_ZERO (&netFD);
        FD_SET (myStack->getSocketFD(), &netFD);
        selret = select (myStack->getSocketFD() + 1,
                         &netFD, NULL, NULL, &timeout);
    }
    delete p; p = NULL;

    cpLog (LOG_DEBUG_STACK, "RtpReceiver: Done empty network queue");
}


/* --- Private Information for RTCP -------------------------------- */

void RtpReceiver::setRTCPrecv (RtcpReceiver* s)
{
    rtcpRecv = s;
}


void RtpReceiver::setApiFormat (RtpPayloadType newtype, int no_samples, int packetSize,
                                RtpPacket* p, bool print)
{
    apiFormat = newtype;
    api_pktSampleSize = no_samples;
    apiFormat_perSampleSize = 1;

    switch (newtype)
    {
        case rtpPayloadPCMU:
        case rtpPayloadPCMA:
        if (print) cpLog(LOG_DEBUG, "Setting api format to: PCMU %d", no_samples);
        apiFormat_clockRate = 8000;
        break;
        case rtpPayloadL16_mono:
        if (print) cpLog(LOG_DEBUG, "Setting api format to: L16 %d", no_samples);
        apiFormat_clockRate = 44100;
        apiFormat_perSampleSize = 2;
        break;
        case rtpPayloadG729:
        if (print) cpLog(LOG_DEBUG, "Setting api format to: G729 %d", no_samples);
        apiFormat_clockRate = 8000;
        break;
        default:
        cpLog(LOG_ERR, "apiFormat: codec(%d) at sampleSize(%d) packetSize(%d)",
              (int)newtype, no_samples, packetSize);
        apiFormat_clockRate = 8000;
    }
    if (p)
        apiFormat_payloadSize = p->getPayloadUsage();
    else
        apiFormat_payloadSize = api_pktSampleSize * apiFormat_perSampleSize;

    if( packetSize != 0 )
        apiFormat_payloadSize = packetSize;
}


void RtpReceiver::setNetworkFormat (RtpPayloadType newtype, int no_samples, int packetSize, 
                                    RtpPacket* p, bool print)
{
    networkFormat = newtype;
    network_pktSampleSize = no_samples;
    networkFormat_perSampleSize = 1;

    switch (newtype)
    {
        case rtpPayloadPCMU:
        case rtpPayloadPCMA:
        if (print) cpLog(LOG_DEBUG, "Setting network format to: PCMU %d", no_samples);
        networkFormat_clockRate = 8000;
        break;
        case rtpPayloadL16_mono:
        if (print) cpLog(LOG_DEBUG, "Setting network format to: L16 %d", no_samples);
        networkFormat_clockRate = 44100;
        networkFormat_perSampleSize = 2;
        break;
        default:
        cpLog(LOG_ERR, "networkFormat: codec(%d) at sampleSize(%d) packetSize(%d)",
              (int)newtype, no_samples, packetSize);
        networkFormat_clockRate = 8000;
    }
    if (p)
        networkFormat_payloadSize = p->getPayloadUsage();
    else
        networkFormat_payloadSize = network_pktSampleSize * networkFormat_perSampleSize;

    if( packetSize != 0 )
        networkFormat_payloadSize = packetSize;
}

void RtpReceiver::setCodecString(const char* codecStringInput)
{
    strncpy(codecString, codecStringInput, strlen(codecStringInput) + 1);
    cpLog(LOG_DEBUG, "set CodecString %s", codecString);
}



syntax highlighted by Code2HTML, v. 0.9.1