Fri Sep 29 11:12:32 2006

Asterisk developer's documentation


translate.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2005, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Translate via the use of pseudo channels
00022  * 
00023  */
00024 
00025 #include <sys/types.h>
00026 #include <sys/socket.h>
00027 #include <sys/time.h>
00028 #include <unistd.h>
00029 #include <stdlib.h>
00030 #include <string.h>
00031 #include <stdio.h>
00032 
00033 #include "asterisk.h"
00034 
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 11120 $")
00036 
00037 #include "asterisk/lock.h"
00038 #include "asterisk/channel.h"
00039 #include "asterisk/logger.h"
00040 #include "asterisk/translate.h"
00041 #include "asterisk/options.h"
00042 #include "asterisk/frame.h"
00043 #include "asterisk/sched.h"
00044 #include "asterisk/cli.h"
00045 #include "asterisk/term.h"
00046 
00047 #define MAX_RECALC 200 /* max sample recalc */
00048 
00049 /*! \note
00050    This could all be done more efficiently *IF* we chained packets together
00051    by default, but it would also complicate virtually every application. */
00052    
00053 AST_MUTEX_DEFINE_STATIC(list_lock);
00054 static struct ast_translator *list = NULL;
00055 
00056 struct ast_translator_dir {
00057    struct ast_translator *step;  /*!< Next step translator */
00058    unsigned int cost;      /*!< Complete cost to destination */
00059    unsigned int multistep;    /*!< Multiple conversions required for this translation */
00060 };
00061 
00062 struct ast_frame_delivery {
00063    struct ast_frame *f;
00064    struct ast_channel *chan;
00065    int fd;
00066    struct translator_pvt *owner;
00067    struct ast_frame_delivery *prev;
00068    struct ast_frame_delivery *next;
00069 };
00070 
00071 static struct ast_translator_dir tr_matrix[MAX_FORMAT][MAX_FORMAT];
00072 
00073 struct ast_trans_pvt {
00074    struct ast_translator *step;
00075    struct ast_translator_pvt *state;
00076    struct ast_trans_pvt *next;
00077    struct timeval nextin;
00078    struct timeval nextout;
00079 };
00080 
00081 
00082 static int powerof(int d)
00083 {
00084    int x;
00085    for (x = 0; x < 32; x++)
00086       if ((1 << x) & d)
00087          return x;
00088    ast_log(LOG_WARNING, "Powerof %d: No power??\n", d);
00089    return -1;
00090 }
00091 
00092 void ast_translator_free_path(struct ast_trans_pvt *p)
00093 {
00094    struct ast_trans_pvt *pl, *pn;
00095    pn = p;
00096    while(pn) {
00097       pl = pn;
00098       pn = pn->next;
00099       if (pl->state && pl->step->destroy)
00100          pl->step->destroy(pl->state);
00101       free(pl);
00102    }
00103 }
00104 
00105 /*! Build a set of translators based upon the given source and destination formats */
00106 struct ast_trans_pvt *ast_translator_build_path(int dest, int source)
00107 {
00108    struct ast_trans_pvt *tmpr = NULL, *tmp = NULL;
00109    
00110    source = powerof(source);
00111    dest = powerof(dest);
00112    
00113    while(source != dest) {
00114       if (!tr_matrix[source][dest].step) {
00115          /* We shouldn't have allocated any memory */
00116          ast_log(LOG_WARNING, "No translator path from %s to %s\n", 
00117             ast_getformatname(source), ast_getformatname(dest));
00118          return NULL;
00119       }
00120 
00121       if (tmp) {
00122          tmp->next = malloc(sizeof(*tmp));
00123          tmp = tmp->next;
00124       } else
00125          tmp = malloc(sizeof(*tmp));
00126          
00127       if (!tmp) {
00128          ast_log(LOG_WARNING, "Out of memory\n");
00129          if (tmpr)
00130             ast_translator_free_path(tmpr);  
00131          return NULL;
00132       }
00133 
00134       /* Set the root, if it doesn't exist yet... */
00135       if (!tmpr)
00136          tmpr = tmp;
00137 
00138       tmp->next = NULL;
00139       tmp->nextin = tmp->nextout = ast_tv(0, 0);
00140       tmp->step = tr_matrix[source][dest].step;
00141       tmp->state = tmp->step->newpvt();
00142       
00143       if (!tmp->state) {
00144          ast_log(LOG_WARNING, "Failed to build translator step from %d to %d\n", source, dest);
00145          ast_translator_free_path(tmpr);  
00146          return NULL;
00147       }
00148       
00149       /* Keep going if this isn't the final destination */
00150       source = tmp->step->dstfmt;
00151    }
00152    return tmpr;
00153 }
00154 
00155 struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, int consume)
00156 {
00157    struct ast_trans_pvt *p;
00158    struct ast_frame *out;
00159    struct timeval delivery;
00160    p = path;
00161    /* Feed the first frame into the first translator */
00162    p->step->framein(p->state, f);
00163    if (!ast_tvzero(f->delivery)) {
00164       if (!ast_tvzero(path->nextin)) {
00165          /* Make sure this is in line with what we were expecting */
00166          if (!ast_tveq(path->nextin, f->delivery)) {
00167             /* The time has changed between what we expected and this
00168                most recent time on the new packet.  If we have a
00169                valid prediction adjust our output time appropriately */
00170             if (!ast_tvzero(path->nextout)) {
00171                path->nextout = ast_tvadd(path->nextout,
00172                           ast_tvsub(f->delivery, path->nextin));
00173             }
00174             path->nextin = f->delivery;
00175          }
00176       } else {
00177          /* This is our first pass.  Make sure the timing looks good */
00178          path->nextin = f->delivery;
00179          path->nextout = f->delivery;
00180       }
00181       /* Predict next incoming sample */
00182       path->nextin = ast_tvadd(path->nextin, ast_samp2tv(f->samples, 8000));
00183    }
00184    delivery = f->delivery;
00185    if (consume)
00186       ast_frfree(f);
00187    while(p) {
00188       out = p->step->frameout(p->state);
00189       /* If we get nothing out, return NULL */
00190       if (!out)
00191          return NULL;
00192       /* If there is a next state, feed it in there.  If not,
00193          return this frame  */
00194       if (p->next) 
00195          p->next->step->framein(p->next->state, out);
00196       else {
00197          if (!ast_tvzero(delivery)) {
00198             /* Regenerate prediction after a discontinuity */
00199             if (ast_tvzero(path->nextout))
00200                path->nextout = ast_tvnow();
00201 
00202             /* Use next predicted outgoing timestamp */
00203             out->delivery = path->nextout;
00204             
00205             /* Predict next outgoing timestamp from samples in this
00206                frame. */
00207             path->nextout = ast_tvadd(path->nextout, ast_samp2tv( out->samples, 8000));
00208          } else {
00209             out->delivery = ast_tv(0, 0);
00210          }
00211          /* Invalidate prediction if we're entering a silence period */
00212          if (out->frametype == AST_FRAME_CNG)
00213             path->nextout = ast_tv(0, 0);
00214          return out;
00215       }
00216       p = p->next;
00217    }
00218    ast_log(LOG_WARNING, "I should never get here...\n");
00219    return NULL;
00220 }
00221 
00222 
00223 static void calc_cost(struct ast_translator *t, int samples)
00224 {
00225    int sofar=0;
00226    struct ast_translator_pvt *pvt;
00227    struct ast_frame *f, *out;
00228    struct timeval start;
00229    int cost;
00230 
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    start = ast_tvnow();
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    cost = ast_tvdiff_ms(ast_tvnow(), start);
00264    t->destroy(pvt);
00265    t->cost = cost / samples;
00266    if (!t->cost)
00267       t->cost = 1;
00268 }
00269 
00270 /*! \brief Use the list of translators to build a translation matrix */
00271 static void rebuild_matrix(int samples)
00272 {
00273    struct ast_translator *t;
00274    int changed;
00275    int x, y, z;
00276 
00277    if (option_debug)
00278       ast_log(LOG_DEBUG, "Resetting translation matrix\n");
00279 
00280    bzero(tr_matrix, sizeof(tr_matrix));
00281 
00282    for (t = list; t; t = t->next) {
00283       if (samples)
00284          calc_cost(t, samples);
00285      
00286       if (!tr_matrix[t->srcfmt][t->dstfmt].step ||
00287           tr_matrix[t->srcfmt][t->dstfmt].cost > t->cost) {
00288          tr_matrix[t->srcfmt][t->dstfmt].step = t;
00289          tr_matrix[t->srcfmt][t->dstfmt].cost = t->cost;
00290       }
00291    }
00292 
00293    do {
00294       changed = 0;
00295 
00296       /* Don't you just love O(N^3) operations? */
00297       for (x = 0; x< MAX_FORMAT; x++) {         /* For each source format */
00298          for (y = 0; y < MAX_FORMAT; y++) {     /* And each destination format */
00299             if (x == y)          /* Except ourselves, of course */
00300                continue;
00301 
00302             for (z=0; z < MAX_FORMAT; z++) {    /* And each format it might convert to */
00303                if ((x == z) || (y == z))  /* Don't ever convert back to us */
00304                   continue;
00305 
00306                if (tr_matrix[x][y].step &&   /* We can convert from x to y */
00307                    tr_matrix[y][z].step &&   /* And from y to z and... */
00308                    (!tr_matrix[x][z].step ||    /* Either there isn't an x->z conversion */
00309                     (tr_matrix[x][y].cost + 
00310                      tr_matrix[y][z].cost <  /* Or we're cheaper than the existing */
00311                      tr_matrix[x][z].cost)   /* solution */
00312                       )) {
00313                   /* We can get from x to z via y with a cost that
00314                      is the sum of the transition from x to y and
00315                      from y to z */
00316                   
00317                   tr_matrix[x][z].step = tr_matrix[x][y].step;
00318                   tr_matrix[x][z].cost = tr_matrix[x][y].cost + 
00319                      tr_matrix[y][z].cost;
00320                   tr_matrix[x][z].multistep = 1;
00321                   if (option_debug)
00322                      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);
00323                   changed++;
00324                }
00325             }
00326          }
00327       }
00328    } while (changed);
00329 }
00330 
00331 
00332 /*! \brief CLI "show translation" command handler */
00333 static int show_translation(int fd, int argc, char *argv[])
00334 {
00335 #define SHOW_TRANS 11
00336    int x, y, z;
00337    char line[80];
00338    if (argc > 4) 
00339       return RESULT_SHOWUSAGE;
00340 
00341    ast_mutex_lock(&list_lock);
00342    if (argv[2] && !strcasecmp(argv[2],"recalc")) {
00343       z = argv[3] ? atoi(argv[3]) : 1;
00344 
00345       if (z <= 0) {
00346          ast_cli(fd,"         C'mon let's be serious here... defaulting to 1.\n");
00347          z = 1;
00348       }
00349 
00350       if (z > MAX_RECALC) {
00351          ast_cli(fd,"         Maximum limit of recalc exceeded by %d, truncating value to %d\n",z-MAX_RECALC,MAX_RECALC);
00352          z = MAX_RECALC;
00353       }
00354       ast_cli(fd,"         Recalculating Codec Translation (number of sample seconds: %d)\n\n",z);
00355       rebuild_matrix(z);
00356    }
00357 
00358    ast_cli(fd, "         Translation times between formats (in milliseconds)\n");
00359    ast_cli(fd, "          Source Format (Rows) Destination Format(Columns)\n\n");
00360    for (x = -1; x < SHOW_TRANS; x++) {
00361       /* next 2 lines run faster than using strcpy() */
00362       line[0] = ' ';
00363       line[1] = '\0';
00364       for (y=-1;y<SHOW_TRANS;y++) {
00365          if (x >= 0 && y >= 0 && tr_matrix[x][y].step)
00366             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);
00367          else
00368             if (((x == -1 && y >= 0) || (y == -1 && x >= 0))) {
00369                snprintf(line + strlen(line), sizeof(line) - strlen(line), 
00370                   " %5s", ast_getformatname(1<<(x+y+1)) );
00371             } else if (x != -1 && y != -1) {
00372                snprintf(line + strlen(line), sizeof(line) - strlen(line), "     -");
00373             } else {
00374                snprintf(line + strlen(line), sizeof(line) - strlen(line), "      ");
00375             }
00376       }
00377       snprintf(line + strlen(line), sizeof(line) - strlen(line), "\n");
00378       ast_cli(fd, line);         
00379    }
00380    ast_mutex_unlock(&list_lock);
00381    return RESULT_SUCCESS;
00382 }
00383 
00384 static int added_cli = 0;
00385 
00386 static char show_trans_usage[] =
00387 "Usage: show translation [recalc] [<recalc seconds>]\n"
00388 "       Displays known codec translators and the cost associated\n"
00389 "with each conversion.  If the argument 'recalc' is supplied along\n"
00390 "with optional number of seconds to test a new test will be performed\n"
00391 "as the chart is being displayed.\n";
00392 
00393 static struct ast_cli_entry show_trans =
00394 { { "show", "translation", NULL }, show_translation, "Display translation matrix", show_trans_usage };
00395 
00396 int ast_register_translator(struct ast_translator *t)
00397 {
00398    char tmp[80];
00399    t->srcfmt = powerof(t->srcfmt);
00400    t->dstfmt = powerof(t->dstfmt);
00401    if (t->srcfmt >= MAX_FORMAT) {
00402       ast_log(LOG_WARNING, "Source format %s is larger than MAX_FORMAT\n", ast_getformatname(t->srcfmt));
00403       return -1;
00404    }
00405    if (t->dstfmt >= MAX_FORMAT) {
00406       ast_log(LOG_WARNING, "Destination format %s is larger than MAX_FORMAT\n", ast_getformatname(t->dstfmt));
00407       return -1;
00408    }
00409    calc_cost(t,1);
00410    if (option_verbose > 1)
00411       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);
00412    ast_mutex_lock(&list_lock);
00413    if (!added_cli) {
00414       ast_cli_register(&show_trans);
00415       added_cli++;
00416    }
00417    t->next = list;
00418    list = t;
00419    rebuild_matrix(0);
00420    ast_mutex_unlock(&list_lock);
00421    return 0;
00422 }
00423 
00424 /*! \brief unregister codec translator */
00425 int ast_unregister_translator(struct ast_translator *t)
00426 {
00427    char tmp[80];
00428    struct ast_translator *u, *ul = NULL;
00429    ast_mutex_lock(&list_lock);
00430    u = list;
00431    while(u) {
00432       if (u == t) {
00433          if (ul)
00434             ul->next = u->next;
00435          else
00436             list = u->next;
00437          if (option_verbose > 1)
00438             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));
00439          break;
00440       }
00441       ul = u;
00442       u = u->next;
00443    }
00444    rebuild_matrix(0);
00445    ast_mutex_unlock(&list_lock);
00446    return (u ? 0 : -1);
00447 }
00448 
00449 /*! \brief Calculate our best translator source format, given costs, and a desired destination */
00450 int ast_translator_best_choice(int *dst, int *srcs)
00451 {
00452    int x,y;
00453    int best = -1;
00454    int bestdst = 0;
00455    int cur = 1;
00456    int besttime = INT_MAX;
00457    int beststeps = INT_MAX;
00458    int common;
00459 
00460    if ((common = (*dst) & (*srcs))) {
00461       /* We have a format in common */
00462       for (y = 0; y < MAX_FORMAT; y++) {
00463          if (cur & common) {
00464             /* This is a common format to both.  Pick it if we don't have one already */
00465             bestdst = cur;
00466             best = cur;
00467          }
00468          cur = cur << 1;
00469       }
00470    } else {
00471       /* We will need to translate */
00472       ast_mutex_lock(&list_lock);
00473       for (y = 0; y < MAX_FORMAT; y++) {
00474          if (!(cur & *dst)) {
00475             cur = cur << 1;
00476             continue;
00477          }
00478 
00479          for (x = 0; x < MAX_FORMAT; x++) {
00480             if ((*srcs & (1 << x)) &&        /* x is a valid source format */
00481                 tr_matrix[x][y].step) {         /* There's a step */
00482                if (tr_matrix[x][y].cost > besttime)
00483                   continue;         /* It's more expensive, skip it */
00484                
00485                if (tr_matrix[x][y].cost == besttime &&
00486                    tr_matrix[x][y].multistep >= beststeps)
00487                   continue;         /* It requires the same (or more) steps,
00488                                  skip it */
00489 
00490                /* It's better than what we have so far */
00491                best = 1 << x;
00492                bestdst = cur;
00493                besttime = tr_matrix[x][y].cost;
00494                beststeps = tr_matrix[x][y].multistep;
00495             }
00496          }
00497          cur = cur << 1;
00498       }
00499       ast_mutex_unlock(&list_lock);
00500    }
00501 
00502    if (best > -1) {
00503       *srcs = best;
00504       *dst = bestdst;
00505       best = 0;
00506    }
00507 
00508    return best;
00509 }

Generated on Fri Sep 29 11:12:32 2006 for Asterisk - the Open Source PBX by  doxygen 1.4.7