Staticize one, and Constify a bunch of usage strings for CLI commands.
[asterisk/asterisk.git] / main / translate.c
index 7845a24..5fc5101 100644 (file)
@@ -49,7 +49,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #define MAX_RECALC 200 /* max sample recalc */
 
 /*! \brief the list of translators */
-static AST_LIST_HEAD_STATIC(translators, ast_translator);
+static AST_RWLIST_HEAD_STATIC(translators, ast_translator);
 
 struct translator_path {
        struct ast_translator *step;    /*!< Next step translator */
@@ -63,6 +63,9 @@ struct translator_path {
  * until step->dstfmt == desired_format.
  *
  * Array indexes are 'src' and 'dest', in that order.
+ *
+ * Note: the lock in the 'translators' list is also used to protect
+ * this structure.
  */
 static struct translator_path tr_matrix[MAX_FORMAT][MAX_FORMAT];
 
@@ -169,6 +172,7 @@ static int framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
                                }
                                l = plc_fillin(pvt->plc, dst + pvt->samples, l);
                                pvt->samples += l;
+                               pvt->datalen = pvt->samples * 2;        /* SLIN has 2bytes for 1sample */
                        }
                        return 0;
                }
@@ -253,18 +257,22 @@ struct ast_trans_pvt *ast_translator_build_path(int dest, int source)
        source = powerof(source);
        dest = powerof(dest);
        
+       AST_RWLIST_RDLOCK(&translators);
+
        while (source != dest) {
                struct ast_trans_pvt *cur;
                struct ast_translator *t = tr_matrix[source][dest].step;
                if (!t) {
                        ast_log(LOG_WARNING, "No translator path from %s to %s\n", 
                                ast_getformatname(source), ast_getformatname(dest));
+                       AST_RWLIST_UNLOCK(&translators);
                        return NULL;
                }
                if (!(cur = newpvt(t))) {
                        ast_log(LOG_WARNING, "Failed to build translator step from %d to %d\n", source, dest);
                        if (head)
                                ast_translator_free_path(head); 
+                       AST_RWLIST_UNLOCK(&translators);
                        return NULL;
                }
                if (!head)
@@ -276,6 +284,8 @@ struct ast_trans_pvt *ast_translator_build_path(int dest, int source)
                /* Keep going if this isn't the final destination */
                source = cur->t->dstfmt;
        }
+
+       AST_RWLIST_UNLOCK(&translators);
        return head;
 }
 
@@ -417,7 +427,10 @@ static void rebuild_matrix(int samples)
        bzero(tr_matrix, sizeof(tr_matrix));
 
        /* first, compute all direct costs */
-       AST_LIST_TRAVERSE(&translators, t, list) {
+       AST_RWLIST_TRAVERSE(&translators, t, list) {
+               if (!t->active)
+                       continue;
+
                x = t->srcfmt;
                z = t->dstfmt;
 
@@ -476,14 +489,12 @@ static void rebuild_matrix(int samples)
 
 static int show_translation(int fd, int argc, char *argv[])
 {
-#define SHOW_TRANS 12
+#define SHOW_TRANS 13
        int x, y, z;
        int curlen = 0, longest = 0;
 
        if (argc > 5)
                return RESULT_SHOWUSAGE;
-
-       AST_LIST_LOCK(&translators);    
        
        if (argv[3] && !strcasecmp(argv[3], "recalc")) {
                z = argv[4] ? atoi(argv[4]) : 1;
@@ -498,9 +509,13 @@ static int show_translation(int fd, int argc, char *argv[])
                        z = MAX_RECALC;
                }
                ast_cli(fd, "         Recalculating Codec Translation (number of sample seconds: %d)\n\n", z);
+               AST_RWLIST_WRLOCK(&translators);
                rebuild_matrix(z);
+               AST_RWLIST_UNLOCK(&translators);
        }
 
+       AST_RWLIST_RDLOCK(&translators);
+
        ast_cli(fd, "         Translation times between formats (in milliseconds) for one second of data\n");
        ast_cli(fd, "          Source Format (Rows) Destination Format (Columns)\n\n");
        /* Get the length of the longest (usable?) codec name, so we know how wide the left side should be */
@@ -510,7 +525,7 @@ static int show_translation(int fd, int argc, char *argv[])
                        longest = curlen;
        }
        for (x = -1; x < SHOW_TRANS; x++) {
-               char line[80];
+               char line[120];
                char *buf = line;
                size_t left = sizeof(line) - 1; /* one initial space */
                /* next 2 lines run faster than using ast_build_string() */
@@ -539,11 +554,11 @@ static int show_translation(int fd, int argc, char *argv[])
                ast_build_string(&buf, &left, "\n");
                ast_cli(fd, line);                      
        }
-       AST_LIST_UNLOCK(&translators);
+       AST_RWLIST_UNLOCK(&translators);
        return RESULT_SUCCESS;
 }
 
-static char show_trans_usage[] =
+static const char show_trans_usage[] =
 "Usage: core show translation [recalc] [<recalc seconds>]\n"
 "       Displays known codec translators and the cost associated\n"
 "with each conversion.  If the argument 'recalc' is supplied along\n"
@@ -560,6 +575,7 @@ static struct ast_cli_entry cli_translate[] = {
 int __ast_register_translator(struct ast_translator *t, struct ast_module *mod)
 {
        static int added_cli = 0;
+       struct ast_translator *u;
 
        if (!mod) {
                ast_log(LOG_WARNING, "Missing module pointer, you need to supply one\n");
@@ -572,6 +588,11 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod)
        }
 
        t->module = mod;
+
+       t->srcfmt = powerof(t->srcfmt);
+       t->dstfmt = powerof(t->dstfmt);
+       t->active = 1;
+
        if (t->plc_samples) {
                if (t->buffer_samples < t->plc_samples) {
                        ast_log(LOG_WARNING, "plc_samples %d buffer_samples %d\n",
@@ -582,17 +603,16 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod)
                        ast_log(LOG_WARNING, "plc_samples %d format %x\n",
                                t->plc_samples, t->dstfmt);
        }
-       t->srcfmt = powerof(t->srcfmt);
-       t->dstfmt = powerof(t->dstfmt);
-       /* XXX maybe check that it is not existing yet ? */
        if (t->srcfmt >= MAX_FORMAT) {
                ast_log(LOG_WARNING, "Source format %s is larger than MAX_FORMAT\n", ast_getformatname(t->srcfmt));
                return -1;
        }
+
        if (t->dstfmt >= MAX_FORMAT) {
                ast_log(LOG_WARNING, "Destination format %s is larger than MAX_FORMAT\n", ast_getformatname(t->dstfmt));
                return -1;
        }
+
        if (t->buf_size) {
                /*
                * Align buf_size properly, rounding up to the machine-specific
@@ -600,26 +620,51 @@ int __ast_register_translator(struct ast_translator *t, struct ast_module *mod)
                */
                struct _test_align { void *a, *b; } p;
                int align = (char *)&p.b - (char *)&p.a;
+
                t->buf_size = ((t->buf_size + align - 1) / align) * align;
        }
+
        if (t->frameout == NULL)
                t->frameout = default_frameout;
   
        calc_cost(t, 1);
+
        if (option_verbose > 1) {
                char tmp[80];
+
                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);
+                           term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)),
+                           ast_getformatname(1 << t->srcfmt), ast_getformatname(1 << t->dstfmt), t->cost);
        }
-       AST_LIST_LOCK(&translators);
+
        if (!added_cli) {
                ast_cli_register_multiple(cli_translate, sizeof(cli_translate) / sizeof(struct ast_cli_entry));
                added_cli++;
        }
-       AST_LIST_INSERT_HEAD(&translators, t, list);
+
+       AST_RWLIST_WRLOCK(&translators);
+
+       /* find any existing translators that provide this same srcfmt/dstfmt,
+          and put this one in order based on cost */
+       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) {
+               if ((u->srcfmt == t->srcfmt) &&
+                   (u->dstfmt == t->dstfmt) &&
+                   (u->cost > t->cost)) {
+                       AST_RWLIST_INSERT_BEFORE_CURRENT(&translators, t, list);
+                       t = NULL;
+               }
+       }
+       AST_RWLIST_TRAVERSE_SAFE_END;
+
+       /* if no existing translator was found for this format combination,
+          add it to the beginning of the list */
+       if (t)
+               AST_RWLIST_INSERT_HEAD(&translators, t, list);
+
        rebuild_matrix(0);
-       AST_LIST_UNLOCK(&translators);
+
+       AST_RWLIST_UNLOCK(&translators);
+
        return 0;
 }
 
@@ -628,21 +673,44 @@ int ast_unregister_translator(struct ast_translator *t)
 {
        char tmp[80];
        struct ast_translator *u;
-       AST_LIST_LOCK(&translators);
-       AST_LIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) {
+       int found = 0;
+
+       AST_RWLIST_WRLOCK(&translators);
+       AST_RWLIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) {
                if (u == t) {
-                       AST_LIST_REMOVE_CURRENT(&translators, list);
+                       AST_RWLIST_REMOVE_CURRENT(&translators, list);
                        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));
+                       found = 1;
                        break;
                }
        }
-       AST_LIST_TRAVERSE_SAFE_END
-       rebuild_matrix(0);
-       AST_LIST_UNLOCK(&translators);
+       AST_RWLIST_TRAVERSE_SAFE_END;
+
+       if (found)
+               rebuild_matrix(0);
+
+       AST_RWLIST_UNLOCK(&translators);
+
        return (u ? 0 : -1);
 }
 
+void ast_translator_activate(struct ast_translator *t)
+{
+       AST_RWLIST_WRLOCK(&translators);
+       t->active = 1;
+       rebuild_matrix(0);
+       AST_RWLIST_UNLOCK(&translators);
+}
+
+void ast_translator_deactivate(struct ast_translator *t)
+{
+       AST_RWLIST_WRLOCK(&translators);
+       t->active = 0;
+       rebuild_matrix(0);
+       AST_RWLIST_UNLOCK(&translators);
+}
+
 /*! \brief Calculate our best translator source format, given costs, and a desired destination */
 int ast_translator_best_choice(int *dst, int *srcs)
 {
@@ -663,7 +731,7 @@ int ast_translator_best_choice(int *dst, int *srcs)
                *srcs = *dst = cur;
                return 0;
        } else {        /* No, we will need to translate */
-               AST_LIST_LOCK(&translators);
+               AST_RWLIST_RDLOCK(&translators);
                for (cur = 1, y = 0; y < MAX_FORMAT; cur <<= 1, y++) {
                        if (! (cur & *dst))
                                continue;
@@ -681,7 +749,7 @@ int ast_translator_best_choice(int *dst, int *srcs)
                                }
                        }
                }
-               AST_LIST_UNLOCK(&translators);
+               AST_RWLIST_UNLOCK(&translators);
                if (best > -1) {
                        *srcs = best;
                        *dst = bestdst;
@@ -693,12 +761,97 @@ int ast_translator_best_choice(int *dst, int *srcs)
 
 unsigned int ast_translate_path_steps(unsigned int dest, unsigned int src)
 {
+       unsigned int res = -1;
+
        /* convert bitwise format numbers into array indices */
        src = powerof(src);
        dest = powerof(dest);
-       if (!tr_matrix[src][dest].step)
-               return -1;
-       else
-               return tr_matrix[src][dest].multistep + 1;
+
+       AST_RWLIST_RDLOCK(&translators);
+
+       if (tr_matrix[src][dest].step)
+               res = tr_matrix[src][dest].multistep + 1;
+
+       AST_RWLIST_UNLOCK(&translators);
+
+       return res;
 }
 
+unsigned int ast_translate_available_formats(unsigned int dest, unsigned int src)
+{
+       unsigned int res = dest;
+       unsigned int x;
+       unsigned int src_audio = src & AST_FORMAT_AUDIO_MASK;
+       unsigned int src_video = src & AST_FORMAT_VIDEO_MASK;
+
+       /* if we don't have a source format, we just have to try all
+          possible destination formats */
+       if (!src)
+               return dest;
+
+       /* If we have a source audio format, get its format index */
+       if (src_audio)
+               src_audio = powerof(src_audio);
+
+       /* If we have a source video format, get its format index */
+       if (src_video)
+               src_video = powerof(src_video);
+
+       AST_RWLIST_RDLOCK(&translators);
+
+       /* For a given source audio format, traverse the list of
+          known audio formats to determine whether there exists
+          a translation path from the source format to the
+          destination format. */
+       for (x = 1; src_audio && x < AST_FORMAT_MAX_AUDIO; x <<= 1) {
+               /* if this is not a desired format, nothing to do */
+               if (!dest & x)
+                       continue;
+
+               /* if the source is supplying this format, then
+                  we can leave it in the result */
+               if (src & x)
+                       continue;
+
+               /* if we don't have a translation path from the src
+                  to this format, remove it from the result */
+               if (!tr_matrix[src_audio][powerof(x)].step) {
+                       res &= ~x;
+                       continue;
+               }
+
+               /* now check the opposite direction */
+               if (!tr_matrix[powerof(x)][src_audio].step)
+                       res &= ~x;
+       }
+
+       /* For a given source video format, traverse the list of
+          known video formats to determine whether there exists
+          a translation path from the source format to the
+          destination format. */
+       for (; src_video && x < AST_FORMAT_MAX_VIDEO; x <<= 1) {
+               /* if this is not a desired format, nothing to do */
+               if (!dest & x)
+                       continue;
+
+               /* if the source is supplying this format, then
+                  we can leave it in the result */
+               if (src & x)
+                       continue;
+
+               /* if we don't have a translation path from the src
+                  to this format, remove it from the result */
+               if (!tr_matrix[src_video][powerof(x)].step) {
+                       res &= ~x;
+                       continue;
+               }
+
+               /* now check the opposite direction */
+               if (!tr_matrix[powerof(x)][src_video].step)
+                       res &= ~x;
+       }
+
+       AST_RWLIST_UNLOCK(&translators);
+
+       return res;
+}