/*=========================================================================*\
  Copyright (c) 2000-2001 Computer & Communications Research Laboratories,
                          Industrial Technology Research Institute
  
  RTP - RTP implementation
  
  Author: Jiun-Yao Huang <jyhuang@wizs.org>
  Revision: $Id: rtp.c,v 1.32 2001/06/21 03:00:54 jyhuang Exp $
\*=========================================================================*/
#include "rtp.h"
#include "rtp_helper.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <time.h>

static void __init_seq(rtp_source_t *src, u_int16 seq);
static int __update_seq(rtp_source_t *src, u_int16 seq);
THREAD_RETURN_TYPE __rtp_thread(void *);

#ifdef	__UNIX     /* [ for UNIX ] */
#else              /* [ for Windows ] */
static WSADATA	wsadata;
static int	need_winsock_cleanup = 0;
#endif             /* [ #ifdef __UNIX ] */

int rtp_startup(void)
{
	int	ret = 0;
#ifdef	__UNIX     /* [ for UNIX ] */
#else              /* [ for Windows ] */
	SOCKET	sock;
	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) {
		/* we need to initialize winsock */
		DWORD	dw;
		dw = WSAStartup(MAKEWORD(1, 1), &wsadata);
		if (dw == 0)
			need_winsock_cleanup = 1;
		else
			ret = -1;
	} else {	/* no need to initialize winsock */
		closesocket(sock);
	}
#endif             /* [ #ifdef __UNIX ] */

	srand((unsigned) time(0));  rand();  rand();
	return ret;
}

int rtp_cleanup(void)
{
#ifdef	__UNIX     /* [ for UNIX ] */
#else              /* [ for Windows ] */
	if (need_winsock_cleanup)
		WSACleanup();
#endif             /* [ #ifdef __UNIX ] */
	return 0;
}

int rtp_errno(const RTP *rtp)
{
	return rtp->err;
}

RTP *rtp_open(u_int16 port, u_int32 ssrc)
{
	RTP	*rtp = NULL;
	int	ret;

	rtp = (RTP *) malloc(sizeof(RTP));
	if (rtp == NULL)
		return NULL;
	
	memset(rtp, 0, sizeof(RTP));

	rtp->laddr.sin_family = AF_INET;
	rtp->laddr.sin_addr.s_addr = INADDR_ANY;

	if (port != 0) {
		rtp->sock = socket(AF_INET, SOCK_DGRAM, 0);
		if (rtp->sock == INVALID_SOCKET) {
			/* cannot create a UDP socket */
			free(rtp);
			return NULL;
		}

		rtp->laddr.sin_port = htons(port);
		ret = bind(rtp->sock, (struct sockaddr *) &rtp->laddr,
		           sizeof(rtp->laddr));
		if (ret == SOCKET_ERROR) {
			/* cannot bind local address */
			closesocket(rtp->sock);
			free(rtp);
			return NULL;
		}
	} else {	/* find the unbound ports for RTP and RTCP */
		SOCKET	sd;
		struct sockaddr_in	addr;
		int	ret2;
		unsigned short	i;

		addr.sin_family = AF_INET;
		addr.sin_addr.s_addr = INADDR_ANY;
		
		rtp->sock = sd = INVALID_SOCKET;
		for (i = RTP_PORT_BASE; i < RTP_PORT_MAX; i += 2) {
			if (rtp->sock == INVALID_SOCKET) {
				rtp->sock = socket(AF_INET, SOCK_DGRAM, 0);
				if (rtp->sock == INVALID_SOCKET) {
					/* cannot create a UDP socket */
					free(rtp);
					return NULL;
				}
			}

			if (sd == INVALID_SOCKET) {
				sd = socket(AF_INET, SOCK_DGRAM, 0);
				if (sd == INVALID_SOCKET) {
					/* cannot create a UDP socket */
					free(rtp);
					return NULL;
				}
			}
		
			rtp->laddr.sin_port = htons(i);
			addr.sin_port = htons((unsigned short) (i+1));

			ret = bind(rtp->sock, (struct sockaddr *) &rtp->laddr,
			           sizeof(rtp->laddr));
			ret2 = bind(sd, (struct sockaddr *) &addr,
			            sizeof(addr));

			if (ret != SOCKET_ERROR && ret2 != SOCKET_ERROR)
				break;
			if (ret != SOCKET_ERROR) {
				closesocket(rtp->sock);
				rtp->sock = INVALID_SOCKET;
			}
			if (ret2 != SOCKET_ERROR) {
				closesocket(sd);
				sd = INVALID_SOCKET;
			}
		}
		
		if (i >= RTP_PORT_MAX) {
			if (rtp->sock != INVALID_SOCKET)
				closesocket(rtp->sock);
			free(rtp);
			return NULL;
		}
	}
	
	rtp->paddr.sin_family = AF_INET;
	rtp->auto_rtcp = 0;
	/* initialize seq # */
	rtp->last_seq = __rtp_random();
	
	/* generate my SSRC */
	rtp->ssrc = (ssrc == 0) ? __gen_ssrc() : ssrc;
	
	/* initialize receiving SSRC list */
	__srclist_init(&rtp->srclist);
	
	return rtp;
}

int rtp_close(RTP *rtp)
{
	assert(rtp != NULL);
	if (rtp == NULL)
		return -1;
	
	if (rtp->sock != INVALID_SOCKET) {
		closesocket(rtp->sock);
		rtp->sock = INVALID_SOCKET;
	}
	
	__srclist_free(&rtp->srclist);
	
	free(rtp);
	return 0;
}

RTP *rtp_openx(u_int16 port, u_int32 ssrc, const char *cname)
{
	RTP	*rtp;
	
	rtp = rtp_open(port, ssrc);
	if (rtp == NULL)	/* failed */
		return NULL;
	
	rtp->auto_rtcp = 1;
	/* RTCP port = RTP port + 1 */
	rtp->rtcp = rtcp_open(ntohs(rtp->laddr.sin_port) + 1, cname);
	if (rtp->rtcp == NULL) {	/* failed to create an RTCP session */
		rtp_close(rtp);
		return NULL;
	}
	
	return rtp;
}

SOCKET rtp_getsocket(RTP *rtp)
{
	return rtp->sock;
}

int rtp_setpeeraddr(RTP *rtp, const char *host, u_int16 port)
{
	struct in_addr inaddr;

	assert(rtp != NULL);
	assert(host != NULL);

	if (rtp == NULL || host == NULL) {
		if (rtp)  rtp->err = RTP_EINVAL;
		return -1;
	}
	
	if (__getipaddr(host, &inaddr) < 0)	/* failed */
		return -1;
	
	rtp->paddr.sin_family = AF_INET;
	rtp->paddr.sin_addr = inaddr;
	rtp->paddr.sin_port = htons(port);
	
	if (connect(rtp->sock, (struct sockaddr *) &rtp->paddr, 
	            sizeof(rtp->paddr)) == SOCKET_ERROR) {
		/* cannot make a connected UDP socket */
		rtp->err = RTP_ECONN;
		return -1;
	}
	
	if (rtp->auto_rtcp == 1 && rtp->rtcp != NULL) {
		struct sockaddr_in	addr = rtp->paddr;
		addr.sin_port = htons(ntohs(addr.sin_port) + 1);
		rtcp_setpeeraddrx(rtp->rtcp, &addr);
	}
	
	return 0;
}

int rtp_setpeeraddrx(RTP *rtp, const struct sockaddr_in *addr)
{
	assert(rtp != NULL);
	assert(addr != NULL);

	if (rtp == NULL || addr == NULL) {
		if (rtp)  rtp->err = RTP_EINVAL;
		return -1;
	}
	
	memcpy(&rtp->paddr, addr, sizeof(struct sockaddr_in));

	if (connect(rtp->sock, (struct sockaddr *) &rtp->paddr, 
	            sizeof(rtp->paddr)) == SOCKET_ERROR) {
		/* cannot make a connected UDP socket */
		rtp->err = RTP_ECONN;
		return -1;
	}

	if (rtp->auto_rtcp == 1 && rtp->rtcp != NULL)
		rtcp_setpeeraddrx(rtp->rtcp, &(rtp->rtcp->paddr));

	return 0;
}

int rtp_read(RTP *rtp, rtp_pkt_t *pkt)
{
	int	n;
	u_int8	pad_len = 0, hdr_len = 0;
	u_int16	seq;
	rtp_hdr_t	*hdr;
	rtp_source_t	*src;
	
	assert(rtp != NULL && pkt != NULL);

	if (rtp == NULL || pkt == NULL) {
		if (rtp)  rtp->err = RTP_EINVAL;
		return -1;
	}

	/* for connected UDP socket, we call recv() instead of recvfrom() */
	n = recv(rtp->sock, pkt->buf, pkt->buflen, 0);
	if (n < 0) {	/* error */
		/* error handling */
		rtp->err = RTP_EIO;
		return -1;
	}
	hdr = (rtp_hdr_t *) pkt->buf;

	/* RTP version must equal 2 (RTP_VERSION) */
	if (hdr->ver != RTP_VERSION) {
		rtp->err = RTP_EVERSION;
		return -1;
	}
	
	/* calculate header length */
	hdr_len = sizeof(rtp_hdr_t) + (((int) hdr->cc) - 1) * sizeof(u_int32);

	/* check padding */
	if (hdr->p != 0) {
		/* 
		 * The last octet of the packet must contain a valid octet
		 * count (< packet length - header length).
		 */
		pad_len = ((u_int8 *)pkt->buf)[n-1];
		if (pad_len > (n - hdr_len)) {
			rtp->err = RTP_EPKT;
			return -1;
		}
	}

	/* check RTP extension */
	if (hdr->x != 0) {
	}

	/* calculate payload length */
	pkt->datalen = n - hdr_len - pad_len;

	seq = ntohs(hdr->seq);
	src = __srclist_lookup(&rtp->srclist, hdr->ssrc);
	
	if (src == NULL) {
		src = __srclist_add(&rtp->srclist, hdr->ssrc);
		if (src)
			__init_seq(src, seq);
	} else {
		if (__update_seq(src, seq) < 0) {
			rtp->err = RTP_ESEQ;
			return -1;
		}
	}
	
	if (rtp->auto_rtcp) {
		/* RTCP functions */
	}

	rtp->err = 0;
	return n;
}

/*
 * Extended RTP read
 * timeout is measured in millisecond, negative value for infinite.
 */
int rtp_readx(RTP *rtp, rtp_pkt_t *pkt, long timeout)
{
	assert(rtp != NULL && pkt != NULL);

	if (rtp == NULL || pkt == NULL) {
		if (rtp)  rtp->err = RTP_EINVAL;
		return -1;
	}

	if (timeout < 0) {
		return rtp_read(rtp, pkt);
	} else {
		fd_set	readset;
		struct timeval	tv;

		FD_ZERO(&readset);
		FD_SET(rtp->sock, &readset);

		tv.tv_sec = timeout / 1000;
		tv.tv_usec = (timeout % 1000) * 1000;
		if (select(rtp->sock + 1, &readset, NULL, NULL, &tv) < 0) {
			/* select() failed */
			rtp->err = RTP_EIO;
			return -1;
		}

		if (FD_ISSET(rtp->sock, &readset))
			return rtp_read(rtp, pkt);
	}
	
	rtp->err = RTP_ETIMEOUT;
	return -1;
}

int rtp_write(RTP *rtp, const rtp_pkt_t *pkt)
{
	int	n;
	rtp_hdr_t	*hdr;
	
	assert(rtp != NULL && pkt != NULL);

	if (rtp == NULL || pkt == NULL) {
		if (rtp)  rtp->err = RTP_EINVAL;
		return -1;
	}
	
	hdr = (rtp_hdr_t *) pkt->buf;
	if (hdr->seq == 0)	/* generate seq */
		hdr->seq = htons(rtp->last_seq + 1);
	rtp->last_seq = ntohs(hdr->seq);
	if (hdr->ssrc == 0)
		hdr->ssrc = htonl(rtp->ssrc);
	
	/* for connected UDP socket, we call send() instead of sendto() */
	assert(pkt->data > pkt->buf);
//	n = sendto(rtp->sock, pkt->buf, 
//	         pkt->datalen + (pkt->data - pkt->buf), 0,(struct sockaddr*)&rtp->paddr,
	n = send(rtp->sock, pkt->buf, 
	         pkt->datalen + (pkt->data - pkt->buf), 0);
//	sizeof(rtp->paddr));
	
	if (n < 0) {
		rtp->err = RTP_EIO;
		return -1;
	}
	
	if (rtp->auto_rtcp) {
		/* RTCP functions */
	}

	return n;
}

/* timeout is measured in millisecond, negative value for infinite. */
int rtp_wait(RTP *rtp, int events, long timeout)
{
	struct timeval	tv;
	fd_set	rset, wset;
	fd_set	*prset = NULL, *pwset = NULL;
	int	n, ret = 0;

	assert(rtp);

	if (rtp == NULL)
		return -1;
	
	if (events & RTPEVT_READ) {
		FD_ZERO(&rset);
		FD_SET(rtp->sock, &rset);
		prset = &rset;
	}

	if (events & RTPEVT_WRITE) {
		FD_ZERO(&wset);
		FD_SET(rtp->sock, &wset);
		pwset = &wset;
	}

	if (timeout >= 0) {
		tv.tv_sec = timeout / 1000;
		tv.tv_usec = (timeout % 1000) * 1000;
		n = select(rtp->sock + 1, prset, pwset, NULL, &tv);
	} else	/* infinite */
		n = select(rtp->sock + 1, prset, pwset, NULL, NULL);

	if (n < 0) {	/* failed */
		rtp->err = RTP_EIO;
		return -1;
	}

	if (prset && FD_ISSET(rtp->sock, &rset))
		ret |= RTPEVT_READ;
	if (pwset && FD_ISSET(rtp->sock, &wset))
		ret |= RTPEVT_WRITE;

	return ret;
}

const struct sockaddr_in *rtp_getlocaladdr(RTP *rtp)
{
	assert(rtp);

	return rtp ? &rtp->laddr : 0;
}

const struct sockaddr_in *rtp_getpeeraddr(RTP *rtp)
{
	assert(rtp);

	return rtp ? &rtp->paddr : 0;
}

const int rtp_getlocalport(RTP *rtp)
{
	assert(rtp);

	return ntohs(rtp->laddr.sin_port);
}

const int rtp_getpeerport(RTP *rtp)
{
	assert(rtp);

	return ntohs(rtp->paddr.sin_port);
}

u_int32 rtp_getssrc(const RTP *rtp)
{
	assert(rtp);

	return rtp ? rtp->ssrc : 0;
}

u_int32 rtp_setssrc(RTP *rtp, u_int32 ssrc)
{
	u_int32	save_ssrc;

	assert(rtp);

	if (rtp == NULL) {
		rtp->err = RTP_EINVAL;
		return 0;
	}
	
	save_ssrc = rtp->ssrc;
	rtp->ssrc = ssrc;
	return save_ssrc;
}

/*
char *rtp_getdataptr(char *rtpbuf, size_t ncsrc)
{
	return (rtpbuf + ( sizeof(rtp_hdr_t) - sizeof(u_int32) +
	                   ncsrc*sizeof(u_int32) ) );
}
*/

int rtp_pkt_init(rtp_pkt_t *pkt, size_t payload_size)
{
	rtp_hdr_t	*hdr;
	memset(pkt, 0, sizeof(pkt));
	pkt->buflen = payload_size + sizeof(rtp_hdr_t) + 
	              sizeof(u_int32) * 15;
	pkt->buf = malloc(pkt->buflen);
	if (pkt->buf == NULL)
		return -1;
	memset(pkt->buf, 0, pkt->buflen);
	pkt->data = pkt->buf + sizeof(rtp_hdr_t) - sizeof(u_int32);
	hdr = (rtp_hdr_t *) pkt->buf;
	hdr->ver = RTP_VERSION;
	return 0;
}

int rtp_pkt_free(rtp_pkt_t *pkt)
{
	assert(pkt);

	if (pkt == NULL)
		return -1;
	if (pkt->buf) {
		free(pkt->buf);
		pkt->buf = NULL;
	}
	pkt->buflen = 0;
	pkt->data = NULL;
	return 0;
}

int rtp_pkt_getparam(const rtp_pkt_t *pkt, rtp_pkt_param_t *param)
{
	rtp_hdr_t	*hdr;
	
	assert(pkt && param);
	
	hdr = (rtp_hdr_t *) pkt->buf;
	if (param) {
		param->ts	= ntohl(hdr->ts);
		param->m	= (u_int8) hdr->m;
		param->pt	= (u_int8) hdr->pt;
		param->ssrc	= ntohl(hdr->ssrc);
		param->seq	= ntohs(hdr->seq);
		param->cc	= (u_int8) hdr->cc;
		param->csrc_list = &hdr->csrc[0];
	}

	return 0;
}

int rtp_pkt_setparam(rtp_pkt_t *pkt, const rtp_pkt_param_t *param)
{
	rtp_hdr_t	*hdr;

	assert(pkt && param);
	
	hdr = (rtp_hdr_t *) pkt->buf;
	if (param) {
		hdr->ts		= htonl(param->ts);
		hdr->m		= param->m;
		hdr->pt		= param->pt;
		hdr->ssrc	= htonl(param->ssrc);
		hdr->seq	= htons(param->seq);
		hdr->cc		= param->cc;
		pkt->data = pkt->buf + sizeof(rtp_hdr_t) +
		            sizeof(u_int32) * (((int) hdr->cc) - 1);
	} else
		return -1;
	return 0;
}

/*
 * RTP data header validity checks: __init_seq() and __update_seq()
 */
static void __init_seq(rtp_source_t *src, u_int16 seq)
{
	assert(src);

	src->base_seq = seq - 1;
	src->max_seq = seq;
	src->bad_seq = RTP_SEQ_MOD + 1;
	src->cycles = 0;
	src->received = 0;
	src->received_prior = 0;
	src->expected_prior = 0;
}

static int __update_seq(rtp_source_t *src, u_int16 seq)
{
	u_int16	udelta;
	
	assert(src);
	udelta = seq - src->max_seq;
	
	/*
	 * Source is not valid until RTP_MIN_SEQENTIAL packets with sequential
	 * numbres have beed received.
	 */
	if (src->probation) {
		/* packet is in sequence */
		if (seq == src->max_seq + 1) {
			src->probation--;
			src->max_seq = seq;
			if (src->probation == 0) {
				__init_seq(src, seq);
				src->received++;
				return 0;	/* permission */
			}
		} else {
			src->probation = RTP_MIN_SEQUENTIAL - 1;
			src->max_seq = seq;
		}
		return -1;	/* invalid */
	} else if (udelta < RTP_MAX_DROPOUT) {
		/* in order, with permissible gap */
		if (seq < src->max_seq) {
			/* Sequence number wrapped - count anther 64K cycle */
			src->cycles += RTP_SEQ_MOD;
		}
		src->max_seq = seq;
	} else if (udelta <= RTP_SEQ_MOD - RTP_MAX_MISORDER) {
		/* the sequence number made a very large jump */
		if (seq == src->bad_seq) {
			/* 
			 * 2 sequential packets -- assume that the other side
			 * restarted without telling us so just re-sync
			 * (i.e., pretend this was the first packet).
			 */
			 __init_seq(src, seq);
		} else {
			src->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
			return -1;	/* bad */
		}
	} else {
		/* duplicate or reordered packet */
	}
	src->received++;
	return 0;
}
