/*****************************************************************************
* @file    adsTelEpRTP.c
*
* Contents: This file contains the code for the for creating and destroying
            rtp streams.
*
* @par
* INTEL CONFIDENTIAL
* Copyright 2009 Intel Corporation All Rights Reserved.
* 
* The source code contained or described herein and all documents related to the
* source code ("Material") are owned by Intel Corporation or its suppliers or
* licensors. Title to the Material remains with Intel Corporation or its
* suppliers and licensors. The Material may contain trade secrets and proprietary
* and confidential information of Intel Corporation and its suppliers and
* licensors, and is protected by worldwide copyright and trade secret laws and
* treaty provisions. No part of the Material may be used, copied, reproduced,
* modified, published, uploaded, posted, transmitted, distributed, or disclosed
* in any way without Intels prior express written permission.
* No license under any patent, copyright, trade secret or other intellectual
* property right is granted to or conferred upon you by disclosure or delivery
* of the Materials, either expressly, by implication, inducement, estoppel or
* otherwise. Any license under such intellectual property rights must be
* express and approved by Intel in writing.
* 
* Include any supplier copyright notices as supplier requires Intel to use.
* Include supplier trademarks or logos as supplier requires Intel to use,
* preceded by an asterisk.
* An asterisked footnote can be added as follows: 
*   *Third Party trademarks are the property of their respective owners.
* 
* Unless otherwise agreed by Intel in writing, you may not remove or alter this
* notice or any other notice embedded in Materials by Intel or Intels suppliers
* or licensors in any way.
* 
*  version: ADS.L.1.1.0-160
******************************************************************************/

/****************************************************************************/
/******************* INCLUDES ***********************************************/
/****************************************************************************/

#include "ortp/ortp.h"
#include "ortp/rtpsession.h"
#include "adsTel.h"
#include "ortp/rtp.h"
#include "adsTelRTP.h"
#include "sys/time.h"

/****************************************************************************/
/******************* DEFINES ************************************************/
/****************************************************************************/

/*RTP base port*/
#define RTP_BASE_PORT                   6000

/* number of samples in a narrowband PCM frame */
#define SAMPLES_PER_FRAME               80

/* RFC2833 Payload types */
#define REF_APP_RFC2833_EVENT_PAYLOAD   101

/* marker bit offset in payload byte */
#define RTP_MARKER_BIT_OFFSET           7

/*ORTP Scheduler mode*/
#define RTP_SCHED_MODE                  FALSE

/*ORTP blocking mode*/
#define RTP_BLOCK_MODE                  FALSE

/*ORTP jitter buffer mode*/
#define RTP_JB_MODE                     TRUE

/*ORTP jitter buffer compensation value in msec */
#define RTP_JB_COMPENSATION             40

/****************************************************************************/
/******************* GLOBAL VARIABLES ***************************************/
/****************************************************************************/

/*RTP Session IDs*/
static RtpSession *rtpSessionId[MAX_RTP_CONNECTIONS];

/*Timestamp tracking for RTP Rx*/
static unsigned int rtpRxTS[MAX_RTP_CONNECTIONS];

/*buffer to hold the current RFC2833 event*/
static mblk_t tevBuffer[MAX_RTP_CONNECTIONS];

static unsigned int event_occured[MAX_RTP_CONNECTIONS];


/****************************************************************************/
/******************* FUNCTION DECLARATIONS **********************************/
/****************************************************************************/

void recv_tev_cb(RtpSession *session, unsigned long evBuffer, long user_data);

/****************************************************************************/
/******************* FUNCTION DEFINITIONS ***********************************/
/****************************************************************************/

/*****************************************************************************
 *
 * This routine is used to initialize management structures and ORTP
 *
 *****************************************************************************/

void adsTelRtpInit(void)
{
    unsigned int i = 0;

    for (i=0; i<MAX_RTP_CONNECTIONS; i++)
    {
        rtpSessionId[i] = NULL;
        rtpRxTS[i] = 0;
        event_occured[i] = FALSE;
        memset(&tevBuffer[i], 0, sizeof(mblk_t));
    }

    ortp_init();

    ortp_scheduler_init();

    return;
}

/*****************************************************************************
 *
 * This routine is used to unitialise ORTP
 *
 *****************************************************************************/

void adsTelRtpUninit(void)
{
    ortp_exit();

    return;
}

/*****************************************************************************
 *
 * This routine is used to create an RTP session
 *
 *****************************************************************************/

int adsTelRtpSessionCreate(int localChan, char *remoteIp, int remoteChan)
{
    int localPort = localChan + RTP_BASE_PORT;
    int remotePort = remoteChan + RTP_BASE_PORT;

    /*check that the local port ID is valid*/
    if (localChan >= MAX_RTP_CONNECTIONS)
    {
        printf("\nERROR:%s: local port numbers >= %u are not supported\n",
               __FUNCTION__, localPort);
        return ADS_TEL_STATUS_FAIL;
    }

    /*Check if there is an existing RTP session for this channel*/
    if (rtpSessionId[localChan] != NULL)
    {
        printf("\nERROR:%s: A RTP Session for channel %i already exists\n",
                __FUNCTION__, localChan);
        return ADS_TEL_STATUS_FAIL;
    }

    /*reset the Timestamp for the stream*/
    rtpRxTS[localChan] = 0;

    /*reset the event flag for the stream*/
    event_occured[localChan] = FALSE;

    /*memset the event buffer buffer*/
    memset(&tevBuffer[localChan], 0, sizeof(mblk_t));

    /* Set the Telephone events payload type for ORTP to a dummy
     * value in the av(default) profile. This means that event packets
     * will pass through ORTP and be handled by ADS*/
    rtp_profile_set_payload(&av_profile,
                            REF_APP_RFC2833_EVENT_PAYLOAD,
                            &payload_type_telephone_event);

    /*create the rtp session*/
    rtpSessionId[localChan] = rtp_session_new(RTP_SESSION_SENDRECV);

    if (rtpSessionId[localChan] == NULL)
    {
        printf("\nERROR:%s: Failed to create an RTP session for channel %i\n",
               __FUNCTION__,localChan);
        return ADS_TEL_STATUS_FAIL;
    }

    /*Set the scheduling mode*/
    rtp_session_set_scheduling_mode(rtpSessionId[localChan],RTP_SCHED_MODE);

    /*Set up the stream to block*/
    rtp_session_set_blocking_mode(rtpSessionId[localChan],RTP_BLOCK_MODE);

    /*Set the local port*/
    rtp_session_set_local_addr(rtpSessionId[localChan],"0.0.0.0", localPort);

    /*Set the remote Ip Address and port*/
    rtp_session_set_remote_addr(rtpSessionId[localChan], remoteIp, remotePort);

    /*configure the jitter buffer*/
    rtp_session_enable_jitter_buffer(rtpSessionId[localChan],RTP_JB_MODE);

    /*enable adaptive jitter compensation*/
    rtp_session_enable_adaptive_jitter_compensation(rtpSessionId[localChan],
                                                    TRUE);

    /*set jitter compensation value*/
    rtp_session_set_jitter_compensation(rtpSessionId[localChan],
                                        RTP_JB_COMPENSATION);

    /*Set the connected mode - will reject any packets that do not come
      from remoteIp*/
    rtp_session_set_connected_mode(rtpSessionId[localChan],TRUE);

    /*set the payload type*/
    rtp_session_set_payload_type(rtpSessionId[localChan],
                                 PAYLOAD_AUDIO_CONTINUOUS);

    /* register for telephony events */
    rtp_session_signal_connect(rtpSessionId[localChan],
                               "telephone-event_packet",
                               (RtpCallback)recv_tev_cb,0);

    return ADS_TEL_STATUS_SUCCESS;
}

/*****************************************************************************
 *
 * This routine is used to destroy the RTP session
 *
 *****************************************************************************/

int adsTelRtpSessionDestroy(int channel)
{

    if (rtpSessionId[channel] == NULL)
    {
        return ADS_TEL_STATUS_FAIL;
    }

    /*Destroy the RTP Session*/
    rtp_session_destroy(rtpSessionId[channel]);
    rtpSessionId[channel] = NULL;

    return ADS_TEL_STATUS_SUCCESS;
}


/*****************************************************************************
 *
 * This routine is used to transmit data from ADS using RTP
 *
 *****************************************************************************/

AdsTelStatus_t ads_ip_outbound_cb(AdsTelEpHandle_t epHandle,
                                  void             *pBuffer1,
                                  void             *pBuffer2)
{
    AdsTelPacket_t   *pAudioBuffer = pBuffer1;
    mblk_t *pOrtpBuffer = NULL;
    int payloadType = 0;
    int status = 0;
    unsigned int markerBit = 0;
    unsigned int channel = 0;

    /*
     * Convert the EP handle to an RTP Port number. convertEpToRtp is
     * declared in adsTelRtp.h but must be defined in the ref app
     * specific code as the implementation is application specific
     */

     channel = convertEpToRtp(epHandle, NULL, NULL);

    /*
     * Validate the input paramters
     */

    if (channel >= MAX_RTP_CONNECTIONS)
    {
        printf("\nERROR:%s: - DSP trying to transmit on an invalid channel\n",
               __FUNCTION__);
        return ADS_TEL_STATUS_FAIL;
    }

    if (pAudioBuffer == NULL)
    {
        printf("\nERROR:%s: - NULL input parameter detected\n",
               __FUNCTION__);
        return ADS_TEL_STATUS_FAIL;
    }

    if (rtpSessionId[channel] == NULL)
    {
        printf("\nERROR:%s: - Attempt to transmit on an unactivated channel "
               " (%u)\n", __FUNCTION__, channel);
        return ADS_TEL_STATUS_FAIL;
    }

    /*Set the payload type*/
    if (pAudioBuffer->mediaType == ADS_TEL_RTP_MD_TYPE_RFC2833_EVENT)
    {
        payloadType = REF_APP_RFC2833_EVENT_PAYLOAD;
    }
    else
    {
        payloadType = pAudioBuffer->payloadType;
    }

    status = rtp_session_set_send_payload_type(rtpSessionId[channel],
                                               payloadType);
    if (status != 0)
    {
        printf("\nERROR:%s: Invalid payload type %u selected for channel %u\n",
               __FUNCTION__, payloadType, channel);
        return ADS_TEL_STATUS_FAIL;
    }

    /*Allocate an ortp buffer and copy in the data from the ADS dsp buffer*/
    pOrtpBuffer = rtp_session_create_packet(rtpSessionId[channel],
                                            RTP_FIXED_HEADER_SIZE,
                                 (uint8_t *) &(pAudioBuffer->payload),
                                            pAudioBuffer->payloadLen);

    if (pOrtpBuffer == NULL)
    {
        printf("\nERROR:%s: failed to allocate Tx buffer for channel %u\n",
               __FUNCTION__, channel);
        return ADS_TEL_STATUS_FAIL;
    }

    /*Set the marker bit*/
    markerBit = (uint8_t)pAudioBuffer->payloadType >> RTP_MARKER_BIT_OFFSET;
    rtp_set_markbit(pOrtpBuffer,markerBit);

    /*send the RTP packet*/
    status =  rtp_session_sendm_with_ts(rtpSessionId[channel],
                               pOrtpBuffer,
                               pAudioBuffer->timeStamp);

    if (status != (RTP_FIXED_HEADER_SIZE + pAudioBuffer->payloadLen))
    {
        printf("\nERROR:%s: failed to Tx buffer for channel %u\n"
               "Expected Tx Bytes: %u Actual TxBytes: %u\n", __FUNCTION__,
               channel, (sizeof(rtp_header_t)+pAudioBuffer->payloadLen),
               status);
        return ADS_TEL_STATUS_FAIL; /*ortp frees the mblk_t*/
    }

    return ADS_TEL_STATUS_SUCCESS;
}

/*****************************************************************************
 *
 * This routine is used to pass received RTP packets to ADS
 *
 *****************************************************************************/

AdsTelStatus_t ads_ip_inbound_cb(AdsTelEpHandle_t epHandle,
                                 void             *pBuffer1,
                                 void             *pBuffer2)
{
    AdsTelPacket_t   *pAudioBuffer = pBuffer1;
    AdsTelPacket_t   *pToneBuffer = pBuffer2;
    unsigned char *tempBuffer = NULL;
    mblk_t *pOrtpBuffer = NULL;
    int payloadType = 0;
    unsigned int channel = 0;
    unsigned int samplesPerPacket = 0;
    unsigned int return_event = FALSE;
    struct timeval inboundTime;

    /*
     * Convert the EP handle to an RTP Port number. convertEpToRtp is
     * declared in adsTelRtp.h but must be defined in the ref app
     * specific code as the implementation is application specific
     */

    channel = convertEpToRtp(epHandle, &samplesPerPacket, NULL);

    if (channel >= MAX_RTP_CONNECTIONS)
    {
        printf("\nERROR:%s: DSP trying to receive on an invalid channel\n",
           __FUNCTION__);
        return ADS_TEL_STATUS_FAIL;
    }

    if ((pAudioBuffer == NULL) || (pToneBuffer == NULL))
    {
        printf("\nERROR:%s: NULL input parameter detected\n",
           __FUNCTION__);
        return ADS_TEL_STATUS_FAIL;
    }

    if (rtpSessionId[channel] == NULL)
    {
        printf("\nWARNING:%s: - Attempt to receive on an unactivated"
               " channel (%u)\n",
           __FUNCTION__,
           channel);
        return ADS_TEL_STATUS_FAIL;
    }

    /*check for a RTP packet*/
    pOrtpBuffer =  rtp_session_recvm_with_ts(rtpSessionId[channel],
                                                 rtpRxTS[channel]);

    gettimeofday(&inboundTime, NULL);

    /*copy in the event buffer if a RFC2833 packet was received*/
    if (event_occured[channel] == TRUE)
    {
        /*set the payload and marker bit*/
        pToneBuffer->payloadType = (rtp_get_payload_type(&tevBuffer[channel]) |
               (rtp_get_markbit(&tevBuffer[channel])<<RTP_MARKER_BIT_OFFSET));

        /* Set the payload Length and copy in the payload to the dsp buffer.
         * No need to check size of payload as stream setup
         * has limited the max packet receivable to the max payload
         * buffer size */

        pToneBuffer->payloadLen = rtp_get_payload(&tevBuffer[channel],
                                                &tempBuffer);

        memcpy(pToneBuffer->payload,tempBuffer,pToneBuffer->payloadLen);

        /*set the flag for the return code*/
        return_event = TRUE;
        /*reset the event_occured flag*/
        event_occured[channel]  = FALSE;

        /*set the private data fields*/
        pToneBuffer->privateData1 = inboundTime.tv_sec;
        pToneBuffer->privateData2 = inboundTime.tv_usec;


    }


    /*Return if there is no audio buffer*/
    if (pOrtpBuffer == NULL)
    {
        rtpRxTS[channel] += samplesPerPacket;
        if (return_event == TRUE)
        {
            return ADS_TEL_STATUS_TONE;
        }
        return ADS_TEL_STATUS_UNDERFLOW;
    }

    rtpRxTS[channel] += samplesPerPacket;

    /*Set the payload type field*/
    payloadType = rtp_get_payload_type(pOrtpBuffer);

    /*Append the marker bit to the payload value and set the payload field*/
    payloadType = payloadType |
                  (rtp_get_markbit(pOrtpBuffer)<<RTP_MARKER_BIT_OFFSET);
    pAudioBuffer->payloadType = payloadType;

    /* Set the payload Length and copy in the payload to the dsp buffer.
     * No need to check size of payload as stream setup
     * has limited the max packet receivable to the max payload
     * buffer size */

    pAudioBuffer->payloadLen = rtp_get_payload(pOrtpBuffer,
                                            &tempBuffer);

    memcpy(pAudioBuffer->payload,tempBuffer,pAudioBuffer->payloadLen);

    /*set the private data fields*/
    pAudioBuffer->privateData1 = inboundTime.tv_sec;
    pAudioBuffer->privateData2 = inboundTime.tv_usec;

    /*free the ORTP Buffer*/
    freemsg (pOrtpBuffer);

    if (return_event == TRUE)
    {
        return ADS_TEL_STATUS_AUDIO_TONE;
    }

    return ADS_TEL_STATUS_AUDIO;
}


/****************************************************************************
 *
 * Telephpone Event Handling Code
 *
 ****************************************************************************/

void recv_tev_cb(RtpSession *session, unsigned long evBuffer, long user_data)
{
    unsigned int channel = 0;

    printf("\nINFO:%s: - Receiving telephony event",__FUNCTION__);

    /*find the corresponding channel to the session ID*/
    for (channel=0; channel<MAX_RTP_CONNECTIONS; channel++)
    {
        if (rtpSessionId[channel] == session)
        {
            /*copy the event buffer*/
            tevBuffer[channel] = *(mblk_t*)evBuffer;

            /*set the event occured flag*/
            event_occured[channel] = TRUE;

            return;
        }
    }

    return;
}










