Main Page | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

translate.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- A telephony toolkit for Linux.
00003  *
00004  * Translate via the use of pseudo channels
00005  * 
00006  * Copyright (C) 1999, Mark Spencer
00007  *
00008  * Mark Spencer <markster@linux-support.net>
00009  *
00010  * This program is free software, distributed under the terms of
00011  * the GNU General Public License
00012  */
00013 
00014 #include <asterisk/lock.h>
00015 #include <asterisk/channel.h>
00016 #include <asterisk/channel_pvt.h>
00017 #include <asterisk/logger.h>
00018 #include <asterisk/translate.h>
00019 #include <asterisk/options.h>
00020 #include <asterisk/frame.h>
00021 #include <asterisk/sched.h>
00022 #include <asterisk/cli.h>
00023 #include <asterisk/term.h>
00024 #include <sys/socket.h>
00025 #include <sys/time.h>
00026 #include <unistd.h>
00027 #include <stdlib.h>
00028 #include <string.h>
00029 #include <stdio.h>
00030 
00031 #define MAX_RECALC 200 /* max sample recalc */
00032 
00033 /* This could all be done more efficiently *IF* we chained packets together
00034    by default, but it would also complicate virtually every application. */
00035    
00036 AST_MUTEX_DEFINE_STATIC(list_lock);
00037 static struct ast_translator *list = NULL;
00038 
00039 struct ast_translator_dir {
00040    struct ast_translator *step;  /* Next step translator */
00041    int cost;                  /* Complete cost to destination */
00042 };
00043 
00044 struct ast_frame_delivery {
00045    struct ast_frame *f;
00046    struct ast_channel *chan;
00047    int fd;
00048    struct translator_pvt *owner;
00049    struct ast_frame_delivery *prev;
00050    struct ast_frame_delivery *next;
00051 };
00052 
00053 static struct ast_translator_dir tr_matrix[MAX_FORMAT][MAX_FORMAT];
00054 
00055 struct ast_trans_pvt {
00056    struct ast_translator *step;
00057    struct ast_translator_pvt *state;
00058    struct ast_trans_pvt *next;
00059    struct timeval nextin;
00060    struct timeval nextout;
00061 };
00062 
00063 
00064 static int powerof(int d)
00065 {
00066    int x;
00067    for (x = 0; x < 32; x++)
00068       if ((1 << x) & d)
00069          return x;
00070    ast_log(LOG_WARNING, "Powerof %d: No power??\n", d);
00071    return -1;
00072 }
00073 
00074 void ast_translator_free_path(struct ast_trans_pvt *p)
00075 {
00076    struct ast_trans_pvt *pl, *pn;
00077    pn = p;
00078    while(pn) {
00079       pl = pn;
00080       pn = pn->next;
00081       if (pl->state && pl->step->destroy)
00082          pl->step->destroy(pl->state);
00083       free(pl);
00084    }
00085 }
00086 
00087 struct ast_trans_pvt *ast_translator_build_path(int dest, int source)
00088 {
00089    struct ast_trans_pvt *tmpr = NULL, *tmp = NULL;
00090    /* One of the hardest parts:  Build a set of translators based upon
00091       the given source and destination formats */
00092    source = powerof(source);
00093    dest = powerof(dest);
00094    while(source != dest) {
00095       if (tr_matrix[source][dest].step) {
00096          if (tmp) {
00097             tmp->next = malloc(sizeof(struct ast_trans_pvt));
00098             tmp = tmp->next;
00099          } else
00100             tmp = malloc(sizeof(struct ast_trans_pvt));
00101 
00102             
00103          if (tmp) {
00104             tmp->next = NULL;
00105             tmp->nextin.tv_sec = 0;
00106             tmp->nextin.tv_usec = 0;
00107             tmp->nextout.tv_sec = 0;
00108             tmp->nextout.tv_usec = 0;
00109             tmp->step = tr_matrix[source][dest].step;
00110             tmp->state = tmp->step->newpvt();
00111             if (!tmp->state) {
00112                ast_log(LOG_WARNING, "Failed to build translator step from %d to %d\n", source, dest);
00113                free(tmp);
00114                tmp = NULL;
00115                return NULL;
00116             }
00117             /* Set the root, if it doesn't exist yet... */
00118             if (!tmpr)
00119                tmpr = tmp;
00120             /* Keep going if this isn't the final destination */
00121             source = tmp->step->dstfmt;
00122          } else {
00123             /* XXX This could leak XXX */
00124             ast_log(LOG_WARNING, "Out of memory\n");
00125             return NULL;
00126          }
00127       } else {
00128          /* We shouldn't have allocated any memory */
00129          ast_log(LOG_WARNING, "No translator path from %s to %s\n", 
00130             ast_getformatname(source), ast_getformatname(dest));
00131          return NULL;
00132       }
00133    }
00134    return tmpr;
00135 }
00136 
00137 struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, int consume)
00138 {
00139    struct ast_trans_pvt *p;
00140    struct ast_frame *out;
00141    struct timeval delivery;
00142    p = path;
00143    /* Feed the first frame into the first translator */
00144    p->step->framein(p->state, f);
00145    if (f->delivery.tv_sec || f->delivery.tv_usec) {
00146       if (path->nextin.tv_sec || path->nextin.tv_usec) {
00147          /* Make sure this is in line with what we were expecting */
00148          if ((path->nextin.tv_sec != f->delivery.tv_sec) ||
00149              (path->nextin.tv_usec != f->delivery.tv_usec)) {
00150             /* The time has changed between what we expected and this
00151                most recent time on the new packet.  Adjust our output
00152                time appropriately */
00153             long sdiff;
00154             long udiff;
00155             sdiff = f->delivery.tv_sec - path->nextin.tv_sec;
00156             udiff = f->delivery.tv_usec - path->nextin.tv_usec;
00157             path->nextin.tv_sec = f->delivery.tv_sec;
00158             path->nextin.tv_usec = f->delivery.tv_usec;
00159             path->nextout.tv_sec += sdiff;
00160             path->nextout.tv_usec += udiff;
00161             if (path->nextout.tv_usec < 0) {
00162                path->nextout.tv_usec += 1000000;
00163                path->nextout.tv_sec--;
00164             } else if (path->nextout.tv_usec >= 1000000) {
00165                path->nextout.tv_usec -= 1000000;
00166                path->nextout.tv_sec++;
00167             }
00168          }
00169       } else {
00170          /* This is our first pass.  Make sure the timing looks good */
00171          path->nextin.tv_sec = f->delivery.tv_sec;
00172          path->nextin.tv_usec = f->delivery.tv_usec;
00173          path->nextout.tv_sec = f->delivery.tv_sec;
00174          path->nextout.tv_usec = f->delivery.tv_usec;
00175       }
00176       /* Predict next incoming sample */
00177       path->nextin.tv_sec += (f->samples / 8000);
00178       path->nextin.tv_usec += ((f->samples % 8000) * 125);
00179       if (path->nextin.tv_usec >= 1000000) {
00180          path->nextin.tv_usec -= 1000000;
00181          path->nextin.tv_sec++;
00182       }
00183    }
00184    delivery.tv_sec = f->delivery.tv_sec;
00185    delivery.tv_usec = f->delivery.tv_usec;
00186    if (consume)
00187       ast_frfree(f);
00188    while(p) {
00189       out = p->step->frameout(p->state);
00190       /* If we get nothing out, return NULL */
00191       if (!out)
00192          return NULL;
00193       /* If there is a next state, feed it in there.  If not,
00194          return this frame  */
00195       if (p->next) 
00196          p->next->step->framein(p->next->state, out);
00197       else {
00198          if (delivery.tv_sec || delivery.tv_usec) {
00199             /* Use next predicted outgoing timestamp */
00200             out->delivery.tv_sec = path->nextout.tv_sec;
00201             out->delivery.tv_usec = path->nextout.tv_usec;
00202             
00203             /* Predict next outgoing timestamp from samples in this
00204                frame. */
00205             path->nextout.tv_sec += (out->samples / 8000);
00206             path->nextout.tv_usec += ((out->samples % 8000) * 125);
00207             if (path->nextout.tv_usec >= 1000000) {
00208                path->nextout.tv_sec++;
00209                path->nextout.tv_usec -= 1000000;
00210             }
00211          } else {
00212             out->delivery.tv_sec = 0;
00213             out->delivery.tv_usec = 0;
00214          }
00215          return out;
00216       }
00217       p = p->next;
00218    }
00219    ast_log(LOG_WARNING, "I should never get here...\n");
00220    return NULL;
00221 }
00222 
00223 
00224 static void calc_cost(struct ast_translator *t,int samples)
00225 {
00226    int sofar=0;
00227    struct ast_translator_pvt *pvt;
00228    struct ast_frame *f, *out;
00229    struct timeval start, finish;
00230    int cost;
00231    if(!samples)
00232      samples = 1;
00233    
00234    /* If they don't make samples, give them a terrible score */
00235    if (!t->sample) {
00236       ast_log(LOG_WARNING, "Translator '%s' does not produce sample frames.\n", t->name);
00237       t->cost = 99999;
00238       return;
00239    }
00240    pvt = t->newpvt();
00241    if (!pvt) {
00242       ast_log(LOG_WARNING, "Translator '%s' appears to be broken and will probably fail.\n", t->name);
00243       t->cost = 99999;
00244       return;
00245    }
00246    gettimeofday(&start, NULL);
00247    /* Call the encoder until we've processed one second of time */
00248    while(sofar < samples * 8000) {
00249       f = t->sample();
00250       if (!f) {
00251          ast_log(LOG_WARNING, "Translator '%s' failed to produce a sample frame.\n", t->name);
00252          t->destroy(pvt);
00253          t->cost = 99999;
00254          return;
00255       }
00256       t->framein(pvt, f);
00257       ast_frfree(f);
00258       while((out = t->frameout(pvt))) {
00259          sofar += out->samples;
00260          ast_frfree(out);
00261       }
00262    }
00263    gettimeofday(&finish, NULL);
00264    t->destroy(pvt);
00265    cost = (finish.tv_sec - start.tv_sec) * 1000 + (finish.tv_usec - start.tv_usec) / 1000;
00266    t->cost = cost / samples;
00267    if (!t->cost)
00268       t->cost = 1;
00269 }
00270 
00271 static void rebuild_matrix(int samples)
00272 {
00273    struct ast_translator *t;
00274    int changed;
00275    int x,y,z;
00276    if (option_debug)
00277       ast_log(LOG_DEBUG, "Reseting translation matrix\n");
00278    /* Use the list of translators to build a translation matrix */
00279    bzero(tr_matrix, sizeof(tr_matrix));
00280    t = list;
00281    while(t) {
00282      if(samples)
00283        calc_cost(t,samples);
00284      
00285       if (!tr_matrix[t->srcfmt][t->dstfmt].step ||
00286            tr_matrix[t->srcfmt][t->dstfmt].cost > t->cost) {
00287          tr_matrix[t->srcfmt][t->dstfmt].step = t;
00288          tr_matrix[t->srcfmt][t->dstfmt].cost = t->cost;
00289       }
00290       t = t->next;
00291    }
00292    do {
00293       changed = 0;
00294       /* Don't you just love O(N^3) operations? */
00295       for (x=0; x< MAX_FORMAT; x++)          /* For each source format */
00296          for (y=0; y < MAX_FORMAT; y++)         /* And each destination format */
00297             if (x != y)                   /* Except ourselves, of course */
00298                for (z=0; z < MAX_FORMAT; z++)   /* And each format it might convert to */
00299                   if ((x!=z) && (y!=z))      /* Don't ever convert back to us */
00300                      if (tr_matrix[x][y].step && /* We can convert from x to y */
00301                         tr_matrix[y][z].step && /* And from y to z and... */
00302                         (!tr_matrix[x][z].step ||  /* Either there isn't an x->z conversion */
00303                         (tr_matrix[x][y].cost + 
00304                          tr_matrix[y][z].cost < /* Or we're cheaper than the existing */
00305                          tr_matrix[x][z].cost)  /* solution */
00306                           )) {
00307                                  /* We can get from x to z via y with a cost that
00308                                     is the sum of the transition from x to y and
00309                                     from y to z */
00310                          
00311                            tr_matrix[x][z].step = tr_matrix[x][y].step;
00312                            tr_matrix[x][z].cost = tr_matrix[x][y].cost + 
00313                                              tr_matrix[y][z].cost;
00314                            if (option_debug)
00315                               ast_log(LOG_DEBUG, "Discovered %d cost path from %s to %s, via %d\n", tr_matrix[x][z].cost, ast_getformatname(x), ast_getformatname(z), y);
00316                            changed++;
00317                          }
00318       
00319    } while (changed);
00320 }
00321 
00322 
00323 
00324 
00325 
00326 static int show_translation(int fd, int argc, char *argv[])
00327 {
00328 #define SHOW_TRANS 11
00329    int x, y, z;
00330    char line[80];
00331    if (argc > 4) 
00332       return RESULT_SHOWUSAGE;
00333 
00334    if (argv[2] && !strcasecmp(argv[2],"recalc")) {
00335       z = argv[3] ? atoi(argv[3]) : 1;
00336 
00337       if (z <= 0) {
00338          ast_cli(fd,"         C'mon let's be serious here... defaulting to 1.\n");
00339          z = 1;
00340       }
00341 
00342       if (z > MAX_RECALC) {
00343          ast_cli(fd,"         Maximum limit of recalc exceeded by %d, truncating value to %d\n",z-MAX_RECALC,MAX_RECALC);
00344          z = MAX_RECALC;
00345       }
00346       ast_cli(fd,"         Recalculating Codec Translation (number of sample seconds: %d)\n\n",z);
00347       rebuild_matrix(z);
00348    }
00349 
00350    ast_cli(fd, "         Translation times between formats (in milliseconds)\n");
00351    ast_cli(fd, "          Source Format (Rows) Destination Format(Columns)\n\n");
00352    ast_mutex_lock(&list_lock);
00353    for (x=-1;x<SHOW_TRANS; x++) {
00354       /* next 2 lines run faster than using strcpy() */
00355       line[0] = ' ';
00356       line[1] = '\0';
00357       for (y=-1;y<SHOW_TRANS;y++) {
00358          if (x >= 0 && y >= 0 && tr_matrix[x][y].step)
00359             snprintf(line + strlen(line), sizeof(line) - strlen(line), " %5d", tr_matrix[x][y].cost >= 99999 ? tr_matrix[x][y].cost-99999 : tr_matrix[x][y].cost);
00360          else
00361             if (((x == -1 && y >= 0) || (y == -1 && x >= 0))) {
00362                snprintf(line + strlen(line), sizeof(line) - strlen(line), 
00363                   " %5s", ast_getformatname(1<<(x+y+1)) );
00364             } else if (x != -1 && y != -1) {
00365                snprintf(line + strlen(line), sizeof(line) - strlen(line), "     -");
00366             } else {
00367                snprintf(line + strlen(line), sizeof(line) - strlen(line), "      ");
00368             }
00369       }
00370       snprintf(line + strlen(line), sizeof(line) - strlen(line), "\n");
00371       ast_cli(fd, line);         
00372    }
00373    ast_mutex_unlock(&list_lock);
00374    return RESULT_SUCCESS;
00375 }
00376 
00377 static int added_cli = 0;
00378 
00379 static char show_trans_usage[] =
00380 "Usage: show translation [recalc] [<recalc seconds>]\n"
00381 "       Displays known codec translators and the cost associated\n"
00382 "with each conversion.  if the arguement 'recalc' is supplied along\n"
00383 "with optional number of seconds to test a new test will be performed\n"
00384 "as the chart is being displayed.\n";
00385 
00386 static struct ast_cli_entry show_trans =
00387 { { "show", "translation", NULL }, show_translation, "Display translation matrix", show_trans_usage };
00388 
00389 int ast_register_translator(struct ast_translator *t)
00390 {
00391    char tmp[80];
00392    t->srcfmt = powerof(t->srcfmt);
00393    t->dstfmt = powerof(t->dstfmt);
00394    if ((t->srcfmt >= MAX_FORMAT) || (t->dstfmt >= MAX_FORMAT)) {
00395       ast_log(LOG_WARNING, "Format %s is larger than MAX_FORMAT\n", ast_getformatname(t->srcfmt));
00396       return -1;
00397    }
00398    calc_cost(t,1);
00399    if (option_verbose > 1)
00400       ast_verbose(VERBOSE_PREFIX_2 "Registered translator '%s' from format %s to %s, cost %d\n", term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)), ast_getformatname(1 << t->srcfmt), ast_getformatname(1 << t->dstfmt), t->cost);
00401    ast_mutex_lock(&list_lock);
00402    if (!added_cli) {
00403       ast_cli_register(&show_trans);
00404       added_cli++;
00405    }
00406    t->next = list;
00407    list = t;
00408    rebuild_matrix(0);
00409    ast_mutex_unlock(&list_lock);
00410    return 0;
00411 }
00412 
00413 int ast_unregister_translator(struct ast_translator *t)
00414 {
00415    char tmp[80];
00416    struct ast_translator *u, *ul = NULL;
00417    ast_mutex_lock(&list_lock);
00418    u = list;
00419    while(u) {
00420       if (u == t) {
00421          if (ul)
00422             ul->next = u->next;
00423          else
00424             list = u->next;
00425          if (option_verbose > 1)
00426             ast_verbose(VERBOSE_PREFIX_2 "Unregistered translator '%s' from format %s to %s\n", term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)), ast_getformatname(1 << t->srcfmt), ast_getformatname(1 << t->dstfmt));
00427          break;
00428       }
00429       ul = u;
00430       u = u->next;
00431    }
00432    rebuild_matrix(0);
00433    ast_mutex_unlock(&list_lock);
00434    return (u ? 0 : -1);
00435 }
00436 
00437 int ast_translator_best_choice(int *dst, int *srcs)
00438 {
00439    /* Calculate our best source format, given costs, and a desired destination */
00440    int x,y;
00441    int best=-1;
00442    int bestdst=0;
00443    int cur = 1;
00444    int besttime=999999999;
00445    ast_mutex_lock(&list_lock);
00446    for (y=0;y<MAX_FORMAT;y++) {
00447       if ((cur & *dst) && (cur & *srcs)) {
00448          /* This is a common format to both.  Pick it if we don't have one already */
00449          besttime=0;
00450          bestdst = cur;
00451          best = cur;
00452          break;
00453       }
00454       if (cur & *dst)
00455          for (x=0;x<MAX_FORMAT;x++) {
00456             if (tr_matrix[x][y].step &&   /* There's a step */
00457                 (tr_matrix[x][y].cost < besttime) && /* We're better than what exists now */
00458                (*srcs & (1 << x)))        /* x is a valid source format */
00459                {
00460                   best = 1 << x;
00461                   bestdst = cur;
00462                   besttime = tr_matrix[x][y].cost;
00463                }
00464          }
00465       cur = cur << 1;
00466    }
00467    if (best > -1) {
00468       *srcs = best;
00469       *dst = bestdst;
00470       best = 0;
00471    }
00472    ast_mutex_unlock(&list_lock);
00473    return best;
00474 }

Generated on Thu Nov 29 22:50:24 2007 for Asterisk by  doxygen 1.4.2