Skip to content
Snippets Groups Projects
rtp.c 34.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    			ast_rtp_raw_write(rtp, f, codec);
    		break;
    	case AST_FORMAT_G729A:
    		if (!rtp->smoother) {
    			rtp->smoother = ast_smoother_new(20);
    		}
    		if (!rtp->smoother) {
    			ast_log(LOG_WARNING, "Unable to create g729 smoother :(\n");
    			return -1;
    		}
    		ast_smoother_feed(rtp->smoother, _f);
    		
    		while((f = ast_smoother_read(rtp->smoother)))
    			ast_rtp_raw_write(rtp, f, codec);
    		break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case AST_FORMAT_GSM:
    		if (!rtp->smoother) {
    			rtp->smoother = ast_smoother_new(33);
    		}
    		if (!rtp->smoother) {
    			ast_log(LOG_WARNING, "Unable to create GSM smoother :(\n");
    			return -1;
    		}
    		ast_smoother_feed(rtp->smoother, _f);
    		while((f = ast_smoother_read(rtp->smoother)))
    			ast_rtp_raw_write(rtp, f, codec);
    		break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	case AST_FORMAT_ILBC:
    		if (!rtp->smoother) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			rtp->smoother = ast_smoother_new(50);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    		if (!rtp->smoother) {
    			ast_log(LOG_WARNING, "Unable to create ILBC smoother :(\n");
    			return -1;
    		}
    		ast_smoother_feed(rtp->smoother, _f);
    		while((f = ast_smoother_read(rtp->smoother)))
    			ast_rtp_raw_write(rtp, f, codec);
    		break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	default:	
    
    		ast_log(LOG_WARNING, "Not sure about sending format %d packets\n", 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:
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (_f->offset < hdrlen) {
    			f = ast_frdup(_f);
    		} else {
    			f = _f;
    		}
    		ast_rtp_raw_write(rtp, f, codec);
    	}
    		
    	return 0;
    }
    
    
    void ast_rtp_proto_unregister(struct ast_rtp_protocol *proto)
    {
    	struct ast_rtp_protocol *cur, *prev;
    	cur = protos;
    	prev = NULL;
    	while(cur) {
    		if (cur == proto) {
    			if (prev)
    				prev->next = proto->next;
    			else
    				protos = proto->next;
    			return;
    		}
    		prev = cur;
    		cur = cur->next;
    	}
    }
    
    int ast_rtp_proto_register(struct ast_rtp_protocol *proto)
    {
    	struct ast_rtp_protocol *cur;
    	cur = protos;
    	while(cur) {
    		if (cur->type == proto->type) {
    			ast_log(LOG_WARNING, "Tried to register same protocol '%s' twice\n", cur->type);
    			return -1;
    		}
    		cur = cur->next;
    	}
    	proto->next = protos;
    	protos = proto;
    	return 0;
    }
    
    static struct ast_rtp_protocol *get_proto(struct ast_channel *chan)
    {
    	struct ast_rtp_protocol *cur;
    	cur = protos;
    	while(cur) {
    		if (cur->type == chan->type) {
    			return cur;
    		}
    		cur = cur->next;
    	}
    	return NULL;
    }
    
    int ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc)
    {
    	struct ast_frame *f;
    	struct ast_channel *who, *cs[3];
    	struct ast_rtp *p0, *p1;
    
    	struct ast_rtp_protocol *pr0, *pr1;
    
    	struct sockaddr_in ac0, ac1;
    
    	struct sockaddr_in vac0, vac1;
    
    	struct sockaddr_in t0, t1;
    
    	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);
    	ast_autoservice_start(c1);
    	usleep(500000);
    	ast_autoservice_stop(c0);
    	ast_autoservice_stop(c1);
    
    	/* if need DTMF, cant native bridge */
    	if (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))
    		return -2;
    	ast_pthread_mutex_lock(&c0->lock);
    	ast_pthread_mutex_lock(&c1->lock);
    	pr0 = get_proto(c0);
    	pr1 = get_proto(c1);
    	if (!pr0) {
    		ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name);
    		ast_pthread_mutex_unlock(&c0->lock);
    		ast_pthread_mutex_unlock(&c1->lock);
    		return -1;
    	}
    	if (!pr1) {
    		ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name);
    		ast_pthread_mutex_unlock(&c0->lock);
    		ast_pthread_mutex_unlock(&c1->lock);
    		return -1;
    	}
    	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, 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, 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);
    	cs[0] = c0;
    	cs[1] = c1;
    	cs[2] = NULL;
    	for (;;) {
    		if ((c0->pvt->pvt != pvt0)  ||
    			(c1->pvt->pvt != pvt1) ||
    			(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, 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, NULL)) 
    
    						ast_log(LOG_WARNING, "Channel '%s' failed to revert back\n", c1->name);
    				}
    				/* Tell it to try again later */
    				return -3;
    		}
    		to = -1;
    
    		ast_rtp_get_peer(p1, &t1);
    
    		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, 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) || (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, 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) {
    			ast_log(LOG_DEBUG, "Ooh, empty read...\n");
    			continue;
    		}
    		f = ast_read(who);
    		if (!f || ((f->frametype == AST_FRAME_DTMF) &&
    				   (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) || 
    			       ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) {
    			*fo = f;
    			*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, 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, NULL)) 
    
    					ast_log(LOG_WARNING, "Channel '%s' failed to revert back\n", c1->name);
    			}
    			/* That's all we needed */
    			return 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if ((f->frametype == AST_FRAME_DTMF) || 
    				(f->frametype == AST_FRAME_VOICE) || 
    				(f->frametype == AST_FRAME_VIDEO)) {
    
    				/* Forward voice or DTMF frames if they happen upon us */
    				if (who == c0) {
    					ast_write(c1, f);
    				} else if (who == c1) {
    					ast_write(c0, f);
    				}
    			}
    
    			ast_frfree(f);
    
    		/* Swap priority not that it's a big deal at this point */
    		cs[2] = cs[0];
    		cs[0] = cs[1];
    		cs[1] = cs[2];
    		
    	}
    	return -1;
    }
    
    
    void ast_rtp_reload(void)
    {
    	struct ast_config *cfg;
    	char *s;
    	rtpstart = 5000;
    	rtpend = 31000;
    	cfg = ast_load("rtp.conf");
    	if (cfg) {
    		if ((s = ast_variable_retrieve(cfg, "general", "rtpstart"))) {
    			rtpstart = atoi(s);
    			if (rtpstart < 1024)
    				rtpstart = 1024;
    			if (rtpstart > 65535)
    				rtpstart = 65535;
    		}
    		if ((s = ast_variable_retrieve(cfg, "general", "rtpend"))) {
    			rtpend = atoi(s);
    			if (rtpend < 1024)
    				rtpend = 1024;
    			if (rtpend > 65535)
    				rtpend = 65535;
    		}
    		ast_destroy(cfg);
    	}
    	if (rtpstart >= rtpend) {
    		ast_log(LOG_WARNING, "Unreasonable values for RTP start/end\n");
    		rtpstart = 5000;
    		rtpend = 31000;
    	}
    	if (option_verbose > 1)
    		ast_verbose(VERBOSE_PREFIX_2 "RTP Allocating from port range %d -> %d\n", rtpstart, rtpend);
    }
    
    void ast_rtp_init(void)
    {
    	ast_rtp_reload();
    }