mirror of
				https://github.com/asterisk/asterisk.git
				synced 2025-10-31 02:37:10 +00:00 
			
		
		
		
	Various contributed VPB fixes
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@865 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
		
							
								
								
									
										1
									
								
								CREDITS
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								CREDITS
									
									
									
									
									
								
							| @@ -11,6 +11,7 @@ Telesthetic - for supporting SIP development | ||||
|  | ||||
| Christos Ricudis - for substantial code contributions | ||||
|  | ||||
| Paul Bagyenda, Digital Solutions - for initial Voicetronix driver development | ||||
| === WISHLIST CONTRIBUTERS === | ||||
| Jeremy McNamara - SpeeX support | ||||
| Nick Seraphin - RDNIS support | ||||
|   | ||||
| @@ -94,10 +94,10 @@ chan_nbs.so: chan_nbs.o | ||||
| 	$(CC) -shared -Xlinker -x -o $@ $< -lnbs | ||||
|  | ||||
| chan_vpb.o: chan_vpb.c | ||||
| 	$(CC) -c $(CFLAGS) -o $@ chan_vpb.c | ||||
| 	$(CXX) -c $(CFLAGS) -o $@ chan_vpb.c | ||||
|  | ||||
| chan_vpb.so: chan_vpb.o | ||||
| 	 $(CCC) -shared -Xlinker -x -o $@ $< -lvpb -lpthread -lm -ldl | ||||
| 	 $(CXX) -shared -Xlinker -x -o $@ $< -lvpb -lpthread -lm -ldl | ||||
|  | ||||
| #chan_modem.so : chan_modem.o | ||||
| #	$(CC) -rdynamic -shared -Xlinker -x -o $@ $< | ||||
|   | ||||
| @@ -35,8 +35,8 @@ | ||||
|  | ||||
|  | ||||
| #define DEFAULT_GAIN 1.0 | ||||
| #define VPB_SAMPLES 160 /* 20ms for recording and playback.*/ | ||||
| #define VPB_MAX_BUF VPB_SAMPLES*4 | ||||
| #define VPB_SAMPLES 240  | ||||
| #define VPB_MAX_BUF VPB_SAMPLES*4 + AST_FRIENDLY_OFFSET | ||||
|  | ||||
| #define VPB_NULL_EVENT 200 | ||||
|  | ||||
| @@ -102,6 +102,19 @@ static VPB_TONE Busytone     = {425,   0,   0, -10,  -100, -100,   500,  500}; | ||||
| static VPB_TONE Ringbacktone = {425,   0,   0, -10,  -100, -100,  1000, 3000}; | ||||
| #endif | ||||
|  | ||||
| #define VPB_MAX_BRIDGES 128  | ||||
| static struct vpb_bridge_t { | ||||
|      int inuse; | ||||
|      struct ast_channel *c0, *c1, **rc; | ||||
|      struct ast_frame **fo; | ||||
|      int flags; | ||||
|       | ||||
|      pthread_mutex_t lock; | ||||
|      pthread_cond_t cond; | ||||
| } bridges[VPB_MAX_BRIDGES]; /* Bridges...*/ | ||||
|  | ||||
| static pthread_mutex_t bridge_lock = AST_MUTEX_INITIALIZER; | ||||
|  | ||||
| static struct vpb_pvt { | ||||
|  | ||||
|      struct ast_channel *owner;		/* Channel we belong to, possibly NULL */ | ||||
| @@ -110,6 +123,7 @@ static struct vpb_pvt { | ||||
|  | ||||
|      char dev[256]; | ||||
|  | ||||
|      struct ast_frame f, fr; | ||||
|      char buf[VPB_MAX_BUF];			/* Static buffer for reading frames */ | ||||
|  | ||||
|      int dialtone; | ||||
| @@ -126,10 +140,11 @@ static struct vpb_pvt { | ||||
|      int lastinput; | ||||
|      int lastoutput; | ||||
|  | ||||
|      | ||||
|      struct vpb_bridge_t *bridge; | ||||
|       | ||||
|      pthread_mutex_t lock; | ||||
|  | ||||
|      int hangup; | ||||
|     int stopreads; /* Stop reading...*/ | ||||
|      pthread_t readthread;    /* For monitoring read channel. One per owned channel. */ | ||||
|  | ||||
|      struct vpb_pvt *next;			/* Next channel in list */ | ||||
| @@ -137,10 +152,90 @@ static struct vpb_pvt { | ||||
|  | ||||
| static char callerid[AST_MAX_EXTENSION]; | ||||
|  | ||||
| static int vpb_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc) | ||||
| { | ||||
|      struct vpb_pvt *p0 = (struct vpb_pvt *)c0->pvt->pvt; | ||||
|      struct vpb_pvt *p1 = (struct vpb_pvt *)c1->pvt->pvt; | ||||
|      int i, len = sizeof bridges/sizeof bridges[0], res; | ||||
|       | ||||
|      /* Bridge channels, check if we can.  I believe we always can, so find a slot.*/ | ||||
|       | ||||
|      ast_pthread_mutex_lock(&bridge_lock); { | ||||
| 	  for (i = 0; i < len; i++)  | ||||
| 	       if (!bridges[i].inuse) | ||||
| 		    break; | ||||
| 	  if (i < len) { | ||||
| 	       bridges[i].inuse = 1; | ||||
| 	       bridges[i].flags = flags; | ||||
| 	       bridges[i].rc = rc; | ||||
| 	       bridges[i].fo = fo; | ||||
| 	       bridges[i].c0 = c0; | ||||
| 	       bridges[i].c1 = c1; | ||||
| 	       pthread_mutex_init(&bridges[i].lock, NULL); | ||||
| 	       pthread_cond_init(&bridges[i].cond, NULL); | ||||
| 	  } 	        | ||||
|      } ast_pthread_mutex_unlock(&bridge_lock);  | ||||
|  | ||||
|      if (i == len) { | ||||
| 	  ast_log(LOG_WARNING, "Failed to bridge %s and %s!\n", c0->name, c1->name); | ||||
| 	  return -2; | ||||
|      } else { | ||||
|  | ||||
| 	  /* Set bridge pointers. You don't want to take these locks while holding bridge lock.*/ | ||||
| 	  ast_pthread_mutex_lock(&p0->lock); { | ||||
| 	       p0->bridge = &bridges[i]; | ||||
| 	  } ast_pthread_mutex_unlock(&p0->lock); | ||||
|  | ||||
| 	  ast_pthread_mutex_lock(&p1->lock); { | ||||
| 	       p1->bridge = &bridges[i]; | ||||
| 	  } ast_pthread_mutex_unlock(&p1->lock); | ||||
|  | ||||
| 	  if (option_verbose > 1)  | ||||
| 	       ast_verbose(VERBOSE_PREFIX_3  | ||||
| 			   " Bridging call entered with [%s, %s]\n", c0->name, c1->name); | ||||
|      } | ||||
|      res = vpb_bridge(p0->handle, p1->handle, VPB_BRIDGE_ON, 0); | ||||
|  | ||||
|      if (res != VPB_OK)  | ||||
| 	  goto done; | ||||
|  | ||||
|      res = pthread_cond_wait(&bridges[i].cond, &bridges[i].lock); /* Wait for condition signal. */ | ||||
|       | ||||
|       | ||||
|  done: /* Out of wait. */ | ||||
|  | ||||
|      vpb_bridge(p0->handle, p1->handle, VPB_BRIDGE_OFF, 0);  | ||||
|  | ||||
|       | ||||
|      ast_pthread_mutex_lock(&bridge_lock); { | ||||
| 	  bridges[i].inuse = 0; | ||||
| 	  pthread_mutex_destroy(&bridges[i].lock); | ||||
| 	  pthread_cond_destroy(&bridges[i].cond);	 | ||||
|      } ast_pthread_mutex_unlock(&bridge_lock);  | ||||
|       | ||||
|      ast_pthread_mutex_lock(&p0->lock); { | ||||
| 	  p0->bridge = NULL; | ||||
|      } ast_pthread_mutex_unlock(&p0->lock); | ||||
|       | ||||
|      ast_pthread_mutex_lock(&p1->lock); { | ||||
| 	  p1->bridge = NULL; | ||||
|      } ast_pthread_mutex_unlock(&p1->lock); | ||||
|  | ||||
|       | ||||
|      if (option_verbose > 2)  | ||||
| 	  ast_verbose(VERBOSE_PREFIX_3  | ||||
| 		      " Bridging call done with [%s, %s] => %d\n", c0->name, c1->name, res); | ||||
|      | ||||
|      if (res != 0 && res != VPB_OK) /* Don't assume VPB_OK is zero! */ | ||||
| 	  return -1; | ||||
|      else  | ||||
| 	  return 0; | ||||
| } | ||||
|  | ||||
| static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e) | ||||
| { | ||||
|      struct ast_frame f = {AST_FRAME_CONTROL}; /* default is control, Clear rest. */ | ||||
|      int endbridge = 0; | ||||
|  | ||||
|      if (option_verbose > 4)  | ||||
| 	  ast_verbose(VERBOSE_PREFIX_3 " %s handle_owned got event: [%d=>%d]\n", | ||||
| @@ -201,8 +296,49 @@ static inline int monitor_handle_owned(struct vpb_pvt *p, VPB_EVENT *e) | ||||
|      } | ||||
|  | ||||
|      if (option_verbose > 2)  | ||||
| 	  ast_verbose(VERBOSE_PREFIX_3 " handle_owned: putting frame: [%d=>%d]\n", | ||||
| 		      f.frametype, f.subclass); | ||||
| 	  ast_verbose(VERBOSE_PREFIX_3 " handle_owned: putting frame: [%d=>%d], bridge=%p\n", | ||||
| 		      f.frametype, f.subclass, (void *)p->bridge); | ||||
|  | ||||
|      ast_pthread_mutex_lock(&p->lock); { | ||||
| 	  if (p->bridge) { /* Check what happened, see if we need to report it. */ | ||||
| 	       switch (f.frametype) {  | ||||
| 	       case AST_FRAME_DTMF: | ||||
| 		    if (!(p->bridge->c0 == p->owner &&  | ||||
| 			  (p->bridge->flags & AST_BRIDGE_DTMF_CHANNEL_0) ) && | ||||
| 			!(p->bridge->c1 == p->owner &&  | ||||
| 			  (p->bridge->flags & AST_BRIDGE_DTMF_CHANNEL_1) ))  | ||||
| 			 /* Kill bridge, this is interesting. */ | ||||
| 			 endbridge = 1; | ||||
| 		    break; | ||||
| 		     | ||||
| 	       case AST_FRAME_CONTROL: | ||||
| 		    if (!(p->bridge->flags & AST_BRIDGE_IGNORE_SIGS))  | ||||
| #if 0 | ||||
| 			 if (f.subclass == AST_CONTROL_BUSY || | ||||
| 			     f.subclass == AST_CONTROL_CONGESTION || | ||||
| 			     f.subclass == AST_CONTROL_HANGUP || | ||||
| 			     f.subclass == AST_CONTROL_FLASH) | ||||
| #endif | ||||
| 			      endbridge = 1; | ||||
| 		    break; | ||||
| 	       default: | ||||
| 		     | ||||
| 		    break; | ||||
| 	       } | ||||
| 	       if (endbridge) { | ||||
| 		    if (p->bridge->fo) | ||||
| 			 *p->bridge->fo = ast_frisolate(&f); | ||||
| 		    if (p->bridge->rc) | ||||
| 			 *p->bridge->rc = p->owner; | ||||
| 		     | ||||
| 		    ast_pthread_mutex_lock(&p->bridge->lock); { | ||||
| 			 pthread_cond_signal(&p->bridge->cond); | ||||
| 		    } ast_pthread_mutex_unlock(&p->bridge->lock); 	       		    | ||||
| 	       }	   | ||||
| 	  } | ||||
|      } ast_pthread_mutex_unlock(&p->lock); | ||||
|       | ||||
|      if (endbridge) return 0; | ||||
|       | ||||
|      if (f.frametype >= 0 && f.frametype != AST_FRAME_NULL)  | ||||
| 	  ast_queue_frame(p->owner, &f, 0); | ||||
| @@ -425,8 +561,12 @@ struct vpb_pvt *mkif(int board, int channel, int mode, float txgain, float rxgai | ||||
|      tmp->lastinput = -1; | ||||
|      tmp->lastoutput = -1; | ||||
|  | ||||
|      tmp->bridge = NULL; | ||||
|  | ||||
|      tmp->readthread = 0; | ||||
|  | ||||
|      pthread_mutex_init(&tmp->lock, NULL); | ||||
|  | ||||
|      if (setrxgain)       | ||||
| 	  vpb_record_set_gain(tmp->handle, rxgain); | ||||
|      if (settxgain)   | ||||
| @@ -522,19 +662,21 @@ static int vpb_hangup(struct ast_channel *ast) | ||||
|  | ||||
|     ast_setstate(ast,AST_STATE_DOWN); | ||||
|      | ||||
|     p->lastinput = p->lastoutput  = -1; | ||||
|     p->ext[0]  = 0; | ||||
|     p->owner = NULL; | ||||
|     p->dialtone = 0; | ||||
|     ast->pvt->pvt = NULL;  | ||||
|         | ||||
|     ast_pthread_mutex_lock(&p->lock); {     | ||||
| 	 p->lastinput = p->lastoutput  = -1; | ||||
| 	 p->ext[0]  = 0; | ||||
| 	 p->owner = NULL; | ||||
| 	 p->dialtone = 0; | ||||
| 	 ast->pvt->pvt = NULL;  | ||||
|     } ast_pthread_mutex_unlock(&p->lock); | ||||
| 	  | ||||
|     ast_pthread_mutex_lock(&usecnt_lock); { | ||||
| 	usecnt--; | ||||
|     } ast_pthread_mutex_unlock(&usecnt_lock); | ||||
|     ast_update_use_count(); | ||||
|  | ||||
|     /* Stop thread doing reads. */ | ||||
|     p->hangup = 1; | ||||
|     p->stopreads = 1; | ||||
|     pthread_join(p->readthread, NULL);  | ||||
|  | ||||
|     if (option_verbose > 2) | ||||
| @@ -625,13 +767,10 @@ static int vpb_write(struct ast_channel *ast, struct ast_frame *frame) | ||||
|     fmt = ast2vpbformat(frame->subclass); | ||||
|  | ||||
|     if (option_verbose > 4) | ||||
| 	 ast_verbose(VERBOSE_PREFIX_3  | ||||
| 		     " Write chan %s: got frame type = %d, " | ||||
| 		     "samples=%d, len=%d, oldfmt=%d,new=%d,rawwrite=%d\n", | ||||
| 		     p->dev, frame->subclass,  | ||||
| 		     frame->samples, frame->datalen, p->lastoutput, fmt,  | ||||
| 		     ast->pvt->rawwriteformat); | ||||
|      | ||||
| 	ast_verbose(VERBOSE_PREFIX_3  | ||||
| 		    " Write chan %s: got frame type = %d\n", | ||||
| 		    p->dev, frame->subclass); | ||||
|         | ||||
|     if (fmt < 0) { | ||||
| 	ast_log(LOG_WARNING, "vpb_write Cannot handle frames of %d format!\n",  | ||||
| 		frame->subclass); | ||||
| @@ -656,55 +795,89 @@ static int vpb_write(struct ast_channel *ast, struct ast_frame *frame) | ||||
| static void *do_chanreads(void *pvt) | ||||
| { | ||||
|      struct vpb_pvt *p = (struct vpb_pvt *)pvt; | ||||
|      char buf[VPB_MAX_BUF]; | ||||
|      struct ast_frame fr = {AST_FRAME_VOICE}; | ||||
|      struct ast_frame *fr = &p->fr; | ||||
|      char *readbuf = ((char *)p->buf) + AST_FRIENDLY_OFFSET; | ||||
|      int bridgerec = 0; | ||||
|   | ||||
|      fr->frametype = AST_FRAME_VOICE; | ||||
|      fr->src = type; | ||||
|      fr->mallocd = 0; | ||||
|       | ||||
|      fr.src = type; | ||||
|      memset(p->buf, 0, sizeof p->buf); | ||||
|  | ||||
|      while (!p->hangup && p->owner) {	  | ||||
|      while (!p->stopreads && p->owner) {	  | ||||
| 	 int res = -1, fmt; | ||||
| 	 struct ast_channel *owner = p->owner; | ||||
| 	 int afmt = (owner) ? owner->pvt->rawreadformat : AST_FORMAT_SLINEAR; | ||||
| 	 int state = (owner) ? owner->_state : AST_STATE_DOWN; | ||||
| 	 int readlen; | ||||
| 	 int readlen;	 | ||||
| 	  | ||||
| 	 fmt = ast2vpbformat(afmt); | ||||
| 	  | ||||
| 	 if (fmt < 0) { | ||||
| 	     p->stopreads = 1; | ||||
| 	     goto done; | ||||
| 	 } | ||||
|  | ||||
| 	 readlen = VPB_SAMPLES * astformatbits(afmt) / 8; | ||||
|  | ||||
| 	 if (p->lastinput != fmt) { | ||||
| 	     if (option_verbose > 2)  | ||||
| 		 ast_verbose(" Read_channel ##  %s: Setting record mode\n",  | ||||
| 			     p->dev);     	      | ||||
| 		 ast_verbose(" Read_channel ##  %s: Setting record mode, bridge = %d\n",  | ||||
| 			     p->dev, p->bridge ? 1 : 0);     	      | ||||
| 	     vpb_record_buf_start(p->handle, fmt); | ||||
| 	     p->lastinput = fmt; | ||||
| 	 }  | ||||
|  | ||||
|  | ||||
| 	 if (state == AST_STATE_UP)  /* Read only if up. */ | ||||
| 	      res = vpb_record_buf_sync(p->handle, buf, readlen); | ||||
| 	 | ||||
| 	 ast_pthread_mutex_lock(&p->lock); { | ||||
| 	      if (p->bridge)  | ||||
| 		   if (p->bridge->c0 == p->owner &&  | ||||
| 		       (p->bridge->flags & AST_BRIDGE_REC_CHANNEL_0)) | ||||
| 			bridgerec = 1; | ||||
| 		   else if (p->bridge->c1 == p->owner &&  | ||||
| 		       (p->bridge->flags & AST_BRIDGE_REC_CHANNEL_1)) | ||||
| 			bridgerec = 1; | ||||
| 		   else  | ||||
| 			bridgerec = 0; | ||||
| 	      else | ||||
| 		   bridgerec = 1; | ||||
| 	 } ast_pthread_mutex_unlock(&p->lock); | ||||
| 	  | ||||
| 	 if (state == AST_STATE_UP && bridgerec)  /* Read only if up and not bridged, or a bridge for which we can read. */ | ||||
| 	      res = vpb_record_buf_sync(p->handle, readbuf, readlen); | ||||
| 	 else { | ||||
| 	      res = 0; | ||||
| 	      vpb_sleep(10); | ||||
| 	 } | ||||
| 	 if (res == VPB_OK) { | ||||
| 	     fr.subclass = afmt; | ||||
| 	     fr.samples = VPB_SAMPLES; | ||||
| 	     fr.datalen = readlen; | ||||
| 	     fr.data = buf; | ||||
| 	      | ||||
| 	     ast_queue_frame(p->owner, &fr, 0); | ||||
| 	      fr->subclass = afmt; | ||||
| 	      fr->samples = VPB_SAMPLES; | ||||
| 	      fr->data = readbuf; | ||||
| 	      fr->datalen = readlen; | ||||
| 	      fr->offset = AST_FRIENDLY_OFFSET; | ||||
|  | ||||
| 	      ast_pthread_mutex_lock(&p->lock); {     | ||||
| 		   if (p->owner) ast_queue_frame(p->owner, fr, 0); | ||||
| 	      } ast_pthread_mutex_unlock(&p->lock); | ||||
| 	 } else  | ||||
| 	     vpb_sleep(10); | ||||
| 	      p->stopreads = 1; | ||||
| 	 	  | ||||
|      done: (void)0; | ||||
| 	 if (option_verbose > 4) | ||||
| 	     if (state == AST_STATE_UP) | ||||
| 		 ast_verbose(" Read_channel  %s (state=%d): got frame: res=%d, rawread=%d, rlen=%d\n",  | ||||
| 			     p->dev, state, res, owner ? owner->pvt->rawreadformat : -1, readlen);      | ||||
| 	     ast_verbose(" Read_channel  %s (state=%d), res=%d, bridge=%d\n",  | ||||
| 			 p->dev, state, res, bridgerec);      | ||||
|      } | ||||
|       | ||||
|      /* When hangup seen, go away! */ | ||||
|      /* When stopreads seen, go away! */ | ||||
|      vpb_record_buf_finish(p->handle); | ||||
|  | ||||
|      if (option_verbose > 4) | ||||
| 	 ast_verbose(" Read_channel  %s terminating, stopreads=%d, owner=%s\n",  | ||||
| 			     p->dev, p->stopreads, p->owner? "yes" : "no");      | ||||
|  | ||||
|      return NULL; | ||||
| } | ||||
|  | ||||
|  | ||||
| static struct ast_channel *vpb_new(struct vpb_pvt *i, int state, char *context) | ||||
| { | ||||
| 	struct ast_channel *tmp;  | ||||
| @@ -732,6 +905,7 @@ static struct ast_channel *vpb_new(struct vpb_pvt *i, int state, char *context) | ||||
| 		tmp->pvt->answer = vpb_answer; | ||||
| 		tmp->pvt->read = vpb_read; | ||||
| 		tmp->pvt->write = vpb_write; | ||||
| 		tmp->pvt->bridge = vpb_bridge; | ||||
|  | ||||
| 		strncpy(tmp->context, context, sizeof(tmp->context)-1); | ||||
| 		if (strlen(i->ext)) | ||||
| @@ -756,8 +930,8 @@ static struct ast_channel *vpb_new(struct vpb_pvt *i, int state, char *context) | ||||
| 			  ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name); | ||||
| 			  ast_hangup(tmp); | ||||
| 		     } | ||||
| 		pthread_mutex_init(&i->lock, NULL); | ||||
| 		i->hangup = 0; /* So read thread runs. */	 | ||||
|  | ||||
| 		i->stopreads = 0; /* So read thread runs. */	 | ||||
| 		/* Finally start read monitoring thread. */ | ||||
| 		pthread_create(&i->readthread, NULL, do_chanreads, (void *)i); | ||||
| 	} else | ||||
| @@ -961,7 +1135,10 @@ int unload_module() | ||||
|  | ||||
| 		while(iflist) { | ||||
| 		     p = iflist;		     | ||||
|  | ||||
| 		     pthread_mutex_destroy(&p->lock); | ||||
| 		     pthread_cancel(p->readthread); | ||||
| 		     p->readthread = 0; | ||||
| 		      | ||||
| 		     iflist = iflist->next; | ||||
| 		      | ||||
| 		     free(p); | ||||
| @@ -969,6 +1146,11 @@ int unload_module() | ||||
| 		iflist = NULL; | ||||
| 	} ast_pthread_mutex_unlock(&iflock); | ||||
| 	 | ||||
| 	ast_pthread_mutex_lock(&bridge_lock); { | ||||
| 	     memset(bridges, 0, sizeof bridges);	      | ||||
| 	} ast_pthread_mutex_unlock(&bridge_lock); | ||||
| 	pthread_mutex_destroy(&bridge_lock); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user