testcall-jb.c

00001 /*
00002  * testcall: make a single test call with IAXCLIENT
00003  *
00004  * IAX Support for talking to Asterisk and other Gnophone clients
00005  *
00006  * Copyright (C) 1999, Linux Support Services, Inc.
00007  *
00008  * Mark Spencer <markster@linux-support.net>
00009  *
00010  * This program is free software, distributed under the terms of
00011  * the GNU Lesser (Library) General Public License
00012  */
00013 
00014 /* #define      PRINTCHUCK /* enable this to indicate chucked incomming packets */
00015 
00016 #include <stdio.h>
00017 #include <stdlib.h>
00018 #include <string.h>
00019 #include <fcntl.h>
00020 #include <stdio.h>
00021 #include <time.h>
00022 
00023 #include "iaxclient.h"
00024 
00025 #define NETSTATS
00026 #define JITTERMAKER
00027 
00028 #ifdef JITTERMAKER
00029 #include <netdb.h>
00030 #include <sys/socket.h>
00031 #include <netinet/in.h>
00032 #include <sys/time.h>
00033 #include <stdlib.h>
00034 #include <string.h>
00035 #include <stdarg.h>
00036 #include <stdio.h>
00037 #include <unistd.h>
00038 #include <fcntl.h>
00039 #include <errno.h>
00040 #include <sys/select.h>
00041 #include <netinet/in.h>
00042 #include <arpa/inet.h>
00043 #include <time.h>
00044 
00045 
00046 int netfd = -1;
00047 int preferredportno = 0;
00048 
00049 #define JM_BUCKETS 500
00050 
00051 static int jm_buckets_full= 0;
00052 
00053 static int jm_dropped = 0;
00054 static int jm_delayed = 0;
00055 
00056 static int jm_droppct = 0;
00057 static int jm_buckpct = 0;
00058 static double jm_jitter = 0;
00059 
00060 static int jm_starttime = 0;
00061 
00062 
00063 struct jm_frame {
00064     char buf[2048];
00065     size_t len;
00066     struct sockaddr from;
00067     int occupied;
00068     unsigned deltime;
00069 };
00070 
00071 struct jm_frame jm_buckets[JM_BUCKETS];
00072 
00073 
00074 long jm_time() {
00075   struct timeval tv;
00076   gettimeofday(&tv, NULL);
00077   return tv.tv_sec * 1000 + tv.tv_usec / 1000;
00078 }
00079 
00080 int jm_init() {
00081         int portno = preferredportno;
00082         struct sockaddr_in sin;
00083         unsigned int sinlen;
00084         int flags;
00085 
00086         if (netfd > -1) {
00087                 /* Sokay, just don't do anything */
00088                 fprintf(stderr,  "Already initialized.");
00089                 return 0;
00090         }
00091         netfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
00092         if (netfd < 0) {
00093                 fprintf(stderr,  "Unable to allocate UDP socket\n");
00094                 fprintf(stderr,  "Unable to allocate UDP socket\n");
00095                 return -1;
00096         }
00097 
00098         if (preferredportno == 0)
00099                 preferredportno = 4569;
00100 
00101         if (preferredportno > 0) {
00102                 sin.sin_family = AF_INET;
00103                 sin.sin_addr.s_addr = 0;
00104                 sin.sin_port = htons((short)preferredportno);
00105                 if (bind(netfd, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
00106                         fprintf(stderr,  "Unable to bind to preferred port.  Using random one instead.");
00107                 }
00108         }
00109         sinlen = sizeof(sin);
00110         if (getsockname(netfd, (struct sockaddr *) &sin, &sinlen) < 0) {
00111                 close(netfd);
00112                 netfd = -1;
00113                 fprintf(stderr,  "Unable to figure out what I'm bound to.");
00114                 fprintf(stderr,  "Unable to determine bound port number.");
00115         }
00116 #ifdef  WIN32
00117         flags = 1;
00118         if (ioctlsocket(netfd,FIONBIO,(unsigned long *) &flags)) {
00119                 _close(netfd);
00120                 netfd = -1;
00121                 fprintf(stderr,  "Unable to set non-blocking mode.");
00122                 fprintf(stderr,  "Unable to set non-blocking mode.");
00123         }
00124 
00125 #else
00126         if ((flags = fcntl(netfd, F_GETFL)) < 0) {
00127                 close(netfd);
00128                 netfd = -1;
00129                 fprintf(stderr,  "Unable to retrieve socket flags.");
00130                 fprintf(stderr,  "Unable to retrieve socket flags.");
00131         }
00132         if (fcntl(netfd, F_SETFL, flags | O_NONBLOCK) < 0) {
00133                 close(netfd);
00134                 netfd = -1;
00135                 fprintf(stderr,  "Unable to set non-blocking mode.");
00136                 fprintf(stderr,  "Unable to set non-blocking mode.");
00137         }
00138 #endif
00139 }
00140 
00141 int jm_sendto(int fd, const void *buf, size_t sz, int i, const struct sockaddr * addr, socklen_t asz) {
00142         (void)fd;
00143 
00144         // X% outgoing loss
00145         //if((1.0+rand())/RAND_MAX  < 0.10) return 0;
00146 
00147         return sendto(netfd, buf, sz, i, addr, asz);    
00148 }
00149 
00150 
00151 
00152 
00153 int jm_recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) {
00154         int ret;
00155         static int report;
00156 
00157         (void)s;
00158 
00159         ret = recvfrom(netfd,buf,len,flags,from,fromlen);
00160 
00161         /* always return errors/empty */
00162         if(ret <= 0) return ret;
00163 
00164         if((report++ % 200) == 0) 
00165             fprintf(stderr, "JM: %d dropped, %d delayed\n", jm_dropped, jm_delayed);
00166 
00167         /* randomly throw some away */
00168         if( (100.0*rand()/(RAND_MAX+1.0)) < jm_droppct) {
00169           //fprintf(stderr, "JM: dropping\n");
00170           jm_dropped++;
00171           errno = EAGAIN;
00172           return -1;
00173         }
00174 
00175         /* JM_BUCKET_PCT of the time, put in a bucket, and try to remove from a bucket */
00176         if( 1 && (100.0*rand()/(RAND_MAX+1.0)) < jm_buckpct ) {
00177             int i,j;
00178             unsigned earlytm;
00179 
00180             /* try to put in a bucket */
00181             for(i=0;i<JM_BUCKETS;i++) {
00182                 //fprintf(stderr, "JM: bucketing\n");
00183                 if(!jm_buckets[i].occupied) {
00184                     memcpy(jm_buckets[i].buf, buf, ret); 
00185                     jm_buckets[i].len = ret;
00186                     jm_buckets[i].from = *from;
00187                     jm_buckets[i].occupied = 1;
00188                     jm_buckets[i].deltime = jm_time() + (int)(jm_jitter*rand()/(RAND_MAX+1.0)) ;
00189                     //fprintf(stderr, "time = %ld, setting deltime to %ld\n", jm_time(), jm_buckets[i].deltime);
00190                     jm_buckets_full++;
00191                     break;
00192                 }
00193             }
00194             if(i == JM_BUCKETS) jm_dropped++;
00195 
00196             j = -1;
00197             earlytm = -1;
00198 
00199             for(i = 0; i < JM_BUCKETS; i++) {
00200               if(jm_buckets[i].occupied && jm_buckets[i].deltime < earlytm) {   
00201                 j = i;
00202                 earlytm = jm_buckets[i].deltime;
00203               }
00204             }
00205 
00206             i=j;
00207             if(earlytm < jm_time() && i < JM_BUCKETS) {
00208                   ret = jm_buckets[i].len;
00209                   memcpy(buf,jm_buckets[i].buf, ret); 
00210                   *from = jm_buckets[i].from;
00211                   jm_buckets[i].occupied = 0;
00212                   jm_buckets_full --;
00213                   jm_delayed++;
00214                   //fprintf(stderr, "delivering deltime of %ld\n", jm_buckets[i].deltime);
00215                   return ret;
00216             }
00217 
00218 
00219 
00220 
00221 #if 0
00222             /* randomly try to find a full bucket.  Otherwise go on */
00223             for(j=0;j<JM_BUCKETS/2;j++) {
00224                 i = (int) ((double)JM_BUCKETS*rand()/(RAND_MAX+1.0));
00225                 if(jm_buckets[i].occupied) {
00226                     ret = jm_buckets[i].len;
00227                     memcpy(buf,jm_buckets[i].buf, ret); 
00228                     *from = jm_buckets[i].from;
00229                     jm_buckets[i].occupied = 0;
00230                     jm_buckets_full --;
00231                     jm_delayed++;
00232                     return ret;
00233                 }
00234             }
00235 #endif
00236 
00237             /* if we got here, we didn't find something in our buckets */
00238             jm_dropped++;
00239             errno = EAGAIN;
00240             return -1;
00241         }
00242 
00243 
00244         return ret;
00245 }
00246 
00247 
00248 
00249 #endif
00250 
00251 
00252 static int answered_call;
00253 static char *output_filename = NULL;
00254 int do_levels = 0;
00255 
00256 /* routine called at exit to shutdown audio I/O and close nicely.
00257 NOTE: If all this isnt done, the system doesnt not handle this
00258 cleanly and has to be rebooted. What a pile of doo doo!! */
00259 void killem(void)
00260 {
00261         iaxc_shutdown();
00262         return;
00263 }
00264 
00265 void fatal_error(char *err) {
00266         killem();
00267         fprintf(stderr, "FATAL ERROR: %s\n", err);
00268         exit(1);
00269 }
00270 
00271 void mysleep(void)
00272 {
00273         iaxc_millisleep(10);
00274 }
00275 
00276 int levels_callback(float input, float output) {
00277     static int i;
00278 #ifdef notdefNETSTATS    
00279     if(i%25 == 0)
00280         fprintf(stderr, "RTT\tRJIT\tRLOSP\tRLOSC\tRPKTS\tRDEL\tLJIT\tLLOSP\tLLOSC\tLPKTS\tLDEL\n");
00281     if(i++ % 5 == 0) {
00282         unsigned int info[10];
00283         iaxc_get_netstats(iaxc_selected_call(),info,10);
00284         fprintf(stderr, "%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t\n",
00285             info[0], info[1], info[2]>>24, info[2] & 0xffffff, info[3], info[4],
00286             info[5], info[6]>>24, info[6] & 0xffffff, info[7], info[8]);
00287     }
00288 
00289 #endif
00290     if(do_levels) fprintf(stderr, "IN: %f OUT: %f\n", input, output);
00291     return 0;
00292 }
00293 
00294 int netstat_callback(struct iaxc_ev_netstats n) {
00295     static int i;
00296     if(i++%25 == 0)
00297         fprintf(stderr, "RTT\t"
00298             "Rjit\tRlos%\tRlosC\tRpkts\tRdel\tRdrop\tRooo\t"
00299             "Ljit\tLlos%\tLlosC\tLpkts\tLdel\tLdrop\tLooo\n"
00300             );
00301 
00302     fprintf(stderr, "%d\t"
00303           "%d\t%d\t%d\t%d\t%d\t%d\t%d\t"
00304           "%d\t%d\t%d\t%d\t%d\t%d\t%d\n",
00305 
00306             n.rtt,
00307 
00308             n.remote.jitter,
00309             n.remote.losspct,
00310             n.remote.losscnt,
00311             n.remote.packets,
00312             n.remote.delay,
00313             n.remote.dropped,
00314             n.remote.ooo,
00315 
00316             n.local.jitter,
00317             n.local.losspct,
00318             n.local.losscnt,
00319             n.local.packets,
00320             n.local.delay,
00321             n.local.dropped,
00322             n.local.ooo
00323     );
00324 
00325     return 0;
00326 }
00327 
00328 int iaxc_callback(iaxc_event e)
00329 {
00330     switch(e.type) {
00331         case IAXC_EVENT_LEVELS:
00332             return levels_callback(e.ev.levels.input, e.ev.levels.output);
00333         case IAXC_EVENT_NETSTAT:
00334             return netstat_callback(e.ev.netstats);
00335         case IAXC_EVENT_TEXT:
00336             return 0; // don't handle
00337         case IAXC_EVENT_STATE:
00338             return 0;
00339         default:
00340             return 0;  // not handled
00341     }
00342 }
00343 
00344 void list_devices()
00345 {
00346     struct iaxc_audio_device *devs;
00347     int nDevs, input, output, ring;
00348     int i;
00349 
00350     iaxc_audio_devices_get(&devs,&nDevs, &input, &output, &ring);
00351     for(i=0;i<nDevs;i++) {
00352         fprintf(stderr, "DEVICE ID=%d NAME=%s CAPS=%x\n", devs[i].devID, devs[i].name, devs[i].capabilities);
00353     }
00354 }
00355 
00356 void usage()
00357 { 
00358     fprintf(stderr, "Usage is XXX\n");
00359     exit(1);
00360 }
00361 
00362 int main(int argc, char **argv)
00363 {
00364         FILE *f;
00365         char c;
00366         int i;
00367         char *dest = "guest@10.23.1.31/9999";
00368         double silence_threshold = -99;
00369 
00370 
00371         f = stdout;
00372 
00373         for(i=1;i<argc;i++)
00374         {
00375            if(argv[i][0] == '-') 
00376            {
00377               switch(tolower(argv[i][1]))
00378               {
00379                 case 'v':
00380                   do_levels = 1;
00381                   break;
00382                 case 's':
00383                   if(i+1 >= argc) usage();
00384                   silence_threshold = atof(argv[++i]);
00385                   break;
00386                 case 'f':
00387                   if(i+1 >= argc) usage();
00388                   output_filename = argv[++i];
00389                   break;
00390 
00391                 default:
00392                   usage();
00393               }
00394             } else {
00395               dest=argv[i];
00396             }
00397         }
00398 
00399 
00400         printf("settings: \n");
00401         printf("\tsilence threshold: %f\n", silence_threshold);
00402         printf("\tlevel output: %s\n", do_levels ? "on" : "off");
00403 
00404         /* activate the exit handler */
00405         atexit(killem);
00406 
00407 #ifdef JITTERMAKER      
00408         jm_init();      
00409         iaxc_set_networking(jm_sendto, jm_recvfrom);
00410 #endif
00411         
00412         if(output_filename) {
00413           FILE *outfile;
00414           if(iaxc_initialize(AUDIO_INTERNAL_FILE,1))
00415             fatal_error("cannot initialize iaxclient!");
00416           outfile = fopen(output_filename,"w");
00417           iaxc_set_files(NULL, outfile);
00418         } else {
00419           if(iaxc_initialize(AUDIO_INTERNAL_PA,1))
00420             fatal_error("cannot initialize iaxclient!");
00421         }
00422 
00423         //iaxc_set_formats(IAXC_FORMAT_SPEEX,IAXC_FORMAT_ULAW|IAXC_FORMAT_GSM|IAXC_FORMAT_SPEEX);
00424         iaxc_set_formats(IAXC_FORMAT_SPEEX,IAXC_FORMAT_ILBC|IAXC_FORMAT_ALAW|IAXC_FORMAT_ULAW|IAXC_FORMAT_GSM|IAXC_FORMAT_SPEEX);
00425         //iaxc_set_formats(IAXC_FORMAT_ILBC,IAXC_FORMAT_ILBC);
00426         //iaxc_set_formats(IAXC_FORMAT_SPEEX,IAXC_FORMAT_SPEEX);
00427 
00428         iaxc_set_silence_threshold(silence_threshold);
00429 
00430         list_devices();
00431 
00432         iaxc_set_event_callback(iaxc_callback); 
00433 
00434 
00435         fprintf(f, "\n\
00436             TestCall accept some keyboard input while it's running.\n\
00437             You must hit 'enter' for your keypresses to be recognized,\n\
00438             although you can type more than one key on a line\n\
00439 \n\
00440             q: drop the call and hangup.\n\
00441             0-9 * or #: dial those DTMF digits.\n");
00442         fprintf(f, "Calling %s\n", dest);
00443         
00444         iaxc_call(dest);
00445 
00446         iaxc_start_processing_thread();
00447         printf("ready for keyboard input\n");
00448         
00449         if(output_filename) {
00450             for(;;)
00451               sleep(10);
00452         }
00453         while(c = getc(stdin)) {
00454             switch (tolower(c)) {
00455               case 'q':
00456                 printf("Hanging up and exiting\n");
00457                 iaxc_dump_call();
00458                 iaxc_millisleep(1000);
00459                 iaxc_stop_processing_thread();
00460                 exit(0);
00461               break;            
00462 
00463               case '1': case '2': case '3': case '4': case '5':
00464                 jm_droppct = 10 * (c - '1');
00465                 fprintf(stderr, "setting jm_droppct to %d\n", jm_droppct);
00466               break;
00467               case '6': case '7': case '8': case '9': case '0':
00468                 jm_buckpct = 15 * (c - '6');
00469                 fprintf(stderr, "setting jm_buckpct to %d\n", jm_buckpct);
00470               break;
00471               case '#': case '*':
00472                 printf ("sending %c\n", c);
00473                 iaxc_send_dtmf(c);
00474               break;
00475             }
00476         }
00477 
00478         return 0;
00479 }

Generated on Mon Sep 24 15:43:29 2007 for IAXClient by  doxygen 1.5.3