From f5e13431a53e5ce6ebd93e447633b710ef639632 Mon Sep 17 00:00:00 2001
From: Mark Spencer <markster@digium.com>
Date: Sat, 28 Jun 2003 16:40:02 +0000
Subject: [PATCH] Add SIP/RTP video support, video enable app_echo, start on
 RTCP

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@1128 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 apps/app_echo.c                |   3 +
 channel.c                      |  20 +-
 channels/chan_mgcp.c           |   5 +-
 channels/chan_sip.c            | 295 +++++++++++++++++++------
 configs/sip.conf.sample        |   1 +
 frame.c                        |   4 +
 include/asterisk/channel.h     |  25 ++-
 include/asterisk/channel_pvt.h |   2 +
 include/asterisk/frame.h       |   2 +
 include/asterisk/rtp.h         |  12 +-
 rtp.c                          | 393 +++++++++++++++++++++++++--------
 11 files changed, 589 insertions(+), 173 deletions(-)

diff --git a/apps/app_echo.c b/apps/app_echo.c
index 7690209ac5..27ba0d86f5 100755
--- a/apps/app_echo.c
+++ b/apps/app_echo.c
@@ -55,6 +55,9 @@ static int echo_exec(struct ast_channel *chan, void *data)
 		if (f->frametype == AST_FRAME_VOICE) {
 			if (ast_write(chan, f)) 
 				break;
+		} else if (f->frametype == AST_FRAME_VIDEO) {
+			if (ast_write(chan, f)) 
+				break;
 		} else if (f->frametype == AST_FRAME_DTMF) {
 			if (f->subclass == '#') {
 				res = 0;
diff --git a/channel.c b/channel.c
index 3071113c3f..2127a46bde 100755
--- a/channel.c
+++ b/channel.c
@@ -1,4 +1,4 @@
-/*
+ /*
  * Asterisk -- A telephony toolkit for Linux.
  *
  * Channel Management
@@ -1269,6 +1269,17 @@ int ast_prod(struct ast_channel *chan)
 	return 0;
 }
 
+int ast_write_video(struct ast_channel *chan, struct ast_frame *fr)
+{
+	int res;
+	if (!chan->pvt->write_video)
+		return 0;
+	res = ast_write(chan, fr);
+	if (!res)
+		res = 1;
+	return res;
+}
+
 int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 {
 	int res = -1;
@@ -1306,6 +1317,13 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
 		if (chan->pvt->send_text)
 			res = chan->pvt->send_text(chan, (char *) fr->data);
 		break;
+	case AST_FRAME_VIDEO:
+		/* XXX Handle translation of video codecs one day XXX */
+		if (chan->pvt->write_video)
+			res = chan->pvt->write_video(chan, fr);
+		else
+			res = 0;
+		break;
 	default:
 		if (chan->pvt->write) {
 			if (chan->pvt->writetrans) {
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index c6a29e4fd8..27a962bd33 100755
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -1717,7 +1717,7 @@ static void start_rtp(struct mgcp_subchannel *sub)
 {
 		ast_pthread_mutex_lock(&sub->lock);
 		/* Allocate the RTP now */
-		sub->rtp = ast_rtp_new(NULL, NULL);
+		sub->rtp = ast_rtp_new(sched, io, 1, 0);
 		if (sub->rtp && sub->owner)
 			sub->owner->fds[0] = ast_rtp_fd(sub->rtp);
 		if (sub->rtp)
@@ -2791,8 +2791,9 @@ static struct ast_rtp *mgcp_get_rtp_peer(struct ast_channel *chan)
 	return NULL;
 }
 
-static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp)
+static int mgcp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp)
 {
+	/* XXX Is there such thing as video support with MGCP? XXX */
 	struct mgcp_subchannel *sub;
 	sub = chan->pvt->pvt;
 	if (sub) {
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 6ff15bcacb..35a506aeb3 100755
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -114,7 +114,7 @@ static pthread_t monitor_thread = 0;
 static int restart_monitor(void);
 
 /* Codecs that we support by default: */
-static int capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM;
+static int capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW | AST_FORMAT_GSM | AST_FORMAT_H261;
 static int noncodeccapability = AST_RTP_DTMF;
 
 static char ourhost[256];
@@ -125,6 +125,8 @@ static int sipdebug = 0;
 
 static int tos = 0;
 
+static int videosupport = 0;
+
 static int globaldtmfmode = SIP_DTMF_RFC2833;
 
 /* Expire slowly */
@@ -186,6 +188,7 @@ static struct sip_pvt {
 	int nat;							/* Whether to try to support NAT */
 	struct sockaddr_in sa;				/* Our peer */
 	struct sockaddr_in redirip;			/* Where our RTP should be going if not to us */
+	struct sockaddr_in vredirip;		/* Where our Video RTP should be going if not to us */
 	struct sockaddr_in recv;			/* Received as */
 	struct in_addr ourip;				/* Our IP */
 	struct ast_channel *owner;			/* Who owns us */
@@ -233,6 +236,7 @@ static struct sip_pvt {
 	struct sip_peer *peerpoke;			/* If this calls is to poke a peer, which one */
 	struct sip_registry *registry;			/* If this is a REGISTER call, to which registry */
 	struct ast_rtp *rtp;				/* RTP Session */
+	struct ast_rtp *vrtp;				/* Video RTP session */
 	struct sip_pkt *packets;			/* Packets scheduled for re-transmission */
 	struct sip_pvt *next;
 } *iflist = NULL;
@@ -367,7 +371,7 @@ static int transmit_response_with_auth(struct sip_pvt *p, char *msg, struct sip_
 static int transmit_request(struct sip_pvt *p, char *msg, int inc, int reliable);
 static int transmit_request_with_auth(struct sip_pvt *p, char *msg, int inc, int reliable);
 static int transmit_invite(struct sip_pvt *p, char *msg, int sendsdp, char *auth, char *vxml_url,char *distinctive_ring);
-static int transmit_reinvite_with_sdp(struct sip_pvt *p, struct ast_rtp *rtp);
+static int transmit_reinvite_with_sdp(struct sip_pvt *p, struct ast_rtp *rtp, struct ast_rtp *vrtp);
 static int transmit_info_with_digit(struct sip_pvt *p, char digit);
 static int transmit_message_with_text(struct sip_pvt *p, char *text);
 static int transmit_refer(struct sip_pvt *p, char *dest);
@@ -612,6 +616,10 @@ static int create_addr(struct sip_pvt *r, char *peer)
 				ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", r->nat);
 				ast_rtp_setnat(r->rtp, r->nat);
 			}
+			if (r->vrtp) {
+				ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", r->nat);
+				ast_rtp_setnat(r->vrtp, r->nat);
+			}
 			strncpy(r->peername, p->username, sizeof(r->peername)-1);
 			strncpy(r->peersecret, p->secret, sizeof(r->peersecret)-1);
 			strncpy(r->username, p->username, sizeof(r->username)-1);
@@ -761,6 +769,7 @@ static int sip_pref_append(int format)
 static int sip_codec_choose(int formats)
 {
 	struct sip_codec_pref *cur;
+	formats &= (AST_FORMAT_MAX_AUDIO - 1);
 	cur = prefs;
 	while(cur) {
 		if (formats & cur->codec)
@@ -828,6 +837,9 @@ static void __sip_destroy(struct sip_pvt *p, int lockowner)
 	if (p->rtp) {
 		ast_rtp_destroy(p->rtp);
 	}
+	if (p->vrtp) {
+		ast_rtp_destroy(p->vrtp);
+	}
 	if (p->route) {
 		free_old_route(p->route);
 		p->route = NULL;
@@ -965,31 +977,42 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
 {
 	struct sip_pvt *p = ast->pvt->pvt;
 	int res = 0;
-	if (frame->frametype != AST_FRAME_VOICE) {
-		if (frame->frametype == AST_FRAME_IMAGE)
-			return 0;
-		else {
-			ast_log(LOG_WARNING, "Can't send %d type frames with SIP write\n", frame->frametype);
-			return 0;
-		}
-	} else {
+	if (frame->frametype == AST_FRAME_VOICE) {
 		if (!(frame->subclass & ast->nativeformats)) {
 			ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
 				frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
 			return -1;
 		}
-	}
-	if (p) {
-		ast_pthread_mutex_lock(&p->lock);
-		if (p->rtp) {
-			if ((ast->_state != AST_STATE_UP) && !p->progress && !p->outgoing) {
-				transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, 0);
-				p->progress = 1;
+		if (p) {
+			ast_pthread_mutex_lock(&p->lock);
+			if (p->rtp) {
+				if ((ast->_state != AST_STATE_UP) && !p->progress && !p->outgoing) {
+					transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, 0);
+					p->progress = 1;
+				}
+				res =  ast_rtp_write(p->rtp, frame);
 			}
-			res =  ast_rtp_write(p->rtp, frame);
+			ast_pthread_mutex_unlock(&p->lock);
 		}
-		ast_pthread_mutex_unlock(&p->lock);
+	} else if (frame->frametype == AST_FRAME_VIDEO) {
+		if (p) {
+			ast_pthread_mutex_lock(&p->lock);
+			if (p->vrtp) {
+				if ((ast->_state != AST_STATE_UP) && !p->progress && !p->outgoing) {
+					transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, 0);
+					p->progress = 1;
+				}
+				res =  ast_rtp_write(p->vrtp, frame);
+			}
+			ast_pthread_mutex_unlock(&p->lock);
+		}
+	} else if (frame->frametype == AST_FRAME_IMAGE) {
+		return 0;
+	} else {
+		ast_log(LOG_WARNING, "Can't send %d type frames with SIP write\n", frame->frametype);
+		return 0;
 	}
+
 	return res;
 }
 
@@ -1109,6 +1132,11 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, char *title)
                     ast_dsp_set_features(i->vad, DSP_FEATURE_DTMF_DETECT);
                 }
 		tmp->fds[0] = ast_rtp_fd(i->rtp);
+		tmp->fds[1] = ast_rtcp_fd(i->rtp);
+		if (i->vrtp) {
+			tmp->fds[2] = ast_rtp_fd(i->vrtp);
+			tmp->fds[3] = ast_rtcp_fd(i->vrtp);
+		}
 		ast_setstate(tmp, state);
 		if (state == AST_STATE_RING)
 			tmp->rings = 1;
@@ -1124,6 +1152,7 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, char *title)
 		tmp->pvt->answer = sip_answer;
 		tmp->pvt->read = sip_read;
 		tmp->pvt->write = sip_write;
+		tmp->pvt->write_video = sip_write;
 		tmp->pvt->indicate = sip_indicate;
 		tmp->pvt->transfer = sip_transfer;
 		tmp->pvt->fixup = sip_fixup;
@@ -1245,12 +1274,27 @@ static char *get_header(struct sip_request *req, char *name)
 	return __get_header(req, name, &start);
 }
 
-static struct ast_frame *sip_rtp_read(struct sip_pvt *p)
+static struct ast_frame *sip_rtp_read(struct ast_channel *ast, struct sip_pvt *p)
 {
 	/* Retrieve audio/etc from channel.  Assumes p->lock is already held. */
 	struct ast_frame *f;
 	static struct ast_frame null_frame = { AST_FRAME_NULL, };
-	f = ast_rtp_read(p->rtp);
+	switch(ast->fdno) {
+	case 0:
+		f = ast_rtp_read(p->rtp);
+		break;
+	case 1:
+		f = ast_rtcp_read(p->rtp);
+		break;
+	case 2:
+		f = ast_rtp_read(p->vrtp);
+		break;
+	case 3:
+		f = ast_rtcp_read(p->vrtp);
+		break;
+	default:
+		f = &null_frame;
+	}
 	/* Don't send RFC2833 if we're not supposed to */
 	if (f && (f->frametype == AST_FRAME_DTMF) && !(p->dtmfmode & SIP_DTMF_RFC2833))
 		return &null_frame;
@@ -1276,7 +1320,7 @@ static struct ast_frame *sip_read(struct ast_channel *ast)
 	struct ast_frame *fr;
 	struct sip_pvt *p = ast->pvt->pvt;
 	ast_pthread_mutex_lock(&p->lock);
-	fr = sip_rtp_read(p);
+	fr = sip_rtp_read(ast, p);
 	ast_pthread_mutex_unlock(&p->lock);
 	return fr;
 }
@@ -1308,7 +1352,9 @@ static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin, int useg
 	p->initid = -1;
 	p->autokillid = -1;
 	p->stateid = -1;
-	p->rtp = ast_rtp_new(NULL, NULL);
+	p->rtp = ast_rtp_new(sched, io, 1, 0);
+	if (videosupport)
+		p->vrtp = ast_rtp_new(sched, io, 1, 0);
 	p->branch = rand();	
 	p->tag = rand();
 	
@@ -1320,17 +1366,18 @@ static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin, int useg
 		return NULL;
 	}
 	ast_rtp_settos(p->rtp, tos);
+	if (p->vrtp)
+		ast_rtp_settos(p->vrtp, tos);
 	if (useglobalnat && sin) {
 		/* Setup NAT structure according to global settings if we have an address */
 		p->nat = globalnat;
 		memcpy(&p->recv, sin, sizeof(p->recv));
 		ast_rtp_setnat(p->rtp, p->nat);
+		if (p->vrtp)
+			ast_rtp_setnat(p->vrtp, p->nat);
 	}
 	ast_pthread_mutex_init(&p->lock);
-#if 0
-	ast_rtp_set_data(p->rtp, p);
-	ast_rtp_set_callback(p->rtp, rtpready);
-#endif	
+
 	if (sin) {
 		memcpy(&p->sa, sin, sizeof(p->sa));
 		if (ast_ouraddrfor(&p->sa.sin_addr,&p->ourip))
@@ -1554,13 +1601,16 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
 	char *a;
 	char host[258];
 	int len = -1;
-	int portno;
+	int portno=0;
+	int vportno=0;
 	int peercapability, peernoncodeccapability;
+	int vpeercapability, vpeernoncodeccapability;
 	struct sockaddr_in sin;
 	char *codecs;
 	struct hostent *hp;
 	int codec;
 	int iterator;
+	int x;
 
 	/* Get codec and RTP info from SDP */
 	if (strcasecmp(get_header(req, "Content-Type"), "application/sdp")) {
@@ -1583,51 +1633,85 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
 		ast_log(LOG_WARNING, "Unable to lookup host in c= line, '%s'\n", c);
 		return -1;
 	}
-	if ((sscanf(m, "audio %d RTP/AVP %n", &portno, &len) != 1) || (len < 0)) {
-		ast_log(LOG_WARNING, "Unable to determine port number for RTP in '%s'\n", m); 
-		return -1;
+	sdpLineNum_iterator_init(&iterator);
+	while ((m = get_sdp_iterate(&iterator, req, "m"))[0] != '\0') {
+		if ((sscanf(m, "audio %d RTP/AVP %n", &x, &len) == 1)) {
+			portno = x;
+			// Scan through the RTP payload types specified in a "m=" line:
+			ast_rtp_pt_clear(p->rtp);
+			codecs = m + len;
+			while(strlen(codecs)) {
+				if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
+					ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
+					return -1;
+				}
+				if (sipdebug)
+					ast_verbose("Found audio format %d\n", codec);
+				ast_rtp_set_m_type(p->rtp, codec);
+				codecs += len;
+				/* Skip over any whitespace */
+				while(*codecs && (*codecs < 33)) codecs++;
+			}
+		}
+		if (p->vrtp && (sscanf(m, "video %d RTP/AVP %n", &x, &len) == 1)) {
+			vportno = x;
+			// Scan through the RTP payload types specified in a "m=" line:
+			ast_rtp_pt_clear(p->vrtp);
+			codecs = m + len;
+			while(strlen(codecs)) {
+				if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
+					ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
+					return -1;
+				}
+				if (sipdebug)
+					ast_verbose("Found video format %d\n", codec);
+				ast_rtp_set_m_type(p->vrtp, codec);
+				codecs += len;
+				/* Skip over any whitespace */
+				while(*codecs && (*codecs < 33)) codecs++;
+			}
+		}
 	}
 	sin.sin_family = AF_INET;
 	memcpy(&sin.sin_addr, hp->h_addr, sizeof(sin.sin_addr));
+	/* Setup audio port number */
 	sin.sin_port = htons(portno);
-	if (p->rtp)
+	if (p->rtp && sin.sin_port)
 		ast_rtp_set_peer(p->rtp, &sin);
+	/* Setup video port number */
+	sin.sin_port = htons(vportno);
+	if (p->vrtp && sin.sin_port)
+		ast_rtp_set_peer(p->vrtp, &sin);
 #if 0
 	printf("Peer RTP is at port %s:%d\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
 #endif	
-	// Scan through the RTP payload types specified in a "m=" line:
-	ast_rtp_pt_clear(p->rtp);
-	codecs = m + len;
-	while(strlen(codecs)) {
-		if (sscanf(codecs, "%d%n", &codec, &len) != 1) {
-			ast_log(LOG_WARNING, "Error in codec string '%s'\n", codecs);
-			return -1;
-		}
-		ast_rtp_set_m_type(p->rtp, codec);
-		codecs += len;
-		/* Skip over any whitespace */
-		while(*codecs && (*codecs < 33)) codecs++;
-	}
-
 	// Next, scan through each "a=rtpmap:" line, noting each
 	// specified RTP payload type (with corresponding MIME subtype):
 	sdpLineNum_iterator_init(&iterator);
 	while ((a = get_sdp_iterate(&iterator, req, "a"))[0] != '\0') {
-          char* mimeSubtype = strdup(a); // ensures we have enough space
+      char* mimeSubtype = ast_strdupa(a); // ensures we have enough space
+	  if (sipdebug)
+		ast_verbose("Pre-Found description format %s\n", mimeSubtype);
 	  if (sscanf(a, "rtpmap: %u %[^/]/", &codec, mimeSubtype) != 2) continue;
+	  if (sipdebug)
+		ast_verbose("Found description format %s\n", mimeSubtype);
 	  // Note: should really look at the 'freq' and '#chans' params too
 	  ast_rtp_set_rtpmap_type(p->rtp, codec, "audio", mimeSubtype);
-	  free(mimeSubtype);
+	  if (p->vrtp)
+		  ast_rtp_set_rtpmap_type(p->vrtp, codec, "video", mimeSubtype);
 	}
 
 	// Now gather all of the codecs that were asked for:
 	ast_rtp_get_current_formats(p->rtp,
 				&peercapability, &peernoncodeccapability);
-	p->capability = capability & peercapability;
-	p->noncodeccapability = noncodeccapability & peernoncodeccapability;
+	ast_rtp_get_current_formats(p->vrtp,
+				&vpeercapability, &vpeernoncodeccapability);
+	p->capability = capability & (peercapability | vpeercapability);
+	p->noncodeccapability = noncodeccapability & (peernoncodeccapability | vpeernoncodeccapability);
+	
 	if (sipdebug) {
-		ast_verbose("Capabilities: us - %d, them - %d, combined - %d\n",
-			    capability, peercapability, p->capability);
+		ast_verbose("Capabilities: us - %d, them - %d/%d, combined - %d\n",
+			    capability, peercapability, vpeercapability, p->capability);
 		ast_verbose("Non-codec capabilities: us - %d, them - %d, combined - %d\n",
 			    noncodeccapability, peernoncodeccapability,
 			    p->noncodeccapability);
@@ -2114,13 +2198,14 @@ static int add_digit(struct sip_request *req, char digit)
 	return 0;
 }
 
-static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp *rtp)
+static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp *rtp, struct ast_rtp *vrtp)
 {
 	int len;
 	int codec;
 	int alreadysent = 0;
 	char costr[80];
 	struct sockaddr_in sin;
+	struct sockaddr_in vsin;
 	struct sip_codec_pref *cur;
 	char v[256];
 	char s[256];
@@ -2128,9 +2213,12 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp *
 	char c[256];
 	char t[256];
 	char m[256];
+	char m2[256];
 	char a[1024] = "";
+	char a2[1024] = "";
 	int x;
 	struct sockaddr_in dest;
+	struct sockaddr_in vdest;
 	/* XXX We break with the "recommendation" and send our IP, in order that our
 	       peer doesn't have to gethostbyname() us XXX */
 	len = 0;
@@ -2139,6 +2227,9 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp *
 		return -1;
 	}
 	ast_rtp_get_us(p->rtp, &sin);
+	if (p->vrtp)
+		ast_rtp_get_us(p->vrtp, &vsin);
+
 	if (p->redirip.sin_addr.s_addr) {
 		dest.sin_port = p->redirip.sin_port;
 		dest.sin_addr = p->redirip.sin_addr;
@@ -2148,14 +2239,30 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp *
 		dest.sin_addr = p->ourip;
 		dest.sin_port = sin.sin_port;
 	}
+
+	/* Determine video destination */
+	if (p->vrtp) {
+		if (p->vredirip.sin_addr.s_addr) {
+			vdest.sin_port = p->vredirip.sin_port;
+			vdest.sin_addr = p->vredirip.sin_addr;
+		} else if (vrtp) {
+			ast_rtp_get_peer(vrtp, &vdest);
+		} else {
+			vdest.sin_addr = p->ourip;
+			vdest.sin_port = vsin.sin_port;
+		}
+	}
 	if (sipdebug)
 		ast_verbose("We're at %s port %d\n", inet_ntoa(p->ourip), ntohs(sin.sin_port));	
+	if (sipdebug && p->vrtp)
+		ast_verbose("Video is at %s port %d\n", inet_ntoa(p->ourip), ntohs(vsin.sin_port));	
 	snprintf(v, sizeof(v), "v=0\r\n");
 	snprintf(o, sizeof(o), "o=root %d %d IN IP4 %s\r\n", getpid(), getpid(), inet_ntoa(dest.sin_addr));
 	snprintf(s, sizeof(s), "s=session\r\n");
 	snprintf(c, sizeof(c), "c=IN IP4 %s\r\n", inet_ntoa(dest.sin_addr));
 	snprintf(t, sizeof(t), "t=0 0\r\n");
 	snprintf(m, sizeof(m), "m=audio %d RTP/AVP", ntohs(dest.sin_port));
+	snprintf(m2, sizeof(m2), "m=video %d RTP/AVP", ntohs(vdest.sin_port));
 	/* Start by sending our preferred codecs */
 	cur = prefs;
 	while(cur) {
@@ -2165,9 +2272,15 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp *
 			codec = ast_rtp_lookup_code(p->rtp, 1, cur->codec);
 			if (codec > -1) {
 				snprintf(costr, sizeof(costr), " %d", codec);
-				strcat(m, costr);
-				snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, cur->codec));
-				strcat(a, costr);
+				if (cur->codec < AST_FORMAT_MAX_AUDIO) {
+					strcat(m, costr);
+					snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, cur->codec));
+					strcat(a, costr);
+				} else {
+					strcat(m2, costr);
+					snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/90000\r\n", codec, ast_rtp_lookup_mime_subtype(1, cur->codec));
+					strcat(a2, costr);
+				}
 			}
 		}
 		alreadysent |= cur->codec;
@@ -2180,10 +2293,16 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp *
 				ast_verbose("Answering with capability %d\n", x);	
 			codec = ast_rtp_lookup_code(p->rtp, 1, x);
 			if (codec > -1) {
-			snprintf(costr, sizeof(costr), " %d", codec);
-				strcat(m, costr);
-				snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, x));
-				strcat(a, costr);
+				snprintf(costr, sizeof(costr), " %d", codec);
+				if (x < AST_FORMAT_MAX_AUDIO) {
+					strcat(m, costr);
+					snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/8000\r\n", codec, ast_rtp_lookup_mime_subtype(1, x));
+					strcat(a, costr);
+				} else {
+					strcat(m2, costr);
+					snprintf(costr, sizeof(costr), "a=rtpmap:%d %s/90000\r\n", codec, ast_rtp_lookup_mime_subtype(1, x));
+					strcat(a2, costr);
+				}
 			}
 		}
 	}
@@ -2207,7 +2326,10 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp *
 		}
 	}
 	strcat(m, "\r\n");
+	strcat(m2, "\r\n");
 	len = strlen(v) + strlen(s) + strlen(o) + strlen(c) + strlen(t) + strlen(m) + strlen(a);
+	if (p->vrtp)
+		len += strlen(m2) + strlen(a2);
 	snprintf(costr, sizeof(costr), "%d", len);
 	add_header(resp, "Content-Type", "application/sdp");
 	add_header(resp, "Content-Length", costr);
@@ -2218,6 +2340,10 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p, struct ast_rtp *
 	add_line(resp, t);
 	add_line(resp, m);
 	add_line(resp, a);
+	if (p->vrtp) {
+		add_line(resp, m2);
+		add_line(resp, a2);
+	}
 	return 0;
 }
 
@@ -2244,7 +2370,7 @@ static int transmit_response_with_sdp(struct sip_pvt *p, char *msg, struct sip_r
 		return -1;
 	}
 	respprep(&resp, p, msg, req);
-	add_sdp(&resp, p, NULL);
+	add_sdp(&resp, p, NULL, NULL);
 	return send_response(p, &resp, retrans, seqno);
 }
 
@@ -2307,14 +2433,14 @@ static int determine_firstline_parts( struct sip_request *req ) {
   return 1;
 }
 
-static int transmit_reinvite_with_sdp(struct sip_pvt *p, struct ast_rtp *rtp)
+static int transmit_reinvite_with_sdp(struct sip_pvt *p, struct ast_rtp *rtp, struct ast_rtp *vrtp)
 {
 	struct sip_request req;
 	if (p->canreinvite == REINVITE_UPDATE)
 		reqprep(&req, p, "UPDATE", 0);
 	else
 		reqprep(&req, p, "INVITE", 0);
-	add_sdp(&req, p, rtp);
+	add_sdp(&req, p, rtp, vrtp);
 	/* Use this as the basis */
 	copy_request(&p->initreq, &req);
 	parse(&p->initreq);
@@ -2410,7 +2536,7 @@ static int transmit_invite(struct sip_pvt *p, char *cmd, int sdp, char *auth, ch
 		add_header(&req, "Alert-info",distinctive_ring);
 	}
 	if (sdp) {
-		add_sdp(&req, p, NULL);
+		add_sdp(&req, p, NULL, NULL);
 	} else {
 		add_header(&req, "Content-Length", "0");
 		add_blank_header(&req);
@@ -3437,6 +3563,10 @@ static int check_user(struct sip_pvt *p, struct sip_request *req, char *cmd, cha
 				ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", p->nat);
 				ast_rtp_setnat(p->rtp, p->nat);
 			}
+			if (p->vrtp) {
+				ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", p->nat);
+				ast_rtp_setnat(p->vrtp, p->nat);
+			}
 			if (!(res = check_auth(p, req, p->randdata, sizeof(p->randdata), user->name, user->secret, cmd, uri, reliable))) {
 				sip_cancel_destroy(p);
 				if (strlen(user->context))
@@ -3475,6 +3605,10 @@ static int check_user(struct sip_pvt *p, struct sip_request *req, char *cmd, cha
 					ast_log(LOG_DEBUG, "Setting NAT on RTP to %d\n", p->nat);
 					ast_rtp_setnat(p->rtp, p->nat);
 				}
+				if (p->vrtp) {
+					ast_log(LOG_DEBUG, "Setting NAT on VRTP to %d\n", p->nat);
+					ast_rtp_setnat(p->vrtp, p->nat);
+				}
 				p->canreinvite = peer->canreinvite;
 				strncpy(p->username, peer->name, sizeof(p->username) - 1);
 				if (strlen(peer->context))
@@ -4124,6 +4258,10 @@ static void handle_response(struct sip_pvt *p, int resp, char *rest, struct sip_
 					/* Immediately stop RTP */
 					ast_rtp_stop(p->rtp);
 				}
+				if (p->vrtp) {
+					/* Immediately stop VRTP */
+					ast_rtp_stop(p->vrtp);
+				}
 				/* XXX Locking issues?? XXX */
 				switch(resp) {
 				case 302: /* Moved temporarily */
@@ -4466,6 +4604,10 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
 			/* Immediately stop RTP */
 			ast_rtp_stop(p->rtp);
 		}
+		if (p->vrtp) {
+			/* Immediately stop VRTP */
+			ast_rtp_stop(p->vrtp);
+		}
 		if (p->owner)
 			ast_queue_hangup(p->owner, 0);
 		transmit_response(p, "200 OK", req);
@@ -4478,6 +4620,10 @@ static int handle_request(struct sip_pvt *p, struct sip_request *req, struct soc
 			/* Immediately stop RTP */
 			ast_rtp_stop(p->rtp);
 		}
+		if (p->vrtp) {
+			/* Immediately stop VRTP */
+			ast_rtp_stop(p->vrtp);
+		}
 		if (p->owner)
 			ast_queue_hangup(p->owner, 0);
 		transmit_response(p, "200 OK", req);
@@ -5238,6 +5384,7 @@ static int reload_config(void)
 	strcpy(language, "");
 	strcpy(fromdomain, "");
 	globalcanreinvite = REINVITE_INVITE;
+	videosupport = 0;
 	v = ast_variable_browse(cfg, "general");
 	while(v) {
 		/* Create the interface list */
@@ -5254,6 +5401,8 @@ static int reload_config(void)
 				ast_log(LOG_WARNING, "Unknown dtmf mode '%s', using rfc2833\n", v->value);
 				globaldtmfmode = SIP_DTMF_RFC2833;
 			}
+		} else if (!strcasecmp(v->name, "videosupport")) {
+			videosupport = ast_true(v->value);
 		} else if (!strcasecmp(v->name, "notifymimetype")) {
 			strncpy(notifymime, v->value, sizeof(notifymime) - 1);
 		} else if (!strcasecmp(v->name, "language")) {
@@ -5422,7 +5571,16 @@ static struct ast_rtp *sip_get_rtp_peer(struct ast_channel *chan)
 	return NULL;
 }
 
-static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp)
+static struct ast_rtp *sip_get_vrtp_peer(struct ast_channel *chan)
+{
+	struct sip_pvt *p;
+	p = chan->pvt->pvt;
+	if (p && p->vrtp && p->canreinvite)
+		return p->vrtp;
+	return NULL;
+}
+
+static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp)
 {
 	struct sip_pvt *p;
 	p = chan->pvt->pvt;
@@ -5431,7 +5589,11 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp)
 			ast_rtp_get_peer(rtp, &p->redirip);
 		else
 			memset(&p->redirip, 0, sizeof(p->redirip));
-		transmit_reinvite_with_sdp(p, rtp);
+		if (vrtp)
+			ast_rtp_get_peer(vrtp, &p->vredirip);
+		else
+			memset(&p->vredirip, 0, sizeof(p->vredirip));
+		transmit_reinvite_with_sdp(p, rtp, vrtp);
 		p->outgoing = 1;
 		return 0;
 	}
@@ -5440,6 +5602,7 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp)
 
 static struct ast_rtp_protocol sip_rtp = {
 	get_rtp_info: sip_get_rtp_peer,
+	get_vrtp_info: sip_get_vrtp_peer,
 	set_rtp_peer: sip_set_rtp_peer,
 };
 
diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample
index a26996939d..aa54ea02c0 100755
--- a/configs/sip.conf.sample
+++ b/configs/sip.conf.sample
@@ -11,6 +11,7 @@ context = default		; Default for incoming calls
 ;maxexpirey=3600		; Max length of incoming registration we allow
 ;defaultexpirey=120		; Default length of incoming/outoing registration
 ;notifymimetype=text/plain	; Allow overriding of mime type in NOTIFY
+;videosupport=yes		; Turn on support for SIP video
 ;
 ;register => 1234@mysipprovider.com	; Register with a SIP provider
 ;register => 2345@mysipprovider.com/1234 ; Register 2345 at sip provider as 1234 here.
diff --git a/frame.c b/frame.c
index 482014ca49..ec5c59dda2 100755
--- a/frame.c
+++ b/frame.c
@@ -367,6 +367,10 @@ int ast_getformatbyname(char *name)
 		return AST_FORMAT_SPEEX;
 	else if (!strcasecmp(name, "ilbc"))
 		return AST_FORMAT_ILBC;
+	else if (!strcasecmp(name, "h261"))
+		return AST_FORMAT_H261;
+	else if (!strcasecmp(name, "h263"))
+		return AST_FORMAT_H263;
 	else if (!strcasecmp(name, "all"))
 		return 0x7FFFFFFF;
 	return 0;
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 03b1fa4487..c56a1fc76a 100755
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -39,7 +39,7 @@ extern "C" {
 #define MAX_LANGUAGE 20
 
 
-#define AST_MAX_FDS 4
+#define AST_MAX_FDS 8
 
 struct ast_generator {
 	void *(*alloc)(struct ast_channel *chan, void *params);
@@ -72,7 +72,13 @@ struct ast_channel {
 	int writeinterrupt;
 
 	/*! Who are we bridged to, if we're bridged */
-	struct ast_channel *bridge;		
+	struct ast_channel *bridge;
+	/*! Who did we call? */
+	struct ast_channel *dialed;
+	/*! Who called us? */
+	struct ast_channel *dialing;
+	/*! Reverse the dialed link (0 false, 1 true) */
+	int reversedialed;
 	/*! Channel that will masquerade as us */
 	struct ast_channel *masq;		
 	/*! Who we are masquerading as */
@@ -122,7 +128,7 @@ struct ast_channel {
 	/*! Number of rings so far */
 	int rings;				
 	/*! Current level of application */
-	int stack;				
+	int stack;
 
 
 	/*! Kinds of data this channel can natively handle */
@@ -199,7 +205,7 @@ struct ast_channel {
 	unsigned int pickupgroup;
 	
 	/*! For easy linking */
-	struct ast_channel *next;		
+	struct ast_channel *next;
 
 };
 
@@ -452,7 +458,7 @@ int ast_waitfor_n_fd(int *fds, int n, int *ms, int *exception);
 
 
 //! Reads a frame
-/*! 
+/*!
  * \param chan channel to read a frame from
  * Read a frame.  Returns a frame, or NULL on error.  If it returns NULL, you
    best just stop reading frames and assume the channel has been
@@ -468,6 +474,15 @@ struct ast_frame *ast_read(struct ast_channel *chan);
  */
 int ast_write(struct ast_channel *chan, struct ast_frame *frame);
 
+//! Write video frame to a channel
+/*!
+ * \param chan destination channel of the frame
+ * \param frame frame that will be written
+ * This function writes the given frame to the indicated channel.
+ * It returns 1 on success, 0 if not implemented, and -1 on failure.
+ */
+int ast_write_video(struct ast_channel *chan, struct ast_frame *frame);
+
 /* Send empty audio to prime a channel driver */
 int ast_prod(struct ast_channel *chan);
 
diff --git a/include/asterisk/channel_pvt.h b/include/asterisk/channel_pvt.h
index d52a90fc65..366101eea8 100755
--- a/include/asterisk/channel_pvt.h
+++ b/include/asterisk/channel_pvt.h
@@ -67,6 +67,8 @@ struct ast_channel_pvt {
 	int (*queryoption)(struct ast_channel *chan, int option, void *data, int *datalen);
 	/*! Blind transfer other side */
 	int (*transfer)(struct ast_channel *chan, char *newdest);
+	/*! Write a frame, in standard format */
+	int (*write_video)(struct ast_channel *chan, struct ast_frame *frame);
 };
 
 //! Create a channel structure
diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h
index b0ea7ef0f6..37e681024a 100755
--- a/include/asterisk/frame.h
+++ b/include/asterisk/frame.h
@@ -170,6 +170,8 @@ struct ast_frame_chain {
 #define AST_FORMAT_H261		(1 << 18)
 /*! H.263 Video */
 #define AST_FORMAT_H263		(1 << 19)
+/*! Max one */
+#define AST_FORMAT_MAX_VIDEO	(1 << 24)
 
 /* Control frame types */
 /*! Other end has hungup */
diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h
index c183cb1bdb..cc8d24da7d 100755
--- a/include/asterisk/rtp.h
+++ b/include/asterisk/rtp.h
@@ -37,8 +37,8 @@ extern "C" {
 
 struct ast_rtp_protocol {
 	struct ast_rtp *(*get_rtp_info)(struct ast_channel *chan);				/* Get RTP struct, or NULL if unwilling to transfer */
-	int (*set_rtp_peer)(struct ast_channel *chan, struct ast_rtp *peer);	/* Set RTP peer */
-	int (*get_rtp_willing)(struct ast_channel *chan);		/* Willing to native bridge */
+	struct ast_rtp *(*get_vrtp_info)(struct ast_channel *chan);				/* Get RTP struct, or NULL if unwilling to transfer */
+	int (*set_rtp_peer)(struct ast_channel *chan, struct ast_rtp *peer, struct ast_rtp *vpeer);	/* Set RTP peer */
 	char *type;
 	struct ast_rtp_protocol *next;
 };
@@ -47,11 +47,11 @@ struct ast_rtp;
 
 typedef int (*ast_rtp_callback)(struct ast_rtp *rtp, struct ast_frame *f, void *data);
 
-struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io);
+struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode);
 
 void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them);
 
-void ast_rtp_get_peer(struct ast_rtp *rpt, struct sockaddr_in *them);
+void ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them);
 
 void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us);
 
@@ -65,8 +65,12 @@ int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *f);
 
 struct ast_frame *ast_rtp_read(struct ast_rtp *rtp);
 
+struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp);
+
 int ast_rtp_fd(struct ast_rtp *rtp);
 
+int ast_rtcp_fd(struct ast_rtp *rtp);
+
 int ast_rtp_senddigit(struct ast_rtp *rtp, char digit);
 
 int ast_rtp_settos(struct ast_rtp *rtp, int tos);
diff --git a/rtp.c b/rtp.c
index 63cb6c415d..6fd6f71903 100755
--- a/rtp.c
+++ b/rtp.c
@@ -34,6 +34,8 @@
 #include <asterisk/channel_pvt.h>
 #include <asterisk/config.h>
 
+#define RTP_MTU		1200
+
 #define TYPE_HIGH	 0x0
 #define TYPE_LOW	 0x1
 #define TYPE_SILENCE	 0x2
@@ -57,10 +59,13 @@ struct ast_rtp {
 	int s;
 	char resp;
 	struct ast_frame f;
-	unsigned char rawdata[1024 + AST_FRIENDLY_OFFSET];
+	unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET];
+	int readsofar;
 	unsigned int ssrc;
 	unsigned int lastts;
 	unsigned int lastrxts;
+	unsigned int lastividtimestamp;
+	unsigned int lastovidtimestamp;
 	int lasttxformat;
 	int lastrxformat;
 	int dtmfcount;
@@ -81,6 +86,13 @@ struct ast_rtp {
     int rtp_lookup_code_cache_isAstFormat;
     int rtp_lookup_code_cache_code;
     int rtp_lookup_code_cache_result;
+	struct ast_rtcp *rtcp;
+};
+
+struct ast_rtcp {
+	int s;		/* Socket */
+	struct sockaddr_in us;
+	struct sockaddr_in them;
 };
 
 static struct ast_rtp_protocol *protos = NULL;
@@ -90,6 +102,13 @@ int ast_rtp_fd(struct ast_rtp *rtp)
 	return rtp->s;
 }
 
+int ast_rtcp_fd(struct ast_rtp *rtp)
+{
+	if (rtp->rtcp)
+		return rtp->rtcp->s;
+	return -1;
+}
+
 static int g723_len(unsigned char buf)
 {
 	switch(buf & TYPE_MASK) {
@@ -264,6 +283,47 @@ static int rtpread(int *id, int fd, short events, void *cbdata)
 	return 1;
 }
 
+struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp)
+{
+	static struct ast_frame null_frame = { AST_FRAME_NULL, };
+	int len;
+	int hdrlen = 8;
+	int res;
+	struct sockaddr_in sin;
+	unsigned int rtcpdata[1024];
+	
+	if (!rtp->rtcp)
+		return &null_frame;
+
+	len = sizeof(sin);
+	
+	res = recvfrom(rtp->rtcp->s, rtcpdata, sizeof(rtcpdata),
+					0, (struct sockaddr *)&sin, &len);
+	
+	if (res < 0) {
+		ast_log(LOG_WARNING, "RTP Read error: %s\n", strerror(errno));
+		if (errno == EBADF)
+			CRASH;
+		return &null_frame;
+	}
+
+	if (res < hdrlen) {
+		ast_log(LOG_WARNING, "RTP Read too short\n");
+		return &null_frame;
+	}
+
+	if (rtp->nat) {
+		/* Send to whoever sent to us */
+		if ((rtp->rtcp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
+		    (rtp->rtcp->them.sin_port != sin.sin_port)) {
+			memcpy(&rtp->them, &sin, sizeof(rtp->them));
+			ast_log(LOG_DEBUG, "RTP NAT: Using address %s:%d\n", inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+		}
+	}
+	ast_log(LOG_DEBUG, "Got RTCP report of %d bytes\n", res);
+	return &null_frame;
+}
+
 struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
 {
 	int res;
@@ -272,18 +332,22 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
 	unsigned int seqno;
 	int payloadtype;
 	int hdrlen = 12;
+	int mark;
 	unsigned int timestamp;
 	unsigned int *rtpheader;
+	unsigned char cache[12];
 	static struct ast_frame *f, null_frame = { AST_FRAME_NULL, };
 	struct rtpPayloadType rtpPT;
 	
 	len = sizeof(sin);
 	
-	res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET,
+	/* Cache where the header will go */
+	memcpy(cache, rtp->rawdata + AST_FRIENDLY_OFFSET + rtp->readsofar, 12);
+	res = recvfrom(rtp->s, rtp->rawdata + AST_FRIENDLY_OFFSET + rtp->readsofar, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET - rtp->readsofar,
 					0, (struct sockaddr *)&sin, &len);
 
 
-	rtpheader = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET);
+	rtpheader = (unsigned int *)(rtp->rawdata + rtp->readsofar + AST_FRIENDLY_OFFSET);
 	if (res < 0) {
 		ast_log(LOG_WARNING, "RTP Read error: %s\n", strerror(errno));
 		if (errno == EBADF)
@@ -305,14 +369,22 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
 	/* Get fields */
 	seqno = ntohl(rtpheader[0]);
 	payloadtype = (seqno & 0x7f0000) >> 16;
+	mark = seqno & (1 << 23);
 	seqno &= 0xffff;
 	timestamp = ntohl(rtpheader[1]);
+
+	/* Restore original data if important */
+	if (rtp->readsofar)
+		memcpy(rtp->rawdata + AST_FRIENDLY_OFFSET + rtp->readsofar, cache, 12);
+	rtpheader = NULL;
+	
+	rtp->readsofar += (res - hdrlen);
 #if 0
 	printf("Got RTP packet from %s:%d (type %d, seq %d, ts %d, len = %d)\n", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp,res - hdrlen);
 #endif	
-	rtp->f.frametype = AST_FRAME_VOICE;
 	rtpPT = ast_rtp_lookup_pt(rtp, payloadtype);
 	if (!rtpPT.isAstFormat) {
+	  rtp->readsofar = 0;
 	  // This is special in-band data that's not one of our codecs
 	  if (rtpPT.code == AST_RTP_DTMF) {
 	    /* It's special -- rfc2833 process it */
@@ -332,6 +404,10 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
 	  }
 	}
 	rtp->f.subclass = rtpPT.code;
+	if (rtp->f.subclass < AST_FORMAT_MAX_AUDIO)
+		rtp->f.frametype = AST_FRAME_VOICE;
+	else
+		rtp->f.frametype = AST_FRAME_VIDEO;
 	rtp->lastrxformat = rtp->f.subclass;
 
 	if (!rtp->lastrxts)
@@ -353,43 +429,56 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
 
 	/* Send any pending DTMF */
 	if (rtp->resp && !rtp->dtmfcount) {
+		rtp->readsofar = 0;
 		ast_log(LOG_DEBUG, "Sending pending DTMF\n");
 		return send_dtmf(rtp);
 	}
 	rtp->f.mallocd = 0;
-	rtp->f.datalen = res - hdrlen;
+	rtp->f.datalen = rtp->readsofar;
 	rtp->f.data = rtp->rawdata + hdrlen + AST_FRIENDLY_OFFSET;
 	rtp->f.offset = hdrlen + AST_FRIENDLY_OFFSET;
-	switch(rtp->f.subclass) {
-	case AST_FORMAT_ULAW:
-	case AST_FORMAT_ALAW:
-		rtp->f.samples = rtp->f.datalen;
-		break;
-	case AST_FORMAT_SLINEAR:
-		rtp->f.samples = rtp->f.datalen / 2;
-		break;
-	case AST_FORMAT_GSM:
-		rtp->f.samples = 160 * (rtp->f.datalen / 33);
-		break;
-	case AST_FORMAT_ILBC:
-		rtp->f.samples = 240 * (rtp->f.datalen / 50);
-		break;
-	case AST_FORMAT_ADPCM:
-		rtp->f.samples = rtp->f.datalen * 2;
-		break;
-	case AST_FORMAT_G729A:
-		rtp->f.samples = rtp->f.datalen * 8;
-		break;
-	case AST_FORMAT_G723_1:
-		rtp->f.samples = g723_samples(rtp->f.data, rtp->f.datalen);
-		break;
-	case AST_FORMAT_SPEEX:
-	        rtp->f.samples = 160;
-		// assumes that the RTP packet contained one Speex frame
-		break;
-	default:
-		ast_log(LOG_NOTICE, "Unable to calculate samples for format %d\n", rtp->f.subclass);
-		break;
+	if (rtp->f.subclass < AST_FORMAT_MAX_AUDIO) {
+		rtp->readsofar = 0;
+		switch(rtp->f.subclass) {
+		case AST_FORMAT_ULAW:
+		case AST_FORMAT_ALAW:
+			rtp->f.samples = rtp->f.datalen;
+			break;
+		case AST_FORMAT_SLINEAR:
+			rtp->f.samples = rtp->f.datalen / 2;
+			break;
+		case AST_FORMAT_GSM:
+			rtp->f.samples = 160 * (rtp->f.datalen / 33);
+			break;
+		case AST_FORMAT_ILBC:
+			rtp->f.samples = 240 * (rtp->f.datalen / 50);
+			break;
+		case AST_FORMAT_ADPCM:
+			rtp->f.samples = rtp->f.datalen * 2;
+			break;
+		case AST_FORMAT_G729A:
+			rtp->f.samples = rtp->f.datalen * 8;
+			break;
+		case AST_FORMAT_G723_1:
+			rtp->f.samples = g723_samples(rtp->f.data, rtp->f.datalen);
+			break;
+		case AST_FORMAT_SPEEX:
+		        rtp->f.samples = 160;
+			// assumes that the RTP packet contained one Speex frame
+			break;
+		default:
+			ast_log(LOG_NOTICE, "Unable to calculate samples for format %d\n", rtp->f.subclass);
+			break;
+		}
+	} else {
+		/* Video -- samples is # of samples vs. 90000 */
+		rtp->f.samples = timestamp - rtp->lastividtimestamp;
+		rtp->lastividtimestamp = timestamp;
+		/* Return now if it's not the whole frame */
+		if (!mark) {
+			return &null_frame;
+		}
+		rtp->readsofar = 0;
 	}
 	rtp->f.src = "RTP";
 	return &rtp->f;
@@ -566,7 +655,27 @@ char* ast_rtp_lookup_mime_subtype(int isAstFormat, int code) {
   return "";
 }
 
-struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io)
+static struct ast_rtcp *ast_rtcp_new(void)
+{
+	struct ast_rtcp *rtcp;
+	long flags;
+	rtcp = malloc(sizeof(struct ast_rtcp));
+	if (!rtcp)
+		return NULL;
+	memset(rtcp, 0, sizeof(struct ast_rtcp));
+	rtcp->s = socket(AF_INET, SOCK_DGRAM, 0);
+	rtcp->us.sin_family = AF_INET;
+	if (rtcp->s < 0) {
+		free(rtcp);
+		ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno));
+		return NULL;
+	}
+	flags = fcntl(rtcp->s, F_GETFL);
+	fcntl(rtcp->s, F_SETFL, flags | O_NONBLOCK);
+	return rtcp;
+}
+
+struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode)
 {
 	struct ast_rtp *rtp;
 	int x;
@@ -586,6 +695,10 @@ struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io)
 		ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno));
 		return NULL;
 	}
+	if (sched && rtcpenable) {
+		rtp->sched = sched;
+		rtp->rtcp = ast_rtcp_new();
+	}
 	flags = fcntl(rtp->s, F_GETFL);
 	fcntl(rtp->s, F_SETFL, flags | O_NONBLOCK);
 	/* Find us a place */
@@ -595,11 +708,18 @@ struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io)
 	for (;;) {
 		/* Must be an even port number by RTP spec */
 		rtp->us.sin_port = htons(x);
-		if (!bind(rtp->s, &rtp->us, sizeof(rtp->us)))
+		if (rtp->rtcp)
+			rtp->rtcp->us.sin_port = htons(x + 1);
+		if (!bind(rtp->s, &rtp->us, sizeof(rtp->us)) &&
+			(!rtp->rtcp || !bind(rtp->rtcp->s, &rtp->rtcp->us, sizeof(rtp->rtcp->us))))
 			break;
 		if (errno != EADDRINUSE) {
 			ast_log(LOG_WARNING, "Unexpected bind error: %s\n", strerror(errno));
 			close(rtp->s);
+			if (rtp->rtcp) {
+				close(rtp->rtcp->s);
+				free(rtp->rtcp);
+			}
 			free(rtp);
 			return NULL;
 		}
@@ -609,11 +729,15 @@ struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io)
 		if (x == startplace) {
 			ast_log(LOG_WARNING, "No RTP ports remaining\n");
 			close(rtp->s);
+			if (rtp->rtcp) {
+				close(rtp->rtcp->s);
+				free(rtp->rtcp);
+			}
 			free(rtp);
 			return NULL;
 		}
 	}
-	if (io && sched) {
+	if (io && sched && callbackmode) {
 		/* Operate this one in a callback mode */
 		rtp->sched = sched;
 		rtp->io = io;
@@ -635,6 +759,10 @@ void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them)
 {
 	rtp->them.sin_port = them->sin_port;
 	rtp->them.sin_addr = them->sin_addr;
+	if (rtp->rtcp) {
+		rtp->rtcp->them.sin_port = htons(ntohs(them->sin_port) + 1);
+		rtp->rtcp->them.sin_addr = them->sin_addr;
+	}
 }
 
 void ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them)
@@ -653,6 +781,10 @@ void ast_rtp_stop(struct ast_rtp *rtp)
 {
 	memset(&rtp->them.sin_addr, 0, sizeof(rtp->them.sin_addr));
 	memset(&rtp->them.sin_port, 0, sizeof(rtp->them.sin_port));
+	if (rtp->rtcp) {
+		memset(&rtp->rtcp->them.sin_addr, 0, sizeof(rtp->them.sin_addr));
+		memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->them.sin_port));
+	}
 }
 
 void ast_rtp_destroy(struct ast_rtp *rtp)
@@ -663,6 +795,10 @@ void ast_rtp_destroy(struct ast_rtp *rtp)
 		ast_io_remove(rtp->io, rtp->ioid);
 	if (rtp->s > -1)
 		close(rtp->s);
+	if (rtp->rtcp) {
+		close(rtp->rtcp->s);
+		free(rtp->rtcp);
+	}
 	free(rtp);
 }
 
@@ -750,57 +886,97 @@ static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec
 	int res;
 	int ms;
 	int pred;
+	int mark = 0;
+	int pos, len;
 
 	ms = calc_txstamp(rtp);
 	/* Default prediction */
-	pred = rtp->lastts + ms * 8;
-	
-	switch(f->subclass) {
-	case AST_FORMAT_ULAW:
-	case AST_FORMAT_ALAW:
-		/* If we're within +/- 20ms from when where we
-		   predict we should be, use that */
-		pred = rtp->lastts + f->datalen;
-		break;
-	case AST_FORMAT_G729A:
-		pred = rtp->lastts + f->datalen * 8;
-		break;
-	case AST_FORMAT_GSM:
-		pred = rtp->lastts + (f->datalen * 160 / 33);
-		break;
-	case AST_FORMAT_ILBC:
-		pred = rtp->lastts + (f->datalen * 240 / 50);
-		break;
-	case AST_FORMAT_G723_1:
-		pred = rtp->lastts + g723_samples(f->data, f->datalen);
-		break;
-	case AST_FORMAT_SPEEX:
-	        pred = rtp->lastts + 160;
-		// assumes that the RTP packet contains one Speex frame
-		break;
-	default:
-		ast_log(LOG_WARNING, "Not sure about timestamp format for codec format %d\n", f->subclass);
-	}
+	if (f->subclass < AST_FORMAT_MAX_AUDIO) {
+		pred = rtp->lastts + ms * 8;
+		
+		switch(f->subclass) {
+		case AST_FORMAT_ULAW:
+		case AST_FORMAT_ALAW:
+			/* If we're within +/- 20ms from when where we
+			   predict we should be, use that */
+			pred = rtp->lastts + f->datalen;
+			break;
+		case AST_FORMAT_G729A:
+			pred = rtp->lastts + f->datalen * 8;
+			break;
+		case AST_FORMAT_GSM:
+			pred = rtp->lastts + (f->datalen * 160 / 33);
+			break;
+		case AST_FORMAT_ILBC:
+			pred = rtp->lastts + (f->datalen * 240 / 50);
+			break;
+		case AST_FORMAT_G723_1:
+			pred = rtp->lastts + g723_samples(f->data, f->datalen);
+			break;
+		case AST_FORMAT_SPEEX:
+		        pred = rtp->lastts + 160;
+			// assumes that the RTP packet contains one Speex frame
+			break;
+		default:
+			ast_log(LOG_WARNING, "Not sure about timestamp format for codec format %d\n", f->subclass);
+		}
 
-	/* Re-calculate last TS */
-	rtp->lastts = rtp->lastts + ms * 8;
-	/* If it's close to ou prediction, go for it */
-	if (abs(rtp->lastts - pred) < 640)
-		rtp->lastts = pred;
-	else
-		ast_log(LOG_DEBUG, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms);
-	/* Get a pointer to the header */
-	rtpheader = (unsigned int *)(f->data - hdrlen);
-	rtpheader[0] = htonl((2 << 30) | (codec << 16) | (rtp->seqno++));
-	rtpheader[1] = htonl(rtp->lastts);
-	rtpheader[2] = htonl(rtp->ssrc); 
-	if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) {
-		res = sendto(rtp->s, (void *)rtpheader, f->datalen + hdrlen, 0, &rtp->them, sizeof(rtp->them));
-		if (res <0) 
-			ast_log(LOG_NOTICE, "RTP Transmission error to %s:%d: %s\n", inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno));
+		/* Re-calculate last TS */
+		rtp->lastts = rtp->lastts + ms * 8;
+		/* If it's close to our prediction, go for it */
+		if (abs(rtp->lastts - pred) < 640)
+			rtp->lastts = pred;
+		else
+			ast_log(LOG_DEBUG, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms);
+		/* Get a pointer to the header */
+		rtpheader = (unsigned int *)(f->data - hdrlen);
+		rtpheader[0] = htonl((2 << 30) | (codec << 16) | (rtp->seqno++) | (mark << 23));
+		rtpheader[1] = htonl(rtp->lastts);
+		rtpheader[2] = htonl(rtp->ssrc); 
+		if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) {
+			res = sendto(rtp->s, (void *)rtpheader, f->datalen + hdrlen, 0, &rtp->them, sizeof(rtp->them));
+			if (res <0) 
+				ast_log(LOG_NOTICE, "RTP Transmission error to %s:%d: %s\n", inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno));
 #if 0
-		printf("Sent %d bytes of RTP data to %s:%d\n", res, inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port));
+			printf("Sent %d bytes of RTP data to %s:%d\n", res, inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port));
 #endif		
+		}
+	} else {
+		pred = rtp->lastovidtimestamp + f->samples;
+		/* Re-calculate last TS */
+		rtp->lastts = rtp->lastts + ms * 90;
+		/* If it's close to our prediction, go for it */
+		if (abs(rtp->lastts - pred) < 7200) {
+			rtp->lastts = pred;
+			rtp->lastovidtimestamp += f->samples;
+		} else {
+			ast_log(LOG_DEBUG, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms);
+			rtp->lastovidtimestamp = rtp->lastts;
+		}
+		pos = 0;
+		while(pos < f->datalen) {
+			/* Split packets up if necessary */
+			mark = 0;
+			len = f->datalen;
+			if (len > RTP_MTU)
+				len = RTP_MTU;
+			if ((pos + len) >= f->datalen)
+				mark = 1;
+			/* Get a pointer to the header */
+			rtpheader = (unsigned int *)(f->data + pos - hdrlen);
+			rtpheader[0] = htonl((2 << 30) | (codec << 16) | (rtp->seqno++) | (mark << 23));
+			rtpheader[1] = htonl(rtp->lastts);
+			rtpheader[2] = htonl(rtp->ssrc); 
+			if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) {
+				res = sendto(rtp->s, (void *)rtpheader, len + hdrlen, 0, &rtp->them, sizeof(rtp->them));
+				if (res <0) 
+					ast_log(LOG_NOTICE, "RTP Transmission error to %s:%d: %s\n", inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno));
+#if 0
+				printf("Sent %d bytes of RTP data to %s:%d\n", res, inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port));
+#endif		
+			}
+			pos += len;
+		}
 	}
 	return 0;
 }
@@ -821,7 +997,7 @@ int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f)
 		return 0;
 	
 	/* Make sure we have enough space for RTP header */
-	if (_f->frametype != AST_FRAME_VOICE) {
+	if ((_f->frametype != AST_FRAME_VOICE) && (_f->frametype != AST_FRAME_VIDEO)) {
 		ast_log(LOG_WARNING, "RTP can only send voice\n");
 		return -1;
 	}
@@ -898,6 +1074,8 @@ int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f)
 	default:	
 		ast_log(LOG_WARNING, "Not sure about sending format %d packets\n", _f->subclass);
 		// fall through to...
+	case AST_FORMAT_H261:
+	case AST_FORMAT_H263:
 	case AST_FORMAT_SPEEX:
 	        // Don't buffer outgoing frames; send them one-per-packet:
 		if (_f->offset < hdrlen) {
@@ -963,13 +1141,21 @@ int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, st
 	struct ast_frame *f;
 	struct ast_channel *who, *cs[3];
 	struct ast_rtp *p0, *p1;
+	struct ast_rtp *vp0, *vp1;
 	struct ast_rtp_protocol *pr0, *pr1;
 	struct sockaddr_in ac0, ac1;
+	struct sockaddr_in vac0, vac1;
 	struct sockaddr_in t0, t1;
+	struct sockaddr_in vt0, vt1;
 	
 	void *pvt0, *pvt1;
 	int to;
 
+	memset(&vt0, 0, sizeof(vt0));
+	memset(&vt1, 0, sizeof(vt1));
+	memset(&vac0, 0, sizeof(vac0));
+	memset(&vac1, 0, sizeof(vac1));
+
 	/* XXX Wait a half a second for things to settle up 
 			this really should be fixed XXX */
 	ast_autoservice_start(c0);
@@ -1000,24 +1186,36 @@ int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, st
 	pvt0 = c0->pvt->pvt;
 	pvt1 = c1->pvt->pvt;
 	p0 = pr0->get_rtp_info(c0);
+	if (pr0->get_vrtp_info)
+		vp0 = pr0->get_vrtp_info(c0);
+	else
+		vp0 = NULL;
 	p1 = pr1->get_rtp_info(c1);
+	if (pr1->get_vrtp_info)
+		vp1 = pr1->get_vrtp_info(c1);
+	else
+		vp1 = NULL;
 	if (!p0 || !p1) {
 		/* Somebody doesn't want to play... */
 		ast_pthread_mutex_unlock(&c0->lock);
 		ast_pthread_mutex_unlock(&c1->lock);
 		return -2;
 	}
-	if (pr0->set_rtp_peer(c0, p1)) 
+	if (pr0->set_rtp_peer(c0, p1, vp1)) 
 		ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name);
 	else {
 		/* Store RTP peer */
 		ast_rtp_get_peer(p1, &ac1);
+		if (vp1)
+			ast_rtp_get_peer(p1, &vac1);
 	}
-	if (pr1->set_rtp_peer(c1, p0))
+	if (pr1->set_rtp_peer(c1, p0, vp0))
 		ast_log(LOG_WARNING, "Channel '%s' failed to talk back to '%s'\n", c1->name, c0->name);
 	else {
 		/* Store RTP peer */
 		ast_rtp_get_peer(p0, &ac0);
+		if (vp0)
+			ast_rtp_get_peer(p0, &vac0);
 	}
 	ast_pthread_mutex_unlock(&c0->lock);
 	ast_pthread_mutex_unlock(&c1->lock);
@@ -1030,11 +1228,11 @@ int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, st
 			(c0->masq || c0->masqr || c1->masq || c1->masqr)) {
 				ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
 				if (c0->pvt->pvt == pvt0) {
-					if (pr0->set_rtp_peer(c0, NULL)) 
+					if (pr0->set_rtp_peer(c0, NULL, NULL)) 
 						ast_log(LOG_WARNING, "Channel '%s' failed to revert\n", c0->name);
 				}
 				if (c1->pvt->pvt == pvt1) {
-					if (pr1->set_rtp_peer(c1, NULL)) 
+					if (pr1->set_rtp_peer(c1, NULL, NULL)) 
 						ast_log(LOG_WARNING, "Channel '%s' failed to revert back\n", c1->name);
 				}
 				/* Tell it to try again later */
@@ -1042,18 +1240,23 @@ int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, st
 		}
 		to = -1;
 		ast_rtp_get_peer(p1, &t1);
-		ast_rtp_get_peer(p0, &t0);
-		if (inaddrcmp(&t1, &ac1)) {
+		if (vp1)
+			ast_rtp_get_peer(vp1, &vt1);
+		if (vp0)
+			ast_rtp_get_peer(vp0, &vt0);
+		if (inaddrcmp(&t1, &ac1) || (vp1 && inaddrcmp(&vt1, &vac1))) {
 			ast_log(LOG_DEBUG, "Oooh, '%s' changed end address\n", c1->name);
-			if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL)) 
+			if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL, vt1.sin_addr.s_addr ? vp1 : NULL)) 
 				ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c0->name, c1->name);
 			memcpy(&ac1, &t1, sizeof(ac1));
+			memcpy(&vac1, &vt1, sizeof(vac1));
 		}
-		if (inaddrcmp(&t0, &ac0)) {
+		if (inaddrcmp(&t0, &ac0) || (vp0 && inaddrcmp(&vt0, &vac0))) {
 			ast_log(LOG_DEBUG, "Oooh, '%s' changed end address\n", c0->name);
-			if (pr1->set_rtp_peer(c1, t0.sin_addr.s_addr ? p0 : NULL))
+			if (pr1->set_rtp_peer(c1, t0.sin_addr.s_addr ? p0 : NULL, vt0.sin_addr.s_addr ? vp0 : NULL))
 				ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c1->name, c0->name);
 			memcpy(&ac0, &t0, sizeof(ac0));
+			memcpy(&vac0, &vt0, sizeof(vac0));
 		}
 		who = ast_waitfor_n(cs, 2, &to);
 		if (!who) {
@@ -1068,11 +1271,11 @@ int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, st
 			*rc = who;
 			ast_log(LOG_DEBUG, "Oooh, got a %s\n", f ? "digit" : "hangup");
 			if ((c0->pvt->pvt == pvt0) && (!c0->_softhangup)) {
-				if (pr0->set_rtp_peer(c0, NULL)) 
+				if (pr0->set_rtp_peer(c0, NULL, NULL)) 
 					ast_log(LOG_WARNING, "Channel '%s' failed to revert\n", c0->name);
 			}
 			if ((c1->pvt->pvt == pvt1) && (!c1->_softhangup)) {
-				if (pr1->set_rtp_peer(c1, NULL)) 
+				if (pr1->set_rtp_peer(c1, NULL, NULL)) 
 					ast_log(LOG_WARNING, "Channel '%s' failed to revert back\n", c1->name);
 			}
 			/* That's all we needed */
-- 
GitLab