/ fixed up timers for ring back and busy tones
[asterisk/asterisk.git] / translate.c
index f6b0f2c..1a95295 100755 (executable)
@@ -11,6 +11,7 @@
  * the GNU General Public License
  */
 
+#include <asterisk/lock.h>
 #include <asterisk/channel.h>
 #include <asterisk/channel_pvt.h>
 #include <asterisk/logger.h>
@@ -19,6 +20,7 @@
 #include <asterisk/frame.h>
 #include <asterisk/sched.h>
 #include <asterisk/cli.h>
+#include <asterisk/term.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdio.h>
 
-/* Uncomment the EXPERIMENTAL_TRANSLATION to enable a more complicated, but probably more
-   correct way of handling full duplex translation */
-
-/*
-#define EXPERIMENTAL_TRANSLATION
-*/
+#define MAX_RECALC 200 /* max sample recalc */
 
 /* This could all be done more efficiently *IF* we chained packets together
    by default, but it would also complicate virtually every application. */
    
-static pthread_mutex_t list_lock = PTHREAD_MUTEX_INITIALIZER;
+static ast_mutex_t list_lock = AST_MUTEX_INITIALIZER;
 static struct ast_translator *list = NULL;
 
 struct ast_translator_dir {
@@ -60,6 +57,8 @@ struct ast_trans_pvt {
        struct ast_translator *step;
        struct ast_translator_pvt *state;
        struct ast_trans_pvt *next;
+       struct timeval nextin;
+       struct timeval nextout;
 };
 
 
@@ -75,10 +74,11 @@ static int powerof(int d)
 
 void ast_translator_free_path(struct ast_trans_pvt *p)
 {
-       struct ast_trans_pvt *pl;
-       while(p) {
-               pl = p;
-               p = p->next;
+       struct ast_trans_pvt *pl, *pn;
+       pn = p;
+       while(pn) {
+               pl = pn;
+               pn = pn->next;
                if (pl->state && pl->step->destroy)
                        pl->step->destroy(pl->state);
                free(pl);
@@ -103,11 +103,17 @@ struct ast_trans_pvt *ast_translator_build_path(int dest, int source)
                                
                        if (tmp) {
                                tmp->next = NULL;
+                               tmp->nextin.tv_sec = 0;
+                               tmp->nextin.tv_usec = 0;
+                               tmp->nextout.tv_sec = 0;
+                               tmp->nextout.tv_usec = 0;
                                tmp->step = tr_matrix[source][dest].step;
                                tmp->state = tmp->step->new();
                                if (!tmp->state) {
+                                       ast_log(LOG_WARNING, "Failed to build translator step from %d to %d\n", source, dest);
                                        free(tmp);
                                        tmp = NULL;
+                                       return NULL;
                                }
                                /* Set the root, if it doesn't exist yet... */
                                if (!tmpr)
@@ -121,7 +127,8 @@ struct ast_trans_pvt *ast_translator_build_path(int dest, int source)
                        }
                } else {
                        /* We shouldn't have allocated any memory */
-                       ast_log(LOG_WARNING, "No translator path from %d to %d\n", source, dest);
+                       ast_log(LOG_WARNING, "No translator path from %s to %s\n", 
+                               ast_getformatname(source), ast_getformatname(dest));
                        return NULL;
                }
        }
@@ -132,9 +139,51 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f,
 {
        struct ast_trans_pvt *p;
        struct ast_frame *out;
+       struct timeval delivery;
        p = path;
        /* Feed the first frame into the first translator */
        p->step->framein(p->state, f);
+       if (f->delivery.tv_sec || f->delivery.tv_usec) {
+               if (path->nextin.tv_sec || path->nextin.tv_usec) {
+                       /* Make sure this is in line with what we were expecting */
+                       if ((path->nextin.tv_sec != f->delivery.tv_sec) ||
+                           (path->nextin.tv_usec != f->delivery.tv_usec)) {
+                               /* The time has changed between what we expected and this
+                                  most recent time on the new packet.  Adjust our output
+                                  time appropriately */
+                               long sdiff;
+                               long udiff;
+                               sdiff = f->delivery.tv_sec - path->nextin.tv_sec;
+                               udiff = f->delivery.tv_usec - path->nextin.tv_usec;
+                               path->nextin.tv_sec = f->delivery.tv_sec;
+                               path->nextin.tv_usec = f->delivery.tv_usec;
+                               path->nextout.tv_sec += sdiff;
+                               path->nextout.tv_usec += udiff;
+                               if (path->nextout.tv_usec < 0) {
+                                       path->nextout.tv_usec += 1000000;
+                                       path->nextout.tv_sec--;
+                               } else if (path->nextout.tv_usec >= 1000000) {
+                                       path->nextout.tv_usec -= 1000000;
+                                       path->nextout.tv_sec++;
+                               }
+                       }
+               } else {
+                       /* This is our first pass.  Make sure the timing looks good */
+                       path->nextin.tv_sec = f->delivery.tv_sec;
+                       path->nextin.tv_usec = f->delivery.tv_usec;
+                       path->nextout.tv_sec = f->delivery.tv_sec;
+                       path->nextout.tv_usec = f->delivery.tv_usec;
+               }
+               /* Predict next incoming sample */
+               path->nextin.tv_sec += (f->samples / 8000);
+               path->nextin.tv_usec += ((f->samples % 8000) * 125);
+               if (path->nextin.tv_usec >= 1000000) {
+                       path->nextin.tv_usec -= 1000000;
+                       path->nextin.tv_sec++;
+               }
+       }
+       delivery.tv_sec = f->delivery.tv_sec;
+       delivery.tv_usec = f->delivery.tv_usec;
        if (consume)
                ast_frfree(f);
        while(p) {
@@ -146,15 +195,81 @@ struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f,
                   return this frame  */
                if (p->next) 
                        p->next->step->framein(p->next->state, out);
-               else
+               else {
+                       if (delivery.tv_sec || delivery.tv_usec) {
+                               /* Use next predicted outgoing timestamp */
+                               out->delivery.tv_sec = path->nextout.tv_sec;
+                               out->delivery.tv_usec = path->nextout.tv_usec;
+                               
+                               /* Predict next outgoing timestamp from samples in this
+                                  frame. */
+                               path->nextout.tv_sec += (out->samples / 8000);
+                               path->nextout.tv_usec += ((out->samples % 8000) * 125);
+                               if (path->nextout.tv_usec >= 1000000) {
+                                       path->nextout.tv_sec++;
+                                       path->nextout.tv_usec -= 1000000;
+                               }
+                       } else {
+                               out->delivery.tv_sec = 0;
+                               out->delivery.tv_usec = 0;
+                       }
                        return out;
+               }
                p = p->next;
        }
        ast_log(LOG_WARNING, "I should never get here...\n");
        return NULL;
 }
 
-static void rebuild_matrix()
+
+static void calc_cost(struct ast_translator *t,int samples)
+{
+       int sofar=0;
+       struct ast_translator_pvt *pvt;
+       struct ast_frame *f, *out;
+       struct timeval start, finish;
+       int cost;
+       if(!samples)
+         samples = 1;
+       
+       /* If they don't make samples, give them a terrible score */
+       if (!t->sample) {
+               ast_log(LOG_WARNING, "Translator '%s' does not produce sample frames.\n", t->name);
+               t->cost = 99999;
+               return;
+       }
+       pvt = t->new();
+       if (!pvt) {
+               ast_log(LOG_WARNING, "Translator '%s' appears to be broken and will probably fail.\n", t->name);
+               t->cost = 99999;
+               return;
+       }
+       gettimeofday(&start, NULL);
+       /* Call the encoder until we've processed one second of time */
+       while(sofar < samples * 8000) {
+               f = t->sample();
+               if (!f) {
+                       ast_log(LOG_WARNING, "Translator '%s' failed to produce a sample frame.\n", t->name);
+                       t->destroy(pvt);
+                       t->cost = 99999;
+                       return;
+               }
+               t->framein(pvt, f);
+               ast_frfree(f);
+               while((out = t->frameout(pvt))) {
+                       sofar += out->samples;
+                       ast_frfree(out);
+               }
+       }
+       gettimeofday(&finish, NULL);
+       t->destroy(pvt);
+       cost = (finish.tv_sec - start.tv_sec) * 1000 + (finish.tv_usec - start.tv_usec) / 1000;
+       t->cost = cost / samples;
+       if (!t->cost)
+               t->cost = 1;
+}
+
+static void rebuild_matrix(int samples)
 {
        struct ast_translator *t;
        int changed;
@@ -165,6 +280,9 @@ static void rebuild_matrix()
        bzero(tr_matrix, sizeof(tr_matrix));
        t = list;
        while(t) {
+         if(samples)
+           calc_cost(t,samples);
+         
                if (!tr_matrix[t->srcfmt][t->dstfmt].step ||
                     tr_matrix[t->srcfmt][t->dstfmt].cost > t->cost) {
                        tr_matrix[t->srcfmt][t->dstfmt].step = t;
@@ -195,122 +313,108 @@ static void rebuild_matrix()
                                                                        tr_matrix[x][z].cost = tr_matrix[x][y].cost + 
                                                                                                                   tr_matrix[y][z].cost;
                                                                        if (option_debug)
-                                                                               ast_log(LOG_DEBUG, "Discovered %d cost path from %d to %d, via %d\n", tr_matrix[x][z].cost, x, z, y);
+                                                                               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);
                                                                        changed++;
                                                                 }
                
        } while (changed);
 }
 
-static void calc_cost(struct ast_translator *t)
-{
-       int sofar=0;
-       struct ast_translator_pvt *pvt;
-       struct ast_frame *f, *out;
-       struct timeval start, finish;
-       int cost;
-       /* If they don't make samples, give them a terrible score */
-       if (!t->sample) {
-               ast_log(LOG_WARNING, "Translator '%s' does not produce sample frames.\n", t->name);
-               t->cost = 99999;
-               return;
-       }
-       pvt = t->new();
-       if (!pvt) {
-               ast_log(LOG_WARNING, "Translator '%s' appears to be broken and will probably fail.\n", t->name);
-               t->cost = 99999;
-               return;
-       }
-       gettimeofday(&start, NULL);
-       /* Call the encoder until we've processed one second of time */
-       while(sofar < 1000) {
-               f = t->sample();
-               if (!f) {
-                       ast_log(LOG_WARNING, "Translator '%s' failed to produce a sample frame.\n", t->name);
-                       t->destroy(pvt);
-                       t->cost = 99999;
-                       return;
-               }
-               t->framein(pvt, f);
-               ast_frfree(f);
-               while((out = t->frameout(pvt))) {
-                       sofar += out->timelen;
-                       ast_frfree(out);
-               }
-       }
-       gettimeofday(&finish, NULL);
-       t->destroy(pvt);
-       cost = (finish.tv_sec - start.tv_sec) * 1000 + (finish.tv_usec - start.tv_usec) / 1000;
-       t->cost = cost;
-}
+
+
+
 
 static int show_translation(int fd, int argc, char *argv[])
 {
-#define SHOW_TRANS 14
-       int x,y;
+#define SHOW_TRANS 11
+        int x,y,z;
        char line[80];
-       if (argc != 2) 
+       if (argc > 4) 
                return RESULT_SHOWUSAGE;
-       ast_cli(fd, "                        Translation times between formats (in milliseconds)\n");
-       ast_cli(fd, "                                 Destination Format\n");
-       ast_pthread_mutex_lock(&list_lock);
-       for (x=0;x<SHOW_TRANS; x++) {
-               if (x == 1) 
-                       strcpy(line, "  Src  ");
-               else if (x == 2)
-                       strcpy(line, "  Fmt  ");
-               else
-                       strcpy(line, "       ");
-               for (y=0;y<SHOW_TRANS;y++) {
-                       if (tr_matrix[x][y].step)
-                               snprintf(line + strlen(line), sizeof(line) - strlen(line), " %4d", tr_matrix[x][y].cost);
+
+       if(argv[2] && !strcasecmp(argv[2],"recalc")) {
+         z = argv[3] ? atoi(argv[3]) : 1;
+
+         if(z <= 0) {
+           ast_cli(fd,"         C'mon let's be serious here... defaulting to 1.\n");
+           z = 1;
+         }
+         
+         if(z > MAX_RECALC) {
+           ast_cli(fd,"         Maximum limit of recalc exceeded by %d, truncating value to %d\n",z-MAX_RECALC,MAX_RECALC);
+           z = MAX_RECALC;
+         }
+         ast_cli(fd,"         Recalculating Codec Translation (number of sample seconds: %d)\n\n",z);
+         rebuild_matrix(z);
+
+       }
+
+       ast_cli(fd, "         Translation times between formats (in milliseconds)\n");
+       ast_cli(fd, "          Source Format (Rows) Destination Format(Columns)\n\n");
+       ast_mutex_lock(&list_lock);
+       for (x=-1;x<SHOW_TRANS; x++) {
+               strcpy(line, " ");
+               for (y=-1;y<SHOW_TRANS;y++) {
+                       if (x >= 0 && y >= 0 && tr_matrix[x][y].step)
+                               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);
                        else
-                               snprintf(line + strlen(line), sizeof(line) - strlen(line), "  n/a");
+                               if (((x == -1 && y >= 0) || (y == -1 && x >= 0))) {
+                                       snprintf(line + strlen(line), sizeof(line) - strlen(line), 
+                                               " %5s", ast_getformatname(1<<(x+y+1)) );
+                               } else if (x != -1 && y != -1) {
+                                       snprintf(line + strlen(line), sizeof(line) - strlen(line), "     -");
+                               } else {
+                                       snprintf(line + strlen(line), sizeof(line) - strlen(line), "      ");
+                               }
                }
                snprintf(line + strlen(line), sizeof(line) - strlen(line), "\n");
                ast_cli(fd, line);                      
        }
-       ast_pthread_mutex_unlock(&list_lock);
+       ast_mutex_unlock(&list_lock);
        return RESULT_SUCCESS;
 }
 
 static int added_cli = 0;
 
 static char show_trans_usage[] =
-"Usage: show translation\n"
+"Usage: show translation [recalc] [<recalc seconds>]\n"
 "       Displays known codec translators and the cost associated\n"
-"with each conversion.\n";
+"with each conversion.  if the arguement 'recalc' is supplied along\n"
+"with optional number of seconds to test a new test will be performed\n"
+"as the chart is being displayed.\n";
 
 static struct ast_cli_entry show_trans =
 { { "show", "translation", NULL }, show_translation, "Display translation matrix", show_trans_usage };
 
 int ast_register_translator(struct ast_translator *t)
 {
+       char tmp[80];
        t->srcfmt = powerof(t->srcfmt);
        t->dstfmt = powerof(t->dstfmt);
        if ((t->srcfmt >= MAX_FORMAT) || (t->dstfmt >= MAX_FORMAT)) {
-               ast_log(LOG_WARNING, "Format %d is larger than MAX_FORMAT\n", t->srcfmt);
+               ast_log(LOG_WARNING, "Format %s is larger than MAX_FORMAT\n", ast_getformatname(t->srcfmt));
                return -1;
        }
-       calc_cost(t);
+       calc_cost(t,1);
        if (option_verbose > 1)
-               ast_verbose(VERBOSE_PREFIX_2 "Registered translator '%s' from format %d to %d, cost %d\n", t->name, t->srcfmt, t->dstfmt, t->cost);
-       ast_pthread_mutex_lock(&list_lock);
+               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);
+       ast_mutex_lock(&list_lock);
        if (!added_cli) {
                ast_cli_register(&show_trans);
                added_cli++;
        }
        t->next = list;
        list = t;
-       rebuild_matrix();
-       ast_pthread_mutex_unlock(&list_lock);
+       rebuild_matrix(0);
+       ast_mutex_unlock(&list_lock);
        return 0;
 }
 
 int ast_unregister_translator(struct ast_translator *t)
 {
+       char tmp[80];
        struct ast_translator *u, *ul = NULL;
-       ast_pthread_mutex_lock(&list_lock);
+       ast_mutex_lock(&list_lock);
        u = list;
        while(u) {
                if (u == t) {
@@ -318,13 +422,15 @@ int ast_unregister_translator(struct ast_translator *t)
                                ul->next = u->next;
                        else
                                list = u->next;
+                       if (option_verbose > 1)
+                               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));
                        break;
                }
                ul = u;
                u = u->next;
        }
-       rebuild_matrix();
-       ast_pthread_mutex_unlock(&list_lock);
+       rebuild_matrix(0);
+       ast_mutex_unlock(&list_lock);
        return (u ? 0 : -1);
 }
 
@@ -336,7 +442,7 @@ int ast_translator_best_choice(int *dst, int *srcs)
        int bestdst=0;
        int cur = 1;
        int besttime=999999999;
-       ast_pthread_mutex_lock(&list_lock);
+       ast_mutex_lock(&list_lock);
        for (y=0;y<MAX_FORMAT;y++) {
                if ((cur & *dst) && (cur & *srcs)) {
                        /* This is a common format to both.  Pick it if we don't have one already */
@@ -363,6 +469,6 @@ int ast_translator_best_choice(int *dst, int *srcs)
                *dst = bestdst;
                best = 0;
        }
-       ast_pthread_mutex_unlock(&list_lock);
+       ast_mutex_unlock(&list_lock);
        return best;
 }