/*=========================================================================*\
  Copyright (c) 2000-2001 Computer & Communications Research Laboratories,
                          Industrial Technology Research Institute
  
  RTCP - RTCP implementation
  
  Author: Jiun-Yao Huang <jyhuang@wizs.org>
  Revision: $Id: rtcp.c,v 1.14 2001/06/15 10:40:48 jyhuang Exp $
\*=========================================================================*/
#include "rtp.h"
#include "rtp_helper.h"
#include <stdlib.h>
#include <assert.h>

RTCP *rtcp_open(u_int16 port, const char *cname)
{
	RTCP *rtcp = NULL;

	rtcp = (RTCP *) malloc(sizeof(RTCP));
	if (rtcp == NULL)
		return NULL;

	rtcp->sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (rtcp->sock == INVALID_SOCKET) {
		free(rtcp);
		return NULL;
	}
	
	rtcp->laddr.sin_family = AF_INET;
	rtcp->laddr.sin_addr.s_addr = INADDR_ANY;
	
	if (port != 0) {
		rtcp->laddr.sin_port = htons(port);
		if (bind(rtcp->sock, (struct sockaddr *) &rtcp->laddr,
		         sizeof(rtcp->laddr)) == SOCKET_ERROR) {
			/* cannot bind local address */
			free(rtcp);
			return NULL;
		}
	} else {
		int	i, r;

		for (i = RTP_PORT_BASE + 1; i < RTP_PORT_MAX; i += 2) {
			rtcp->laddr.sin_port = htons(i);
			r = bind(rtcp->sock, (struct sockaddr *) &rtcp->laddr,
			         sizeof(rtcp->laddr));
			if (r != SOCKET_ERROR)	/* found */
				break;
		}

		if (i >= RTP_PORT_MAX) {
			closesocket(rtcp->sock);
			free(rtcp);
			return NULL;
		}
	}
	
	return rtcp;
}

int rtcp_close(RTCP *rtcp)
{
	assert(rtcp != NULL);

	if (rtcp == NULL)
		return -1;

	free(rtcp);
	return 0;
}

SOCKET rtcp_getsocket(RTCP *rtcp)
{
	return rtcp->sock;
}

int rtcp_setpeeraddr(RTCP *rtcp, const char *host, u_int16 port)
{
	struct in_addr	inaddr;

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

	if (rtcp == NULL || host == NULL) {
		if (rtcp)
			rtcp->err = RTP_EINVAL;
		return -1;
	}

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

int rtcp_setpeeraddrx(RTCP *rtcp, const struct sockaddr_in *addr)
{
	assert(rtcp != NULL);
	assert(addr != NULL);

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

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

	return 0;
}

int rtcp_errno(const RTCP *rtcp)
{
	assert(rtcp != NULL);

	return rtcp->err;
}
