Skip to content
Snippets Groups Projects
channel.c 96.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • 				else 
    					ast_write(c0, f);
    			}
    		}
    		ast_frfree(f);
    
    		/* Swap who gets priority */
    		cs[2] = cs[0];
    		cs[0] = cs[1];
    		cs[1] = cs[2];
    	}
    	return res;
    }
    
    
    /*--- ast_channel_bridge: Bridge two channels together */
    
    int ast_channel_bridge(struct ast_channel *c0, struct ast_channel *c1, struct ast_bridge_config *config, struct ast_frame **fo, struct ast_channel **rc) 
    {
    	/* Copy voice back and forth between the two channels.	Give the peer
    	   the ability to transfer calls with '#<extension' syntax. */
    	struct ast_channel *cs[3];
    	struct ast_channel *who = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int nativefailed=0;
    
    	int firstpass;
    
    	int o0nativeformats;
    	int o1nativeformats;
    
    	long elapsed_ms=0, time_left_ms=0;
    	int playit=0, playitagain=1, first_time=1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	*fo = NULL;
    
    	firstpass = config->firstpass;
    	config->firstpass = 0;
    
    
    	if ((ast_test_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING)) && config->start_sound && firstpass)
    
    		bridge_playfile(c0,c1,config->start_sound,time_left_ms / 1000);
    
    	if ((ast_test_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING)) && config->start_sound && firstpass)
    
    		bridge_playfile(c1,c0,config->start_sound,time_left_ms / 1000);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	/* Stop if we're a zombie or need a soft hangup */
    
    	if (ast_test_flag(c0, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) || ast_test_flag(c1, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c1)) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "%s is already in a bridge with %s\n", 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "%s is already in a bridge with %s\n", 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return -1;
    	}
    	
    	/* Keep track of bridge */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	cs[0] = c0;
    	cs[1] = c1;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	manager_event(EVENT_FLAG_CALL, "Link", 
    			"Channel1: %s\r\n"
    
    			"Uniqueid2: %s\r\n"
                            "CallerID1: %s\r\n"
    			"CallerID2: %s\r\n",
    			c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
                                                                            
    
    	o1nativeformats = c1->nativeformats;
    	o0nativeformats = c0->nativeformats;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (/* ever */;;) {
    
    		if (config->timelimit) {
    
    			elapsed_ms = ast_tvdiff_ms(ast_tvnow(), start_time);
    
    			time_left_ms = config->timelimit - elapsed_ms;
    
    
    			if (playitagain && ((ast_test_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING)) || (ast_test_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING))) && (config->play_warning && time_left_ms <= config->play_warning)) { 
    
    				/* narrowing down to the end */
    
    				if (config->warning_freq == 0) {
    
    					playit = 1;
    					first_time=0;
    					playitagain=0;
    
    				} else if (first_time) {
    
    				} else {
    					if ((time_left_ms % config->warning_freq) <= 50) {
    
    			if (time_left_ms <= 0) {
    
    				if ((ast_test_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING)) && config->end_sound)
    
    					bridge_playfile(c0,c1,config->end_sound,0);
    
    				if ((ast_test_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING)) && config->end_sound)
    
    					bridge_playfile(c1,c0,config->end_sound,0);
    
    Anthony Minessale II's avatar
    Anthony Minessale II committed
    				*fo = NULL;
    
    Anthony Minessale II's avatar
    Anthony Minessale II committed
    				res = 0;
    
    			if (time_left_ms >= 5000 && playit) {
    
    				if ((ast_test_flag(&(config->features_caller), AST_FEATURE_PLAY_WARNING)) && config->warning_sound && config->play_warning)
    
    					bridge_playfile(c0,c1,config->warning_sound,time_left_ms / 1000);
    
    				if ((ast_test_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING)) && config->warning_sound && config->play_warning)
    
    					bridge_playfile(c1,c0,config->warning_sound,time_left_ms / 1000);
    
    
    		if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) {
    			if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
    				c0->_softhangup = 0;
    			if (c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE)
    				c1->_softhangup = 0;
    			c0->_bridge = c1;
    			c1->_bridge = c0;
    
    			ast_log(LOG_DEBUG, "Unbridge signal received. Ending native bridge.\n");
    
    Mark Spencer's avatar
    Mark Spencer committed
    		/* Stop if we're a zombie or need a soft hangup */
    
    		if (ast_test_flag(c0, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c0) || ast_test_flag(c1, AST_FLAG_ZOMBIE) || ast_check_hangup_locked(c1)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			*fo = NULL;
    
    Mark Spencer's avatar
    Mark Spencer committed
    			res = 0;
    
    			ast_log(LOG_DEBUG, "Bridge stops because we're zombie or need a soft hangup: c0=%s, c1=%s, flags: %s,%s,%s,%s\n",c0->name,c1->name,ast_test_flag(c0, AST_FLAG_ZOMBIE)?"Yes":"No",ast_check_hangup(c0)?"Yes":"No",ast_test_flag(c1, AST_FLAG_ZOMBIE)?"Yes":"No",ast_check_hangup(c1)?"Yes":"No");
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    		}
    
    		if (c0->tech->bridge && config->timelimit==0 &&
    
    			(c0->tech->bridge == c1->tech->bridge) && !nativefailed && !c0->monitor && !c1->monitor && !c0->spiers && !c1->spiers) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				/* Looks like they share a bridge code */
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (option_verbose > 2) 
    				ast_verbose(VERBOSE_PREFIX_3 "Attempting native bridge of %s and %s\n", c0->name, c1->name);
    
    			ast_set_flag(c0, AST_FLAG_NBRIDGE);
    			ast_set_flag(c1, AST_FLAG_NBRIDGE);
    
    			if (!(res = c0->tech->bridge(c0, c1, config->flags, fo, rc))) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				manager_event(EVENT_FLAG_CALL, "Unlink", 
    					"Channel1: %s\r\n"
    
    					"Uniqueid2: %s\r\n"
                                            "CallerID1: %s\r\n"
                                            "CallerID2: %s\r\n",
    					c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
    
    				ast_log(LOG_DEBUG, "Returning from native bridge, channels: %s, %s\n",c0->name ,c1->name);
    
    				ast_clear_flag(c0, AST_FLAG_NBRIDGE);
    				ast_clear_flag(c1, AST_FLAG_NBRIDGE);
    				if (c0->_softhangup == AST_SOFTHANGUP_UNBRIDGE || c1->_softhangup == AST_SOFTHANGUP_UNBRIDGE) {
    					c0->_bridge = c1;
    					c1->_bridge = c0;
    					continue;
    				}
    				else 
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return 0;
    
    			} else {
    				ast_clear_flag(c0, AST_FLAG_NBRIDGE);
    				ast_clear_flag(c1, AST_FLAG_NBRIDGE);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			/* If they return non-zero then continue on normally.  Let "-2" mean don't worry about
    			   my not wanting to bridge */
    			if ((res != -2) && (res != -3))
    				ast_log(LOG_WARNING, "Private bridge between %s and %s failed\n", c0->name, c1->name);
    
    			if (res != -3)
    				nativefailed++;
    
    		if (((c0->writeformat != c1->readformat) || (c0->readformat != c1->writeformat) || (c0->nativeformats != o0nativeformats) || (c1->nativeformats != o1nativeformats)) &&
    
    Mark Spencer's avatar
    Mark Spencer committed
    			!(c0->generator || c1->generator))  {
    
    Mark Spencer's avatar
    Mark Spencer committed
    			if (ast_channel_make_compatible(c0, c1)) {
    				ast_log(LOG_WARNING, "Can't make %s and %s compatible\n", c0->name, c1->name);
    
                                    manager_event(EVENT_FLAG_CALL, "Unlink",
                                            "Channel1: %s\r\n"
                                            "Channel2: %s\r\n"
                                            "Uniqueid1: %s\r\n"
                                            "Uniqueid2: %s\r\n"
                                            "CallerID1: %s\r\n"
                                            "CallerID2: %s\r\n",
                                            c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
    
    Mark Spencer's avatar
    Mark Spencer committed
    				return -1;
    			}
    
    			o0nativeformats = c0->nativeformats;
    			o1nativeformats = c1->nativeformats;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		res = ast_generic_bridge(&playitagain, &playit, &start_time, c0, c1, config, fo, rc);
    		if (res != -3)
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	manager_event(EVENT_FLAG_CALL, "Unlink",
    
    Mark Spencer's avatar
    Mark Spencer committed
    					"Channel1: %s\r\n"
    
    					"Uniqueid2: %s\r\n"
    					"CallerID1: %s\r\n"
    					"CallerID2: %s\r\n",
    					c0->name, c1->name, c0->uniqueid, c1->uniqueid, c0->cid.cid_num, c1->cid.cid_num);
    
    	ast_log(LOG_DEBUG, "Bridge stops bridging channels %s and %s\n",c0->name,c1->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return res;
    }
    
    
    /*--- ast_channel_setoption: Sets an option on a channel */
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_channel_setoption(struct ast_channel *chan, int option, void *data, int datalen, int block)
    {
    	int res;
    
    	if (chan->tech->setoption) {
    		res = chan->tech->setoption(chan, option, data, datalen);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (res < 0)
    			return res;
    	} else {
    		errno = ENOSYS;
    		return -1;
    	}
    	if (block) {
    		/* XXX Implement blocking -- just wait for our option frame reply, discarding
    		   intermediate packets. XXX */
    		ast_log(LOG_ERROR, "XXX Blocking not implemented yet XXX\n");
    		return -1;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    Mark Spencer's avatar
    Mark Spencer committed
    struct tonepair_def {
    	int freq1;
    	int freq2;
    	int duration;
    	int vol;
    };
    
    struct tonepair_state {
    	float freq1;
    	float freq2;
    	float vol;
    	int duration;
    	int pos;
    	int origwfmt;
    	struct ast_frame f;
    	unsigned char offset[AST_FRIENDLY_OFFSET];
    	short data[4000];
    };
    
    static void tonepair_release(struct ast_channel *chan, void *params)
    {
    	struct tonepair_state *ts = params;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (chan) {
    
    		ast_set_write_format(chan, ts->origwfmt);
    
    static void *tonepair_alloc(struct ast_channel *chan, void *params)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct tonepair_state *ts;
    	struct tonepair_def *td = params;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ts = malloc(sizeof(struct tonepair_state));
    	if (!ts)
    		return NULL;
    	memset(ts, 0, sizeof(struct tonepair_state));
    	ts->origwfmt = chan->writeformat;
    
    	if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name);
    		tonepair_release(NULL, ts);
    		ts = NULL;
    	} else {
    		ts->freq1 = td->freq1;
    		ts->freq2 = td->freq2;
    		ts->duration = td->duration;
    		ts->vol = td->vol;
    	}
    	/* Let interrupts interrupt :) */
    
    	ast_set_flag(chan, AST_FLAG_WRITE_INT);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return ts;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int tonepair_generator(struct ast_channel *chan, void *data, int len, int samples)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    	struct tonepair_state *ts = data;
    	int x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* we need to prepare a frame with 16 * timelen samples as we're 
    	 * generating SLIN audio
    	 */
    	len = samples * 2;
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (len > sizeof(ts->data) / 2 - 1) {
    		ast_log(LOG_WARNING, "Can't generate that much data!\n");
    		return -1;
    	}
    	memset(&ts->f, 0, sizeof(ts->f));
    
    	for (x = 0; x < (len / 2); x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ts->data[x] = ts->vol * (
    				sin((ts->freq1 * 2.0 * M_PI / 8000.0) * (ts->pos + x)) +
    				sin((ts->freq2 * 2.0 * M_PI / 8000.0) * (ts->pos + x))
    			);
    	}
    	ts->f.frametype = AST_FRAME_VOICE;
    	ts->f.subclass = AST_FORMAT_SLINEAR;
    	ts->f.datalen = len;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ts->f.samples = samples;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	ts->f.offset = AST_FRIENDLY_OFFSET;
    	ts->f.data = ts->data;
    	ast_write(chan, &ts->f);
    	ts->pos += x;
    	if (ts->duration > 0) {
    		if (ts->pos >= ts->duration * 8)
    			return -1;
    	}
    	return 0;
    }
    
    static struct ast_generator tonepair = {
    	alloc: tonepair_alloc,
    	release: tonepair_release,
    	generate: tonepair_generator,
    };
    
    int ast_tonepair_start(struct ast_channel *chan, int freq1, int freq2, int duration, int vol)
    {
    	struct tonepair_def d = { 0, };
    
    Mark Spencer's avatar
    Mark Spencer committed
    	d.freq1 = freq1;
    	d.freq2 = freq2;
    	d.duration = duration;
    	if (vol < 1)
    		d.vol = 8192;
    	else
    		d.vol = vol;
    	if (ast_activate_generator(chan, &tonepair, &d))
    		return -1;
    	return 0;
    }
    
    void ast_tonepair_stop(struct ast_channel *chan)
    {
    	ast_deactivate_generator(chan);
    }
    
    int ast_tonepair(struct ast_channel *chan, int freq1, int freq2, int duration, int vol)
    {
    	struct ast_frame *f;
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if ((res = ast_tonepair_start(chan, freq1, freq2, duration, vol)))
    		return res;
    
    	/* Give us some wiggle room */
    	while(chan->generatordata && (ast_waitfor(chan, 100) >= 0)) {
    		f = ast_read(chan);
    		if (f)
    			ast_frfree(f);
    		else
    			return -1;
    	}
    	return 0;
    }
    
    ast_group_t ast_get_group(char *s)
    
    {
    	char *copy;
    	char *piece;
    	char *c=NULL;
    
    	int start=0, finish=0, x;
    
    	ast_group_t group = 0;
    
    	copy = ast_strdupa(s);
    	if (!copy) {
    		ast_log(LOG_ERROR, "Out of memory\n");
    		return 0;
    	}
    	c = copy;
    	
    	while((piece = strsep(&c, ","))) {
    		if (sscanf(piece, "%d-%d", &start, &finish) == 2) {
    			/* Range */
    		} else if (sscanf(piece, "%d", &start)) {
    			/* Just one */
    			finish = start;
    		} else {
    
    			ast_log(LOG_ERROR, "Syntax error parsing group configuration '%s' at '%s'. Ignoring.\n", s, piece);
    
    		for (x = start; x <= finish; x++) {
    
    			if ((x > 63) || (x < 0)) {
    				ast_log(LOG_WARNING, "Ignoring invalid group %d (maximum group is 63)\n", x);
    
    				group |= ((ast_group_t) 1 << x);
    
    
    static int (*ast_moh_start_ptr)(struct ast_channel *, char *) = NULL;
    static void (*ast_moh_stop_ptr)(struct ast_channel *) = NULL;
    
    static void (*ast_moh_cleanup_ptr)(struct ast_channel *) = NULL;
    
    
    
    void ast_install_music_functions(int (*start_ptr)(struct ast_channel *, char *),
    
    								 void (*stop_ptr)(struct ast_channel *),
    								 void (*cleanup_ptr)(struct ast_channel *)
    								 ) 
    
    {
    	ast_moh_start_ptr = start_ptr;
    	ast_moh_stop_ptr = stop_ptr;
    
    }
    
    void ast_uninstall_music_functions(void) 
    {
    	ast_moh_start_ptr = NULL;
    	ast_moh_stop_ptr = NULL;
    
    /*! Turn on music on hold on a given channel */
    
    int ast_moh_start(struct ast_channel *chan, char *mclass) 
    {
    
    	if (ast_moh_start_ptr)
    
    		return ast_moh_start_ptr(chan, mclass);
    
    	if (option_verbose > 2)
    		ast_verbose(VERBOSE_PREFIX_3 "Music class %s requested but no musiconhold loaded.\n", mclass ? mclass : "default");
    	
    	return 0;
    }
    
    
    /*! Turn off music on hold on a given channel */
    
    void ast_moh_stop(struct ast_channel *chan) 
    {
    	if(ast_moh_stop_ptr)
    		ast_moh_stop_ptr(chan);
    }
    
    
    void ast_moh_cleanup(struct ast_channel *chan) 
    {
    	if(ast_moh_cleanup_ptr)
            ast_moh_cleanup_ptr(chan);
    }
    
    
    void ast_channels_init(void)
    {
    	ast_cli_register(&cli_show_channeltypes);
    }
    
    
    /*--- ast_print_group: Print call group and pickup group ---*/
    char *ast_print_group(char *buf, int buflen, ast_group_t group) 
    {
    	unsigned int i;
    	int first=1;
    	char num[3];
    
    	buf[0] = '\0';
    	
    	if (!group)	/* Return empty string if no group */
    		return(buf);
    
    	for (i=0; i<=63; i++) {	/* Max group is 63 */
    
    		if (group & ((ast_group_t) 1 << i)) {
    
    	   		if (!first) {
    				strncat(buf, ", ", buflen);
    			} else {
    				first=0;
    	  		}
    			snprintf(num, sizeof(num), "%u", i);
    			strncat(buf, num, buflen);
    		}
    	}
    	return(buf);
    }
    
    
    void ast_set_variables(struct ast_channel *chan, struct ast_variable *vars)
    {
    	struct ast_variable *cur;
    
    	for (cur = vars; cur; cur = cur->next)
    		pbx_builtin_setvar_helper(chan, cur->name, cur->value);	
    }