/* ====================================================================
 * 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 RtcpTransmitter_cxx_Version =
    "$Id: RtcpTransmitter.cxx,v 1.14 2001/08/10 04:02:15 icahoon 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>


// networking
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

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

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


const int RtcpTransmitter::RTCP_INTERVAL = 5000;


/* ----------------------------------------------------------------- */
/* --- RtcpTransmitter Constructor --------------------------------- */
/* ----------------------------------------------------------------- */

RtcpTransmitter::RtcpTransmitter (const char* remoteHost,
                                  int remoteMinPort,
                                  int remoteMaxPort,
                                  RtcpReceiver* receiver)
{
    NetworkAddress netAddress;

    if ( remoteHost )
    {
        netAddress.setPort(remoteMinPort);
        netAddress.setHostName(remoteHost);
    }

    if (receiver)
    {
        myStack = receiver->getUdpStack();
        myStack->setDestination(&netAddress);
        remoteAddr = netAddress;
        //        myStack->connectPorts();
        freeStack = false;
    }
    else
    {
        myStack = new UdpStack(&netAddress, remoteMinPort, remoteMaxPort,
                               sendonly) ;
        remoteAddr = netAddress;
        //        myStack->connectPorts();
        freeStack = true;
    }

    constructRtcpTransmitter ();
}

RtcpTransmitter::RtcpTransmitter (const char* remoteHost,
                                  int remotePort,
                                  RtcpReceiver* receiver)
{
    NetworkAddress netAddress;

    if ( remoteHost )
    {
        netAddress.setHostName(remoteHost);
        netAddress.setPort(remotePort);
    }

    if (receiver)
    {
        myStack = receiver->getUdpStack();
        myStack->setDestination(&netAddress);
        remoteAddr = netAddress;
        //        myStack->connectPorts();
        freeStack = false;
    }
    else
    {
        myStack = new UdpStack(&netAddress, remotePort, remotePort,
                               sendonly) ;
        remoteAddr = netAddress;
        //        myStack->connectPorts();
        freeStack = true;
    }

    constructRtcpTransmitter ();
}


void RtcpTransmitter::constructRtcpTransmitter ()
{
    tran = NULL;
    recv = NULL;
    rtcpRecv = NULL;
    SDESInfo = NULL;

    // prepare for rtcp timing intervals
    nextInterval = getNtpTime();
    updateInterval();
}


RtcpTransmitter::~RtcpTransmitter ()
{
    if (freeStack)
    {
        delete myStack;
        myStack = 0;
    }

    if ((tran) && (SDESInfo))
    {
        delete SDESInfo;
        SDESInfo = NULL;
    }

    tran = NULL;
    recv = NULL;
    rtcpRecv = NULL;
}

void
RtcpTransmitter::setRemoteAddr (const NetworkAddress& theAddr)
{
    remoteAddr = theAddr;
}

/* --- send packet functions --------------------------------------- */

int RtcpTransmitter::transmit (RtcpPacket* p)
{
    //    NetworkAddress* toAddress = myStack->getDestinationHost();
    // transmit packet
    //int result = sendto (socketFD, p->getPacketData(), p->getTotalUsage(),
    //             0, (struct sockaddr*) &rxAddress, sizeof(rxAddress));
    //    myStack->transmit (p->getPacketData(), p->getTotalUsage());
    myStack->transmitTo ((char*)p->getPacketData(),
                         p->getTotalUsage(),
                         &remoteAddr);
    /*
        if (toAddress)
        {
            delete toAddress;
            toAddress = NULL;
        }
    */



    // exit with sucess
    return 0;
}


void RtcpTransmitter::updateInterval ()
{
    // RTCP_INTERVAL random offset between (.5 to 1.5)
    int delayMs = RTCP_INTERVAL * (500 + rand() / (RAND_MAX / 1000)) / 1000;
    nextInterval = nextInterval + delayMs;
}


int RtcpTransmitter::checkInterval ()
{
    if (getNtpTime() > nextInterval)
    {
        // prepare for next interval
        updateInterval();
        return 1;
    }

    // time not up yet
    return 0;
}




/* --- SR/RR RTCP report packets------------------------------------ */

int RtcpTransmitter::addSR (RtcpPacket* p, int npadSize)
{
    // header
    RtcpHeader* header = reinterpret_cast < RtcpHeader* > (p->freeData());
    int usage = p->allocData (sizeof(RtcpHeader));

    header->version = RTP_VERSION;
    header->padding = (npadSize > 0) ? 1 : 0;
    header->count = 0;
    header->type = (tran) ? rtcpTypeSR : rtcpTypeRR;

    NtpTime nowNtp = getNtpTime();

    // sender information
    if (tran)
    {
        //cpLog (LOG_DEBUG_STACK, "RTCP: Making Sender Info");
        RtcpSender* senderInfo = reinterpret_cast < RtcpSender* > (p->freeData());
        usage += p->allocData (sizeof(RtcpSender));

        int diffNtp = 0;
        if (nowNtp > tran->seedNtpTime)
            diffNtp = nowNtp - tran->seedNtpTime;
        else
            if (tran->seedNtpTime > nowNtp)
                diffNtp = tran->seedNtpTime - nowNtp;

        RtpTime diffRtp = (diffNtp * tran->networkFormat_clockRate) / 1000;
        senderInfo->ssrc = htonl(tran->ssrc);
        senderInfo->ntpTimeSec = htonl(nowNtp.getSeconds());
        senderInfo->ntpTimeFrac = htonl(nowNtp.getFractional());
        senderInfo->rtpTime = htonl(tran->seedRtpTime + diffRtp);
        senderInfo->packetCount = htonl(tran->packetSent);
        senderInfo->octetCount = htonl(tran->payloadSent);
    }
    else
    {
        RtcpChunk* chunk = reinterpret_cast < RtcpChunk* > (p->freeData());
        usage += p->allocData (sizeof(RtpSrc));
        chunk->ssrc = 0 ;  /* if recv only, give src 0 for receiver for now */
    }

    // report blocks
    if ((rtcpRecv) && (rtcpRecv->getTranInfoCount() > 0))
    {
        //cpLog (LOG_DEBUG_STACK, "RTCP: Making Report Block");
        RtpTranInfo* tranInfo = NULL;
        RtpReceiver* recvInfoSpec = NULL;
        RtcpReport* reportBlock = NULL;
        for (int i = 0; i < rtcpRecv->getTranInfoCount(); i++)
        {
            tranInfo = rtcpRecv->getTranInfoList(i);
            recvInfoSpec = tranInfo->recv;

            // only receieved RTCP packets from transmitter
            if (recvInfoSpec == NULL)
                continue;

            // don't report on probation transmitters
            if (recvInfoSpec->probation < 0)
                continue;

            //cpLog (LOG_DEBUG_STACK, "RTCP:  Report block for src %d",
            //       recvInfoSpec->ssrc);
            reportBlock = reinterpret_cast < RtcpReport* > (p->freeData());
            usage += p->allocData (sizeof(RtcpReport));

            reportBlock->ssrc = htonl(recvInfoSpec->ssrc);
            reportBlock->fracLost = calcLostFrac(tranInfo);
            u_int32_t lost = (calcLostCount(tranInfo)) & 0xffffff;
            reportBlock->cumLost[2] = lost & 0xff;
            reportBlock->cumLost[1] = (lost & 0xff00) >> 8;
            reportBlock->cumLost[0] = (lost & 0xff0000) >> 16;
            reportBlock->recvCycles = htons(recvInfoSpec->recvCycles);
            reportBlock->lastSeqRecv = htons(recvInfoSpec->prevSeqRecv);

            // fracational
            // reportBlock->jitter = htonl((u_int32_t)recvInfoSpec->jitter);

            // interger
            //            if (recvInfoSpec->jitter > 0)
            reportBlock->jitter = htonl(recvInfoSpec->jitter >> 4);


            reportBlock->lastSRTimeStamp = htonl(tranInfo->lastSRTimestamp);

            // reportBlock->lastSRDelay in the unit of 1/65536 of sec ??
            // currently it is in ms
            if (tranInfo->lastSRTimestamp == 0)
                reportBlock->lastSRDelay = 0;
            else
            {
                NtpTime thenNtp = tranInfo->recvLastSRTimestamp;
                //                NtpTime thenNtp ((tranInfo->lastSRTimestamp >> 16) |
                //                                 (nowNtp.getSeconds() & 0xffff0000),
                //                                 tranInfo->lastSRTimestamp << 16);
                reportBlock->lastSRDelay = 0;
                if (nowNtp > thenNtp)
                    reportBlock->lastSRDelay = htonl(nowNtp - thenNtp);
                else
                    reportBlock->lastSRDelay = 0;
            }
            // next known transmitter
            header->count++;
        }
    }

    // profile-specific extensions
    // future: not implemented


    // padding
    if (npadSize > 0)
    {
        // future: not implemented
        assert (0);
    }

    // overall packet must ends on 32-bit count
    assert (usage % 4 == 0);

    header->length = htons((usage / 4) - 1);
    //cpLog (LOG_DEBUG_STACK, "RTCP:  SR/RR packet used %d bytes/ %d words",
    //       usage, usage/4);
    return usage;
}



u_int32_t RtcpTransmitter::calcLostFrac (RtpTranInfo* s)
{
    /* from A.3 of RFC 1889 - RTP/RTCP Standards */

    RtpReceiver* r = s->recv;

    u_int32_t expected = ((r->recvCycles + r->prevSeqRecv) - r->seedSeq + 1);
    u_int32_t expected_interval, received_interval, lost_interval;

    expected_interval = expected - s->expectedPrior;
    s->expectedPrior = expected;
    received_interval = r->packetReceived - s->receivedPrior;
    s->receivedPrior = r->packetReceived;
    lost_interval = expected_interval - received_interval;

    u_int32_t fraction;
    if (expected_interval == 0 || lost_interval <= 0) fraction = 0;
    else fraction = (lost_interval << 8) / expected_interval;

    return fraction;
}


u_int32_t RtcpTransmitter::calcLostCount (RtpTranInfo* s)
{
    /* from A.3 of RFC 1889 - RTP/RTCP Standards */

    RtpReceiver* r = s->recv;

    u_int32_t expected = ((r->recvCycles + r->prevSeqRecv) - r->seedSeq + 1);
    return expected - r->packetReceived;
}




/* --- SDES RTCP packet -------------------------------------------- */

int RtcpTransmitter::addSDES (RtcpPacket* p, RtcpSDESType item, int npadSize)
{
    if (!tran) return -1;

    RtcpSDESType list[2];
    list[0] = item;
    list[1] = rtcpSdesEnd;
    return addSDES (p, list, npadSize);
}


int RtcpTransmitter::addSDES (RtcpPacket* p, int npadSize)
{
    if (!tran) return -1;

    RtcpSDESType list[8];
    int i = 0;

    if (strlen(getSdesCname()) > 0) list[i++] = rtcpSdesCname;
    if (strlen(getSdesName()) > 0) list[i++] = rtcpSdesName;
    if (strlen(getSdesEmail()) > 0) list[i++] = rtcpSdesEmail;
    if (strlen(getSdesPhone()) > 0) list[i++] = rtcpSdesPhone;
    if (strlen(getSdesLoc ()) > 0) list[i++] = rtcpSdesLoc ;
    if (strlen(getSdesTool ()) > 0) list[i++] = rtcpSdesTool ;
    if (strlen(getSdesNote ()) > 0) list[i++] = rtcpSdesNote ;

    list[i] = rtcpSdesEnd;
    return addSDES (p, list, npadSize);
}


int RtcpTransmitter::addSDES (RtcpPacket* p, RtcpSDESType* SDESlist,
                              int npadSize)
{
    if (!tran) return -1;

    // header
    //cpLog (LOG_DEBUG_STACK, "RTCP: Making SDES packet");
    RtcpHeader* header = reinterpret_cast < RtcpHeader* > (p->freeData());
    int usage = p->allocData (sizeof(RtcpHeader));

    header->version = RTP_VERSION;
    header->padding = (npadSize > 0) ? 1 : 0;
    header->count = 1;
    header->type = rtcpTypeSDES;

    // only sends the sender's SDE

    // SDES chunk
    RtcpChunk* chunk = reinterpret_cast < RtcpChunk* > (p->freeData());
    usage += p->allocData (sizeof(RtpSrc));
    /*
    cout << "sizeof(RtcpChunk) =" << sizeof(RtcpChunk) << endl; // ?? should be 7
    */
    chunk->ssrc = htonl(tran->ssrc);

    // SDES items
    RtcpSDESItem* item = NULL;
    for (int i = 0; SDESlist[i] != rtcpSdesEnd; i++)
    {
        //cpLog (LOG_DEBUG_STACK, "RTCP:  Adding SDES %d", SDESlist[i]);
        item = reinterpret_cast < RtcpSDESItem* > (p->freeData());
        usage += p->allocData (sizeof(RtcpSDESItem) - 1);

        int len = 0;

        switch (SDESlist[i])
        {
            case rtcpSdesCname:
            strcpy(&(item->startOfText), getSdesCname());
            len = strlen(getSdesCname());
            //cpLog (LOG_DEBUG_STACK, "RTCP:  SDES Item Length: %d", len);
            //cpLog (LOG_DEBUG_STACK, "RTCP:  SDES Item Value: %s", getSdesCname());
            break;
            case rtcpSdesName:
            strcpy(&(item->startOfText), getSdesName());
            len = strlen(getSdesName());
            break;
            case rtcpSdesEmail:
            strcpy(&(item->startOfText), getSdesEmail());
            len = strlen(getSdesEmail());
            break;
            case rtcpSdesPhone:
            strcpy(&(item->startOfText), getSdesPhone());
            len = strlen(getSdesPhone());
            break;
            case rtcpSdesLoc:
            strcpy(&(item->startOfText), getSdesLoc());
            len = strlen(getSdesLoc());
            break;
            case rtcpSdesTool:
            strcpy(&(item->startOfText), getSdesTool());
            len = strlen(getSdesTool());
            break;
            case rtcpSdesNote:
            strcpy(&(item->startOfText), getSdesNote());
            len = strlen(getSdesNote());
            break;
            case rtcpSdesPriv:
            // future: not implemented
            assert (0);
            break;
            default:
            cpLog (LOG_ERR, "RtcpTransmitter:  SDES type unknown");
            assert (0);
            break;
        }
        item->type = SDESlist[i];

        // strlen removes the null that was copied
        item->length = len;
        usage += p->allocData (item->length);
    }

    // ending SDES item
    item = reinterpret_cast < RtcpSDESItem* > (p->freeData());
    usage += p->allocData (sizeof(RtcpSDESItem) - 1);
    item->type = rtcpSdesEnd;
    item->length = 0;

    // padding
    if (npadSize > 0)
    {
        // future: not implemented
        assert (0);
    }

    // end packet on 32-bit count
    if (usage % 4 != 0)
    {
        //cpLog (LOG_DEBUG_STACK, "RTCP:  SDES padded by: %d", 4-usage%4);
        usage += p->allocData (4 - usage % 4);
    }

    header->length = htons((usage / 4) - 1);
    //cpLog (LOG_DEBUG_STACK, "RTCP:  SDES packet used %d bytes/ %d words", usage, usage/4);
    return usage;
}



/* --- BYE RTCP packet --------------------------------------------- */

int RtcpTransmitter::addBYE (RtcpPacket* p, char* reason, int npadSize)
{
    if (!tran) return -1;

    RtpSrc list[1];
    list[0] = tran->getSSRC();
    return addBYE (p, list, 1, reason, npadSize);
}


int RtcpTransmitter::addBYE (RtcpPacket* p, RtpSrc* lst, int count,
                             char* reason, int npadSize)
{
    assert (count > 0);

    // header
    //cpLog (LOG_DEBUG_STACK, "RTCP: Making BYE packet");
    RtcpHeader* header = reinterpret_cast < RtcpHeader* > (p->freeData());
    int usage = p->allocData (sizeof(RtcpHeader));

    header->version = RTP_VERSION;
    header->padding = (npadSize > 0) ? 1 : 0;
    header->count = count;
    header->type = rtcpTypeBYE;

    // transmitter leaving
    RtpSrc* s = NULL;
    for (int i = 0; i < count; i++)
    {
        s = reinterpret_cast < RtpSrc* > (p->freeData());
        usage += p->allocData (sizeof(RtpSrc));
        *s = htonl(lst[i]);
        //cpLog (LOG_DEBUG_STACK, "RTCP:  SRC: %d", list[i]);
    }

    // reason - optional

    if (reason)
    {
        RtcpBye* byeReason = reinterpret_cast < RtcpBye* > (p->freeData());
        usage += p->allocData (sizeof(RtcpBye) - 1);

        byeReason->length = strlen(reason);
        strncpy (&(byeReason->startOfText), reason, byeReason->length);
        usage += p->allocData (byeReason->length);
        //cpLog (LOG_DEBUG_STACK, "RTCP:  Reason: %s", reason);
    }

    // padding

    if (npadSize > 0)
    {
        // future: not implemented
        assert (0);
    }

    // end packet on 32-bit count
    if (usage % 4 != 0)
    {
        //cpLog (LOG_DEBUG_STACK, "RTCP:  BYE padded by: %d", 4-usage%4);
        usage += p->allocData (4 - usage % 4);
    }

    header->length = htons((usage / 4) - 1);
    //cpLog (LOG_DEBUG_STACK, "RTCP:  BYE packet used %d bytes/ %d words",
    //       usage, usage/4);
    return usage;
}



/* --- APP RTCP packet --------------------------------------------- */

int RtcpTransmitter::addAPP (RtcpPacket* packet, int npadSize)
{
    // future: not implemented
    assert (0);
    return -1;
}



/* --- SDES Information -------------------------------------------- */

void RtcpTransmitter::setSdesCname ()
{
    char user[64] = "unknown_user";
    char hn[64] = "uknown_host";
    char cnameres [64 + 20 + 64];

#if 0
    uid_t uid = getuid();
    struct passwd *pw = getpwuid(uid);
    if (pw != NULL)
    {
        strncpy(user, pw->pw_name, sizeof(user));
        user[63] = (char)0;
    }
#endif  // 0
    gethostname (hn, sizeof(hn));
    sprintf (cnameres, "%s.%d@%s", user, getpid(), hn);

    assert (strlen(cnameres) < 255);
    strcpy (SDESInfo->cname, cnameres);
}


void RtcpTransmitter::setSdesName (char* text)
{
    assert (strlen(text) < 255);
    strcpy (SDESInfo->name, text);
}

void RtcpTransmitter::setSdesEmail (char* text)
{
    assert (strlen(text) < 255);
    strcpy (SDESInfo->email, text);
}

void RtcpTransmitter::setSdesPhone (char* text)
{
    assert (strlen(text) < 256);
    strcpy (SDESInfo->phone, text);
}

void RtcpTransmitter::setSdesLoc (char* text)
{
    assert (strlen(text) < 255);
    strcpy (SDESInfo->loc, text);
}

void RtcpTransmitter::setSdesTool (char* text)
{
    assert (strlen(text) < 255);
    strcpy (SDESInfo->tool, text);
}

void RtcpTransmitter::setSdesNote (char* text)
{
    assert (strlen(text) < 255);
    strcpy (SDESInfo->note, text);
}


char* RtcpTransmitter::getSdesCname ()
{
    return SDESInfo->cname;
}

char* RtcpTransmitter::getSdesName ()
{
    return SDESInfo->name;
}

char* RtcpTransmitter::getSdesEmail ()
{
    return SDESInfo->email;
}

char* RtcpTransmitter::getSdesPhone ()
{
    return SDESInfo->phone;
}

char* RtcpTransmitter::getSdesLoc ()
{
    return SDESInfo->loc;
}

char* RtcpTransmitter::getSdesTool ()
{
    return SDESInfo->tool;
}

char* RtcpTransmitter::getSdesNote ()
{
    return SDESInfo->note;
}




/* --- misc functions ---------------------------------------------- */

void RtcpTransmitter::setRTPtran (RtpTransmitter* s)
{
    if (SDESInfo == NULL)
        SDESInfo = new SDESdata;

    tran = s;
}

void RtcpTransmitter::setRTPrecv (RtpReceiver* s)
{
    recv = s;
}

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

int RtcpTransmitter::getPort ()
{
    return myStack->getTxPort();
};

int RtcpTransmitter::getSocketFD ()
{
    return myStack->getSocketFD();
};


syntax highlighted by Code2HTML, v. 0.9.1