/* ====================================================================
 * The Vovida Software License, Version 1.0 
 * 
 * Copyright (c) 2000,2003 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 RtpTransmitter_cxx_Version =
    "$Id: RtpTransmitter.cxx,v 1.20.24.1 2003/02/04 22:20:39 bko 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>


// network socket
//#include <netinet/in.h>                // struct socketaddr_in
//#include <sys/socket.h>
//#include <netdb.h>

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

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

const int RtpTransmitter::OUT_BUFFER_SIZE = 8192;


/* ----------------------------------------------------------------- */
/* --- RtpTransmitter Constructor ---------------------------------- */
/* ----------------------------------------------------------------- */

RtpTransmitter::RtpTransmitter (const char* remoteHost,
                                int remoteMinPort, int remoteMaxPort,
                                RtpPayloadType newApiFormat,
                                RtpPayloadType newNetworkFormat,
                                RtpReceiver* receiver)
{
    NetworkAddress netAddress;

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

    if(  receiver )
    {
        myStack = receiver->getUdpStack();
        myStack->setDestination(&netAddress);
        remoteAddr = netAddress;
        // assume that RTP will always use send instead of sendto
        // so that it can only receive from/send to the same remote port
        //	myStack->connectPorts();
        freeStack = false;
    }
    else
    {
        myStack = new UdpStack (&netAddress, remoteMinPort,
                                remoteMaxPort, sendonly) ;
        remoteAddr = netAddress;
        //	myStack->connectPorts();
        freeStack = true;
    }
    constructRtpTransmitter (newApiFormat, newNetworkFormat);
}


RtpTransmitter::RtpTransmitter (const char* remoteHost, int remotePort,
                                RtpPayloadType newApiFormat,
                                RtpPayloadType newNetworkFormat,
                                RtpReceiver* 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;
    }
    constructRtpTransmitter (newApiFormat, newNetworkFormat);
}


void RtpTransmitter::constructRtpTransmitter (RtpPayloadType newApiFormat,
        RtpPayloadType newNetworkFormat)
{
    outPos = 0;
    recPos = 0;
    memset (outBuff, 0, OUT_BUFFER_SIZE);

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

    // set private variables
    ssrc = generateSRC();
    seedNtpTime = getNtpTime();
    seedRtpTime = generate32();
    prevNtpTime = seedNtpTime;
    prevRtpTime = seedRtpTime;
    prevSequence = generate32();

    // set counters
    packetSent = 0;
    payloadSent = 0;
    codecString[0] = '\0';

    cpLog(LOG_DEBUG_STACK, "Constructed ssrc = %d", ssrc);
}


RtpTransmitter::~RtpTransmitter ()
{
    if( freeStack)
    {
        delete myStack;
        myStack = NULL;
    }
    cpLog(LOG_DEBUG_STACK, "Closed ssrc = %d", ssrc);
}

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

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

RtpPacket* RtpTransmitter::createPacket (int npadSize, int csrc_count)
{
    // create packet
    RtpPacket* packet = new RtpPacket (apiFormat_payloadSize, npadSize, csrc_count);
    assert (packet);

    // load packet
    packet->setSSRC (ssrc);
    packet->setPayloadType (apiFormat);

    return packet;
}


// takes api RTP packet and send to network
// assumes payload size is already set
int RtpTransmitter::transmit(RtpPacket* packet, bool eventFlag )
{
    if( !packet )
    {
        cpLog(LOG_ERR,"Attempting to transmit a NULL rtp packet");
        return -1;
    }

    RtpPacket* p = packet;

    // convert codec
    if( p->getPayloadType() != networkFormat  &&  !eventFlag )
    {
#ifndef __sparc
        // replace p with a new packet
        p = convertRtpPacketCodec (networkFormat, packet);
        assert (packet->getSequence() == p->getSequence());
#endif
    }

    rtp_htonl(p);


    // finish packet
    if( p->getPayloadUsage() != networkFormat_payloadSize  &&  !eventFlag )
    {
        cpLog(LOG_DEBUG_STACK,"Sending mismatched packetSize(%d) to networkFormatSize(%d)",
              p->getPayloadUsage(), networkFormat_payloadSize);
    }
    if( !p->timestampSet )
        p->setRtpTime( prevRtpTime + network_pktSampleSize );
    if( !p->sequenceSet )
        p->setSequence( prevSequence + 1 );
    if( p->getPayloadUsage() < 0  ||  p->getPayloadUsage() > 1012 )
    {
        cpLog(LOG_DEBUG_STACK,"Invalid data packet size %d", p->getPayloadUsage());
        return -1;
    }

    //set marker once
    if( markerOnce )
    {
        cpLog( LOG_DEBUG_STACK,"Setting marker bit once");
        p->setMarkerFlag(1);
        markerOnce = false;
    }

    // for packet reuse
    p->timestampSet = false;
    p->sequenceSet = false;

    // transmit packet
    try
    {
        myStack->transmitTo( (char*)p->getHeader(), p->getTotalUsage(), &remoteAddr );
    }
    catch( UdpStackExceptionConectionRefused& )
    {
        cpLog (LOG_ERR,"Connection refused");
        return -1;
    }

    // update counters
    packetSent++;
    prevSequence = p->getSequence();
    if( !eventFlag )
    {
        payloadSent += p->getPayloadUsage();
        prevNtpTime = getNtpTime();
        prevRtpTime = p->getRtpTime();
    }

    // set up return value
    int result = p->getPayloadUsage();

    // delete newly created packet
    if( ( p != packet )  &&  p )
    {
        delete p; p = NULL;
    }

    // exit with success
    return result;
}


// takes rawdata, buffers it, and send network packets
int RtpTransmitter::transmitRaw (char* data, int len)
{
    assert (data);
    assert(len >= 0);

    int len1;

    char *data_buf = 0;
    char *buffer = 0;

    // convert codec
    if( apiFormat != networkFormat)
    {
        buffer = new char[1012];
        len = convertCodec(apiFormat, networkFormat, data, buffer, len);
        data_buf = buffer;
    }
    else
    {
       data_buf = data;
    }

    assert (data_buf);

    // write packet to output buffer
    if( (outPos + len) < OUT_BUFFER_SIZE)
    {
        memcpy (outBuff + outPos, data_buf, len);
        outPos += len;
    }
    else
    {
        // circular memory copy
        len1 = OUT_BUFFER_SIZE - outPos;
        memcpy (outBuff + outPos, data_buf, len1);
        memcpy (outBuff, data_buf + len1, len - len1);
        outPos = len - len1;
    }


    // check if enough data to send out packet
    int packetSize = networkFormat_payloadSize;
    if(  (outPos == 0) && (recPos == 0) )
    {
        cpLog (LOG_DEBUG_STACK, "Buffer is empty");
        return 0;
    }
    if(  ((outPos + OUT_BUFFER_SIZE - recPos) % OUT_BUFFER_SIZE) < packetSize )
    {
        cpLog (LOG_DEBUG_STACK, "Not enough data for a network packet.  Need %d",
               packetSize);
        return 0;
    }


    // send out packets from buffer
    int result = 0;

    // create packet
    RtpPacket* p = new RtpPacket (networkFormat_payloadSize);
    assert (p);
    p->setSSRC (ssrc);
    p->setPayloadType (networkFormat);
    p->setPayloadUsage (packetSize);

    //fill packets
    while ( ((outPos + OUT_BUFFER_SIZE - recPos) % OUT_BUFFER_SIZE) >= packetSize )
    {
        if(  (recPos + packetSize) < OUT_BUFFER_SIZE)
        {
            memcpy (p->getPayloadLoc(), outBuff + recPos, packetSize);
            recPos += packetSize;
        }
        else
        {
            // circular memory write
            len1 = OUT_BUFFER_SIZE - recPos;
            memcpy (p->getPayloadLoc(), outBuff + recPos, len1);
            memcpy (p->getPayloadLoc() + len1, outBuff, packetSize - len1);
            recPos = packetSize - len1;
        }
        // finish packet
        result += transmit(p);
    }
    if( p) delete p;
    p = 0;

    if( buffer != 0 )
    {
	delete [] buffer;
    }

    // exit with success
    return result;
}



void RtpTransmitter::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;
        case rtpPayloadG729:
        if( print) cpLog(LOG_DEBUG, "Setting network format to: G729 %d", no_samples);
        networkFormat_clockRate = 8000;
        break;
        default:
        cpLog(LOG_ERR, "networkFormat: codec(%d) samples(%d) packetSize(%d)",
              (int)networkFormat, 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 RtpTransmitter::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) samples(%d) packetSize(%d)",
              (int)apiFormat, 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 RtpTransmitter::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