00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026 #include "asterisk.h"
00027
00028 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 308624 $")
00029
00030 #include <sys/time.h>
00031 #include <sys/resource.h>
00032 #include <math.h>
00033
00034 #include "asterisk/lock.h"
00035 #include "asterisk/channel.h"
00036 #include "asterisk/translate.h"
00037 #include "asterisk/module.h"
00038 #include "asterisk/frame.h"
00039 #include "asterisk/sched.h"
00040 #include "asterisk/cli.h"
00041 #include "asterisk/term.h"
00042
00043
00044
00045
00046
00047
00048
00049
00050 #define MAX_RECALC 1000
00051
00052
00053 static AST_RWLIST_HEAD_STATIC(translators, ast_translator);
00054
00055 struct translator_path {
00056 struct ast_translator *step;
00057 uint32_t table_cost;
00058 uint8_t multistep;
00059 };
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072 static struct translator_path **__matrix;
00073
00074
00075
00076
00077
00078
00079 static int *__indextable;
00080
00081
00082 static ast_rwlock_t tablelock;
00083
00084
00085 #define INIT_INDEX 32
00086
00087 #define GROW_INDEX 16
00088
00089
00090 static int cur_max_index;
00091
00092 static int index_size;
00093
00094 static void matrix_rebuild(int samples);
00095
00096
00097
00098
00099
00100 static int format2index(enum ast_format_id id)
00101 {
00102 int x;
00103
00104 ast_rwlock_rdlock(&tablelock);
00105 for (x = 0; x < cur_max_index; x++) {
00106 if (__indextable[x] == id) {
00107
00108 ast_rwlock_unlock(&tablelock);
00109 return x;
00110 }
00111 }
00112 ast_rwlock_unlock(&tablelock);
00113 return -1;
00114 }
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125 static int add_format2index(enum ast_format_id id)
00126 {
00127 if (format2index(id) != -1) {
00128
00129 return 0;
00130 }
00131
00132 ast_rwlock_wrlock(&tablelock);
00133 if (cur_max_index == (index_size)) {
00134 ast_rwlock_unlock(&tablelock);
00135 return -1;
00136 }
00137 __indextable[cur_max_index] = id;
00138 cur_max_index++;
00139 ast_rwlock_unlock(&tablelock);
00140
00141 return 0;
00142 }
00143
00144
00145
00146
00147
00148 static enum ast_format_id index2format(int index)
00149 {
00150 enum ast_format_id format_id;
00151
00152 if (index >= cur_max_index) {
00153 return 0;
00154 }
00155 ast_rwlock_rdlock(&tablelock);
00156 format_id = __indextable[index];
00157 ast_rwlock_unlock(&tablelock);
00158
00159 return format_id;
00160 }
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172 static int matrix_resize(int init)
00173 {
00174 struct translator_path **tmp_matrix = NULL;
00175 int *tmp_table = NULL;
00176 int old_index;
00177 int x;
00178
00179 AST_RWLIST_WRLOCK(&translators);
00180 ast_rwlock_wrlock(&tablelock);
00181
00182 old_index = index_size;
00183 if (init) {
00184 index_size += INIT_INDEX;
00185 } else {
00186 index_size += GROW_INDEX;
00187 }
00188
00189
00190 if (!(tmp_matrix = ast_calloc(1, sizeof(struct translator_path *) * (index_size)))) {
00191 goto resize_cleanup;
00192 }
00193
00194 for (x = 0; x < index_size; x++) {
00195 if (!(tmp_matrix[x] = ast_calloc(1, sizeof(struct translator_path) * (index_size)))) {
00196 goto resize_cleanup;
00197 }
00198 }
00199
00200
00201 if (!(tmp_table = ast_calloc(1, sizeof(int) * index_size))) {
00202 goto resize_cleanup;
00203 }
00204
00205
00206 if (!init) {
00207 for (x = 0; x < old_index; x++) {
00208 ast_free(__matrix[x]);
00209 }
00210 ast_free(__matrix);
00211
00212 memcpy(tmp_table, __indextable, sizeof(int) * old_index);
00213 ast_free(__indextable);
00214 }
00215
00216
00217 __matrix = tmp_matrix;
00218 __indextable = tmp_table;
00219
00220 matrix_rebuild(0);
00221 ast_rwlock_unlock(&tablelock);
00222 AST_RWLIST_UNLOCK(&translators);
00223
00224 return 0;
00225
00226 resize_cleanup:
00227 ast_rwlock_unlock(&tablelock);
00228 AST_RWLIST_UNLOCK(&translators);
00229 if (tmp_matrix) {
00230 for (x = 0; x < index_size; x++) {
00231 ast_free(tmp_matrix[x]);
00232 }
00233 ast_free(tmp_matrix);
00234 }
00235 ast_free(tmp_table);
00236
00237 return -1;
00238 }
00239
00240
00241
00242
00243
00244
00245
00246 static void matrix_clear(void)
00247 {
00248 int x;
00249 for (x = 0; x < index_size; x++) {
00250 memset(__matrix[x], '\0', sizeof(struct translator_path) * (index_size));
00251 }
00252 }
00253
00254
00255
00256
00257
00258
00259
00260 static struct translator_path *matrix_get(unsigned int x, unsigned int y)
00261 {
00262 if (!(x >= 0 && y >= 0)) {
00263 return NULL;
00264 }
00265 return __matrix[x] + y;
00266 }
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276 static void *newpvt(struct ast_translator *t, const struct ast_format *explicit_dst)
00277 {
00278 struct ast_trans_pvt *pvt;
00279 int len;
00280 char *ofs;
00281
00282
00283
00284
00285
00286 len = sizeof(*pvt) + t->desc_size;
00287 if (t->buf_size)
00288 len += AST_FRIENDLY_OFFSET + t->buf_size;
00289 pvt = ast_calloc(1, len);
00290 if (!pvt) {
00291 return NULL;
00292 }
00293 pvt->t = t;
00294 ofs = (char *)(pvt + 1);
00295 if (t->desc_size) {
00296 pvt->pvt = ofs;
00297 ofs += t->desc_size;
00298 }
00299 if (t->buf_size) {
00300 pvt->outbuf.c = ofs + AST_FRIENDLY_OFFSET;
00301 }
00302
00303
00304 if (explicit_dst) {
00305 ast_format_copy(&pvt->explicit_dst, explicit_dst);
00306 }
00307
00308 if (t->newpvt && t->newpvt(pvt)) {
00309 ast_free(pvt);
00310 return NULL;
00311 }
00312 ast_module_ref(t->module);
00313 return pvt;
00314 }
00315
00316 static void destroy(struct ast_trans_pvt *pvt)
00317 {
00318 struct ast_translator *t = pvt->t;
00319
00320 if (t->destroy)
00321 t->destroy(pvt);
00322 ast_free(pvt);
00323 ast_module_unref(t->module);
00324 }
00325
00326
00327 static int framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
00328 {
00329 int ret;
00330 int samples = pvt->samples;
00331
00332
00333 ast_copy_flags(&pvt->f, f, AST_FRFLAG_HAS_TIMING_INFO);
00334 pvt->f.ts = f->ts;
00335 pvt->f.len = f->len;
00336 pvt->f.seqno = f->seqno;
00337
00338 if (f->samples == 0) {
00339 ast_log(LOG_WARNING, "no samples for %s\n", pvt->t->name);
00340 }
00341 if (pvt->t->buffer_samples) {
00342 if (f->datalen == 0) {
00343
00344 if (!pvt->t->native_plc)
00345 return 0;
00346 }
00347 if (pvt->samples + f->samples > pvt->t->buffer_samples) {
00348 ast_log(LOG_WARNING, "Out of buffer space\n");
00349 return -1;
00350 }
00351 }
00352
00353
00354
00355 ret = pvt->t->framein(pvt, f);
00356
00357 if (pvt->samples == samples)
00358 ast_log(LOG_WARNING, "%s did not update samples %d\n",
00359 pvt->t->name, pvt->samples);
00360 return ret;
00361 }
00362
00363
00364
00365
00366
00367
00368 struct ast_frame *ast_trans_frameout(struct ast_trans_pvt *pvt,
00369 int datalen, int samples)
00370 {
00371 struct ast_frame *f = &pvt->f;
00372
00373 if (samples) {
00374 f->samples = samples;
00375 } else {
00376 if (pvt->samples == 0)
00377 return NULL;
00378 f->samples = pvt->samples;
00379 pvt->samples = 0;
00380 }
00381 if (datalen) {
00382 f->datalen = datalen;
00383 } else {
00384 f->datalen = pvt->datalen;
00385 pvt->datalen = 0;
00386 }
00387
00388 f->frametype = AST_FRAME_VOICE;
00389 ast_format_copy(&f->subclass.format, &pvt->t->dst_format);
00390 f->mallocd = 0;
00391 f->offset = AST_FRIENDLY_OFFSET;
00392 f->src = pvt->t->name;
00393 f->data.ptr = pvt->outbuf.c;
00394
00395 return ast_frisolate(f);
00396 }
00397
00398 static struct ast_frame *default_frameout(struct ast_trans_pvt *pvt)
00399 {
00400 return ast_trans_frameout(pvt, 0, 0);
00401 }
00402
00403
00404
00405 void ast_translator_free_path(struct ast_trans_pvt *p)
00406 {
00407 struct ast_trans_pvt *pn = p;
00408 while ( (p = pn) ) {
00409 pn = p->next;
00410 destroy(p);
00411 }
00412 }
00413
00414
00415 struct ast_trans_pvt *ast_translator_build_path(struct ast_format *dst, struct ast_format *src)
00416 {
00417 struct ast_trans_pvt *head = NULL, *tail = NULL;
00418 int src_index, dst_index;
00419 struct ast_format tmp_fmt1;
00420 struct ast_format tmp_fmt2;
00421
00422 src_index = format2index(src->id);
00423 dst_index = format2index(dst->id);
00424
00425 if (src_index == -1 || dst_index == -1) {
00426 ast_log(LOG_WARNING, "No translator path: (%s codec is not valid)\n", src_index == -1 ? "starting" : "ending");
00427 return NULL;
00428 }
00429
00430 AST_RWLIST_RDLOCK(&translators);
00431
00432 while (src_index != dst_index) {
00433 struct ast_trans_pvt *cur;
00434 struct ast_format *explicit_dst = NULL;
00435 struct ast_translator *t = matrix_get(src_index, dst_index)->step;
00436 if (!t) {
00437 int src_id = index2format(src_index);
00438 int dst_id = index2format(dst_index);
00439 ast_log(LOG_WARNING, "No translator path from %s to %s\n",
00440 ast_getformatname(ast_format_set(&tmp_fmt1, src_id, 0)),
00441 ast_getformatname(ast_format_set(&tmp_fmt2, dst_id, 0)));
00442 AST_RWLIST_UNLOCK(&translators);
00443 return NULL;
00444 }
00445 if (dst_index == t->dst_fmt_index) {
00446 explicit_dst = dst;
00447 }
00448 if (!(cur = newpvt(t, explicit_dst))) {
00449 int src_id = index2format(src_index);
00450 int dst_id = index2format(dst_index);
00451 ast_log(LOG_WARNING, "Failed to build translator step from %s to %s\n",
00452 ast_getformatname(ast_format_set(&tmp_fmt1, src_id, 0)),
00453 ast_getformatname(ast_format_set(&tmp_fmt2, dst_id, 0)));
00454 if (head) {
00455 ast_translator_free_path(head);
00456 }
00457 AST_RWLIST_UNLOCK(&translators);
00458 return NULL;
00459 }
00460 if (!head) {
00461 head = cur;
00462 } else {
00463 tail->next = cur;
00464 }
00465 tail = cur;
00466 cur->nextin = cur->nextout = ast_tv(0, 0);
00467
00468 src_index = cur->t->dst_fmt_index;
00469 }
00470
00471 AST_RWLIST_UNLOCK(&translators);
00472 return head;
00473 }
00474
00475
00476 struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, int consume)
00477 {
00478 struct ast_trans_pvt *p = path;
00479 struct ast_frame *out;
00480 struct timeval delivery;
00481 int has_timing_info;
00482 long ts;
00483 long len;
00484 int seqno;
00485
00486 has_timing_info = ast_test_flag(f, AST_FRFLAG_HAS_TIMING_INFO);
00487 ts = f->ts;
00488 len = f->len;
00489 seqno = f->seqno;
00490
00491 if (!ast_tvzero(f->delivery)) {
00492 if (!ast_tvzero(path->nextin)) {
00493
00494 if (!ast_tveq(path->nextin, f->delivery)) {
00495
00496
00497
00498 if (!ast_tvzero(path->nextout)) {
00499 path->nextout = ast_tvadd(path->nextout,
00500 ast_tvsub(f->delivery, path->nextin));
00501 }
00502 path->nextin = f->delivery;
00503 }
00504 } else {
00505
00506 path->nextin = f->delivery;
00507 path->nextout = f->delivery;
00508 }
00509
00510 path->nextin = ast_tvadd(path->nextin, ast_samp2tv(f->samples, ast_format_rate(&f->subclass.format)));
00511 }
00512 delivery = f->delivery;
00513 for (out = f; out && p ; p = p->next) {
00514 framein(p, out);
00515 if (out != f) {
00516 ast_frfree(out);
00517 }
00518 out = p->t->frameout(p);
00519 }
00520 if (consume) {
00521 ast_frfree(f);
00522 }
00523 if (out == NULL) {
00524 return NULL;
00525 }
00526
00527 if (!ast_tvzero(delivery)) {
00528
00529 if (ast_tvzero(path->nextout)) {
00530 path->nextout = ast_tvnow();
00531 }
00532
00533
00534 out->delivery = path->nextout;
00535
00536
00537
00538 path->nextout = ast_tvadd(path->nextout, ast_samp2tv(out->samples, ast_format_rate(&out->subclass.format)));
00539 } else {
00540 out->delivery = ast_tv(0, 0);
00541 ast_set2_flag(out, has_timing_info, AST_FRFLAG_HAS_TIMING_INFO);
00542 if (has_timing_info) {
00543 out->ts = ts;
00544 out->len = len;
00545 out->seqno = seqno;
00546 }
00547 }
00548
00549 if (out->frametype == AST_FRAME_CNG) {
00550 path->nextout = ast_tv(0, 0);
00551 }
00552 return out;
00553 }
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564 static void generate_computational_cost(struct ast_translator *t, int seconds)
00565 {
00566 int num_samples = 0;
00567 struct ast_trans_pvt *pvt;
00568 struct rusage start;
00569 struct rusage end;
00570 int cost;
00571 int out_rate = ast_format_rate(&t->dst_format);
00572
00573 if (!seconds) {
00574 seconds = 1;
00575 }
00576
00577
00578 if (!t->sample) {
00579 ast_debug(3, "Translator '%s' does not produce sample frames.\n", t->name);
00580 t->comp_cost = 999999;
00581 return;
00582 }
00583
00584 pvt = newpvt(t, NULL);
00585 if (!pvt) {
00586 ast_log(LOG_WARNING, "Translator '%s' appears to be broken and will probably fail.\n", t->name);
00587 t->comp_cost = 999999;
00588 return;
00589 }
00590
00591 getrusage(RUSAGE_SELF, &start);
00592
00593
00594 while (num_samples < seconds * out_rate) {
00595 struct ast_frame *f = t->sample();
00596 if (!f) {
00597 ast_log(LOG_WARNING, "Translator '%s' failed to produce a sample frame.\n", t->name);
00598 destroy(pvt);
00599 t->comp_cost = 999999;
00600 return;
00601 }
00602 framein(pvt, f);
00603 ast_frfree(f);
00604 while ((f = t->frameout(pvt))) {
00605 num_samples += f->samples;
00606 ast_frfree(f);
00607 }
00608 }
00609
00610 getrusage(RUSAGE_SELF, &end);
00611
00612 cost = ((end.ru_utime.tv_sec - start.ru_utime.tv_sec) * 1000000) + end.ru_utime.tv_usec - start.ru_utime.tv_usec;
00613 cost += ((end.ru_stime.tv_sec - start.ru_stime.tv_sec) * 1000000) + end.ru_stime.tv_usec - start.ru_stime.tv_usec;
00614
00615 destroy(pvt);
00616
00617 t->comp_cost = cost / seconds;
00618
00619 if (!t->comp_cost) {
00620 t->comp_cost = 1;
00621 }
00622 }
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642 static int generate_table_cost(struct ast_format *src, struct ast_format *dst)
00643 {
00644 int src_rate = ast_format_rate(src);
00645 int src_ll = 0;
00646 int dst_rate = ast_format_rate(dst);
00647 int dst_ll = 0;
00648
00649 if ((AST_FORMAT_GET_TYPE(src->id) != AST_FORMAT_TYPE_AUDIO) || (AST_FORMAT_GET_TYPE(dst->id) != AST_FORMAT_TYPE_AUDIO)) {
00650
00651
00652
00653 return 0;
00654 }
00655 src_ll = ast_format_is_slinear(src);
00656 dst_ll = ast_format_is_slinear(dst);
00657 if (src_ll) {
00658 if (dst_ll && (src_rate == dst_rate)) {
00659 return AST_TRANS_COST_LL_LL_ORIGSAMP;
00660 } else if (!dst_ll && (src_rate == dst_rate)) {
00661 return AST_TRANS_COST_LL_LY_ORIGSAMP;
00662 } else if (dst_ll && (src_rate < dst_rate)) {
00663 return AST_TRANS_COST_LL_LL_UPSAMP;
00664 } else if (!dst_ll && (src_rate < dst_rate)) {
00665 return AST_TRANS_COST_LL_LY_UPSAMP;
00666 } else if (dst_ll && (src_rate > dst_rate)) {
00667 return AST_TRANS_COST_LL_LL_DOWNSAMP;
00668 } else if (!dst_ll && (src_rate > dst_rate)) {
00669 return AST_TRANS_COST_LL_LY_DOWNSAMP;
00670 } else {
00671 return AST_TRANS_COST_LL_UNKNOWN;
00672 }
00673 } else {
00674 if (dst_ll && (src_rate == dst_rate)) {
00675 return AST_TRANS_COST_LY_LL_ORIGSAMP;
00676 } else if (!dst_ll && (src_rate == dst_rate)) {
00677 return AST_TRANS_COST_LY_LY_ORIGSAMP;
00678 } else if (dst_ll && (src_rate < dst_rate)) {
00679 return AST_TRANS_COST_LY_LL_UPSAMP;
00680 } else if (!dst_ll && (src_rate < dst_rate)) {
00681 return AST_TRANS_COST_LY_LY_UPSAMP;
00682 } else if (dst_ll && (src_rate > dst_rate)) {
00683 return AST_TRANS_COST_LY_LL_DOWNSAMP;
00684 } else if (!dst_ll && (src_rate > dst_rate)) {
00685 return AST_TRANS_COST_LY_LY_DOWNSAMP;
00686 } else {
00687 return AST_TRANS_COST_LY_UNKNOWN;
00688 }
00689 }
00690 }
00691
00692
00693
00694
00695
00696 static void matrix_rebuild(int samples)
00697 {
00698 struct ast_translator *t;
00699 int newtablecost;
00700 int x;
00701 int y;
00702 int z;
00703
00704 ast_debug(1, "Resetting translation matrix\n");
00705
00706 matrix_clear();
00707
00708
00709 AST_RWLIST_TRAVERSE(&translators, t, list) {
00710 if (!t->active) {
00711 continue;
00712 }
00713
00714 x = t->src_fmt_index;
00715 z = t->dst_fmt_index;
00716
00717 if (samples) {
00718 generate_computational_cost(t, samples);
00719 }
00720
00721
00722
00723
00724
00725
00726
00727 if (!matrix_get(x, z)->step ||
00728 (t->table_cost < matrix_get(x, z)->step->table_cost) ||
00729 (t->comp_cost < matrix_get(x, z)->step->comp_cost)) {
00730
00731 matrix_get(x, z)->step = t;
00732 matrix_get(x, z)->table_cost = t->table_cost;
00733 }
00734 }
00735
00736
00737
00738
00739
00740
00741
00742 for (;;) {
00743 int changed = 0;
00744 for (x = 0; x < cur_max_index; x++) {
00745 for (y = 0; y < cur_max_index; y++) {
00746 if (x == y) {
00747 continue;
00748 }
00749 for (z = 0; z < cur_max_index; z++) {
00750 if ((z == x || z == y) ||
00751 !matrix_get(x, y)->step ||
00752 !matrix_get(y, z)->step) {
00753 continue;
00754 }
00755
00756
00757 newtablecost = matrix_get(x, y)->table_cost + matrix_get(y, z)->table_cost;
00758
00759
00760
00761 if (!matrix_get(x, z)->step || (newtablecost < matrix_get(x, z)->table_cost)) {
00762 struct ast_format tmpx;
00763 struct ast_format tmpy;
00764 struct ast_format tmpz;
00765 matrix_get(x, z)->step = matrix_get(x, y)->step;
00766 matrix_get(x, z)->table_cost = newtablecost;
00767 matrix_get(x, z)->multistep = 1;
00768 changed++;
00769 ast_debug(3, "Discovered %d cost path from %s to %s, via %s\n",
00770 matrix_get(x, z)->table_cost,
00771 ast_getformatname(ast_format_set(&tmpx, index2format(x), 0)),
00772 ast_getformatname(ast_format_set(&tmpy, index2format(z), 0)),
00773 ast_getformatname(ast_format_set(&tmpz, index2format(y), 0)));
00774 }
00775 }
00776 }
00777 }
00778 if (!changed) {
00779 break;
00780 }
00781 }
00782 }
00783
00784 const char *ast_translate_path_to_str(struct ast_trans_pvt *p, struct ast_str **str)
00785 {
00786 struct ast_trans_pvt *pn = p;
00787 char tmp[256];
00788
00789 if (!p || !p->t) {
00790 return "";
00791 }
00792
00793 ast_str_set(str, 0, "%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), p->t->src_format.id));
00794
00795 while ( (p = pn) ) {
00796 pn = p->next;
00797 ast_str_append(str, 0, "->%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), p->t->dst_format.id));
00798 }
00799
00800 return ast_str_buffer(*str);
00801 }
00802
00803 static char *complete_trans_path_choice(const char *line, const char *word, int pos, int state)
00804 {
00805 int which = 0;
00806 int wordlen = strlen(word);
00807 int i;
00808 char *ret = NULL;
00809 size_t len = 0;
00810 const struct ast_format_list *format_list = ast_format_list_get(&len);
00811
00812 for (i = 0; i < len; i++) {
00813 if (AST_FORMAT_GET_TYPE(format_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) {
00814 continue;
00815 }
00816 if (!strncasecmp(word, format_list[i].name, wordlen) && ++which > state) {
00817 ret = ast_strdup(format_list[i].name);
00818 break;
00819 }
00820 }
00821 ast_format_list_destroy(format_list);
00822 return ret;
00823 }
00824
00825 static void handle_cli_recalc(struct ast_cli_args *a)
00826 {
00827 int time = a->argv[4] ? atoi(a->argv[4]) : 1;
00828
00829 if (time <= 0) {
00830 ast_cli(a->fd, " Recalc must be greater than 0. Defaulting to 1.\n");
00831 time = 1;
00832 }
00833
00834 if (time > MAX_RECALC) {
00835 ast_cli(a->fd, " Maximum limit of recalc exceeded by %d, truncating value to %d\n", time - MAX_RECALC, MAX_RECALC);
00836 time = MAX_RECALC;
00837 }
00838 ast_cli(a->fd, " Recalculating Codec Translation (number of sample seconds: %d)\n\n", time);
00839 AST_RWLIST_WRLOCK(&translators);
00840 matrix_rebuild(time);
00841 AST_RWLIST_UNLOCK(&translators);
00842 }
00843
00844 static char *handle_show_translation_table(struct ast_cli_args *a)
00845 {
00846 int x;
00847 int y;
00848 int i;
00849 int k;
00850 int curlen = 0;
00851 int longest = 0;
00852 int f_len;
00853 size_t f_size = 0;
00854 const struct ast_format_list *f_list = ast_format_list_get(&f_size);
00855 struct ast_str *out = ast_str_create(1024);
00856
00857 f_len = f_size;
00858 AST_RWLIST_RDLOCK(&translators);
00859 ast_cli(a->fd, " Translation times between formats (in microseconds) for one second of data\n");
00860 ast_cli(a->fd, " Source Format (Rows) Destination Format (Columns)\n\n");
00861
00862
00863 for (i = 0; i < f_len; i++) {
00864
00865 if (AST_FORMAT_GET_TYPE(f_list[i].format.id) != AST_FORMAT_TYPE_AUDIO)
00866 continue;
00867 curlen = strlen(ast_getformatname(&f_list[i].format));
00868 if (curlen > longest) {
00869 longest = curlen;
00870 }
00871 }
00872
00873 for (i = -1; i < f_len; i++) {
00874 x = -1;
00875 if ((i >= 0) && ((x = format2index(f_list[i].format.id)) == -1)) {
00876 continue;
00877 }
00878
00879 if (i >= 0 && (AST_FORMAT_GET_TYPE(f_list[i].format.id) != AST_FORMAT_TYPE_AUDIO)) {
00880 continue;
00881 }
00882
00883 if (i >= 0 && !strcmp(ast_getformatname(&f_list[i].format), "unknown")) {
00884 continue;
00885 }
00886 ast_str_set(&out, 0, " ");
00887 for (k = -1; k < f_len; k++) {
00888 y = -1;
00889 if ((k >= 0) && ((y = format2index(f_list[k].format.id)) == -1)) {
00890 continue;
00891 }
00892
00893 if (k >= 0 && (AST_FORMAT_GET_TYPE(f_list[k].format.id) != AST_FORMAT_TYPE_AUDIO)) {
00894 continue;
00895 }
00896
00897 if (k >= 0 && !strcmp(ast_getformatname(&f_list[k].format), "unknown")) {
00898 continue;
00899 }
00900 if (k >= 0) {
00901 curlen = strlen(ast_getformatname(&f_list[k].format));
00902 }
00903 if (curlen < 5) {
00904 curlen = 5;
00905 }
00906
00907 if (x >= 0 && y >= 0 && matrix_get(x, y)->step) {
00908
00909 ast_str_append(&out, 0, "%*d", curlen + 1, (matrix_get(x, y)->table_cost/100));
00910 } else if (i == -1 && k >= 0) {
00911
00912 ast_str_append(&out, 0, "%*s", curlen + 1, ast_getformatname(&f_list[k].format));
00913 } else if (k == -1 && i >= 0) {
00914
00915 ast_str_append(&out, 0, "%*s", longest, ast_getformatname(&f_list[i].format));
00916 } else if (x >= 0 && y >= 0) {
00917
00918 ast_str_append(&out, 0, "%*s", curlen + 1, "-");
00919 } else {
00920
00921 ast_str_append(&out, 0, "%*s", longest, "");
00922 }
00923 }
00924 ast_str_append(&out, 0, "\n");
00925 ast_cli(a->fd, "%s", ast_str_buffer(out));
00926 }
00927 ast_free(out);
00928 AST_RWLIST_UNLOCK(&translators);
00929 ast_format_list_destroy(f_list);
00930 return CLI_SUCCESS;
00931 }
00932
00933 static char *handle_show_translation_path(struct ast_cli_args *a)
00934 {
00935 struct ast_format input_src_format;
00936 size_t len = 0;
00937 int i;
00938 const struct ast_format_list *format_list = ast_format_list_get(&len);
00939 struct ast_str *str = ast_str_alloca(1024);
00940 struct ast_translator *step;
00941 char tmp[256];
00942
00943 ast_format_clear(&input_src_format);
00944 for (i = 0; i < len; i++) {
00945 if (AST_FORMAT_GET_TYPE(format_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) {
00946 continue;
00947 }
00948 if (!strncasecmp(format_list[i].name, a->argv[4], strlen(format_list[i].name))) {
00949 ast_format_copy(&input_src_format, &format_list[i].format);
00950 }
00951 }
00952
00953 if (!input_src_format.id) {
00954 ast_cli(a->fd, "Source codec \"%s\" is not found.\n", a->argv[4]);
00955 ast_format_list_destroy(format_list);
00956 return CLI_FAILURE;
00957 }
00958
00959 AST_RWLIST_RDLOCK(&translators);
00960 ast_cli(a->fd, "--- Translation paths SRC Codec \"%s\" sample rate %d ---\n", a->argv[4], ast_format_rate(&input_src_format));
00961 for (i = 0; i < len; i++) {
00962 int src;
00963 int dst;
00964 if ((AST_FORMAT_GET_TYPE(format_list[i].format.id) != AST_FORMAT_TYPE_AUDIO) || (format_list[i].format.id == input_src_format.id)) {
00965 continue;
00966 }
00967 dst = format2index(format_list[i].format.id);
00968 src = format2index(input_src_format.id);
00969 ast_str_reset(str);
00970 if ((len >= cur_max_index) && (src != -1) && (dst != -1) && matrix_get(src, dst)->step) {
00971 ast_str_append(&str, 0, "%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), matrix_get(src, dst)->step->src_format.id));
00972 while (src != dst) {
00973 step = matrix_get(src, dst)->step;
00974 if (!step) {
00975 ast_str_reset(str);
00976 break;
00977 }
00978 ast_str_append(&str, 0, "->%s", ast_getformatname_multiple_byid(tmp, sizeof(tmp), step->dst_format.id));
00979 src = step->dst_fmt_index;
00980 }
00981 }
00982
00983 if (ast_strlen_zero(ast_str_buffer(str))) {
00984 ast_str_set(&str, 0, "No Translation Path");
00985 }
00986 ast_cli(a->fd, "\t%-10.10s To %-10.10s: %-60.60s\n", a->argv[4], format_list[i].name, ast_str_buffer(str));
00987 }
00988 AST_RWLIST_UNLOCK(&translators);
00989
00990 ast_format_list_destroy(format_list);
00991 return CLI_SUCCESS;
00992 }
00993
00994 static char *handle_cli_core_show_translation(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
00995 {
00996 static const char * const option[] = { "recalc", "paths", NULL };
00997
00998 switch (cmd) {
00999 case CLI_INIT:
01000 e->command = "core show translation";
01001 e->usage =
01002 "Usage: 'core show translation' can be used in two ways.\n"
01003 " 1. 'core show translation [recalc [<recalc seconds>]]\n"
01004 " Displays known codec translators and the cost associated\n"
01005 " with each conversion. If the argument 'recalc' is supplied along\n"
01006 " with optional number of seconds to test a new test will be performed\n"
01007 " as the chart is being displayed.\n"
01008 " 2. 'core show translation paths [codec]'\n"
01009 " This will display all the translation paths associated with a codec\n";
01010 return NULL;
01011 case CLI_GENERATE:
01012 if (a->pos == 3) {
01013 return ast_cli_complete(a->word, option, a->n);
01014 }
01015 if (a->pos == 4 && !strcasecmp(a->argv[3], option[1])) {
01016 return complete_trans_path_choice(a->line, a->word, a->pos, a->n);
01017 }
01018 return NULL;
01019 }
01020
01021 if (a->argc > 5)
01022 return CLI_SHOWUSAGE;
01023
01024 if (a->argv[3] && !strcasecmp(a->argv[3], option[1]) && a->argc == 5) {
01025 return handle_show_translation_path(a);
01026 } else if (a->argv[3] && !strcasecmp(a->argv[3], option[0])) {
01027 handle_cli_recalc(a);
01028 } else if (a->argc > 3) {
01029 return CLI_SHOWUSAGE;
01030 }
01031
01032 return handle_show_translation_table(a);
01033 }
01034
01035 static struct ast_cli_entry cli_translate[] = {
01036 AST_CLI_DEFINE(handle_cli_core_show_translation, "Display translation matrix")
01037 };
01038
01039
01040 int __ast_register_translator(struct ast_translator *t, struct ast_module *mod)
01041 {
01042 struct ast_translator *u;
01043 char tmp[80];
01044
01045 if (add_format2index(t->src_format.id) || add_format2index(t->dst_format.id)) {
01046 if (matrix_resize(0)) {
01047 ast_log(LOG_WARNING, "Translator matrix can not represent any more translators. Out of resources.\n");
01048 return -1;
01049 }
01050 add_format2index(t->src_format.id);
01051 add_format2index(t->dst_format.id);
01052 }
01053
01054 if (!mod) {
01055 ast_log(LOG_WARNING, "Missing module pointer, you need to supply one\n");
01056 return -1;
01057 }
01058
01059 if (!t->buf_size) {
01060 ast_log(LOG_WARNING, "empty buf size, you need to supply one\n");
01061 return -1;
01062 }
01063 if (!t->table_cost && !(t->table_cost = generate_table_cost(&t->src_format, &t->dst_format))) {
01064 ast_log(LOG_WARNING, "Table cost could not be generated for %s, "
01065 "Please set table_cost variable on translator.\n", t->name);
01066 return -1;
01067 }
01068
01069 t->module = mod;
01070 t->src_fmt_index = format2index(t->src_format.id);
01071 t->dst_fmt_index = format2index(t->dst_format.id);
01072 t->active = 1;
01073
01074 if (t->src_fmt_index == -1 || t->dst_fmt_index == -1) {
01075 ast_log(LOG_WARNING, "Invalid translator path: (%s codec is not valid)\n", t->src_fmt_index == -1 ? "starting" : "ending");
01076 return -1;
01077 }
01078 if (t->src_fmt_index >= cur_max_index) {
01079 ast_log(LOG_WARNING, "Source format %s is larger than cur_max_index\n", ast_getformatname(&t->src_format));
01080 return -1;
01081 }
01082
01083 if (t->dst_fmt_index >= cur_max_index) {
01084 ast_log(LOG_WARNING, "Destination format %s is larger than cur_max_index\n", ast_getformatname(&t->dst_format));
01085 return -1;
01086 }
01087
01088 if (t->buf_size) {
01089
01090
01091
01092
01093 struct _test_align { void *a, *b; } p;
01094 int align = (char *)&p.b - (char *)&p.a;
01095
01096 t->buf_size = ((t->buf_size + align - 1) / align) * align;
01097 }
01098
01099 if (t->frameout == NULL) {
01100 t->frameout = default_frameout;
01101 }
01102
01103 generate_computational_cost(t, 1);
01104
01105 ast_verb(2, "Registered translator '%s' from format %s to %s, table cost, %d, computational cost %d\n",
01106 term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)),
01107 ast_getformatname(&t->src_format), ast_getformatname(&t->dst_format), t->table_cost, t->comp_cost);
01108
01109 AST_RWLIST_WRLOCK(&translators);
01110
01111
01112
01113 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) {
01114 if ((u->src_fmt_index == t->src_fmt_index) &&
01115 (u->dst_fmt_index == t->dst_fmt_index) &&
01116 (u->comp_cost > t->comp_cost)) {
01117 AST_RWLIST_INSERT_BEFORE_CURRENT(t, list);
01118 t = NULL;
01119 break;
01120 }
01121 }
01122 AST_RWLIST_TRAVERSE_SAFE_END;
01123
01124
01125
01126 if (t) {
01127 AST_RWLIST_INSERT_HEAD(&translators, t, list);
01128 }
01129
01130 matrix_rebuild(0);
01131
01132 AST_RWLIST_UNLOCK(&translators);
01133
01134 return 0;
01135 }
01136
01137
01138 int ast_unregister_translator(struct ast_translator *t)
01139 {
01140 char tmp[80];
01141 struct ast_translator *u;
01142 int found = 0;
01143
01144 AST_RWLIST_WRLOCK(&translators);
01145 AST_RWLIST_TRAVERSE_SAFE_BEGIN(&translators, u, list) {
01146 if (u == t) {
01147 AST_RWLIST_REMOVE_CURRENT(list);
01148 ast_verb(2, "Unregistered translator '%s' from format %s to %s\n",
01149 term_color(tmp, t->name, COLOR_MAGENTA, COLOR_BLACK, sizeof(tmp)),
01150 ast_getformatname(&t->src_format),
01151 ast_getformatname(&t->dst_format));
01152 found = 1;
01153 break;
01154 }
01155 }
01156 AST_RWLIST_TRAVERSE_SAFE_END;
01157
01158 if (found) {
01159 matrix_rebuild(0);
01160 }
01161
01162 AST_RWLIST_UNLOCK(&translators);
01163
01164 return (u ? 0 : -1);
01165 }
01166
01167 void ast_translator_activate(struct ast_translator *t)
01168 {
01169 AST_RWLIST_WRLOCK(&translators);
01170 t->active = 1;
01171 matrix_rebuild(0);
01172 AST_RWLIST_UNLOCK(&translators);
01173 }
01174
01175 void ast_translator_deactivate(struct ast_translator *t)
01176 {
01177 AST_RWLIST_WRLOCK(&translators);
01178 t->active = 0;
01179 matrix_rebuild(0);
01180 AST_RWLIST_UNLOCK(&translators);
01181 }
01182
01183
01184 int ast_translator_best_choice(struct ast_format_cap *dst_cap,
01185 struct ast_format_cap *src_cap,
01186 struct ast_format *dst_fmt_out,
01187 struct ast_format *src_fmt_out)
01188 {
01189 unsigned int besttablecost = INT_MAX;
01190 unsigned int beststeps = INT_MAX;
01191 struct ast_format best;
01192 struct ast_format bestdst;
01193 struct ast_format_cap *joint_cap = ast_format_cap_joint(dst_cap, src_cap);
01194 ast_format_clear(&best);
01195 ast_format_clear(&bestdst);
01196
01197 if (joint_cap) {
01198 struct ast_format tmp_fmt;
01199 ast_format_cap_iter_start(joint_cap);
01200 while (!ast_format_cap_iter_next(joint_cap, &tmp_fmt)) {
01201
01202 if (!best.id) {
01203 ast_format_copy(&best, &tmp_fmt);
01204 continue;
01205 }
01206
01207 if (ast_format_rate(&best) < ast_format_rate(&tmp_fmt)) {
01208 ast_format_copy(&best, &tmp_fmt);
01209 continue;
01210 }
01211
01212 }
01213 ast_format_cap_iter_end(joint_cap);
01214
01215
01216 ast_format_copy(dst_fmt_out, &best);
01217 ast_format_copy(src_fmt_out, &best);
01218 ast_format_cap_destroy(joint_cap);
01219 return 0;
01220 } else {
01221 struct ast_format cur_dst;
01222 struct ast_format cur_src;
01223 AST_RWLIST_RDLOCK(&translators);
01224
01225 ast_format_cap_iter_start(dst_cap);
01226 while (!ast_format_cap_iter_next(dst_cap, &cur_dst)) {
01227 ast_format_cap_iter_start(src_cap);
01228 while (!ast_format_cap_iter_next(src_cap, &cur_src)) {
01229 int x = format2index(cur_src.id);
01230 int y = format2index(cur_dst.id);
01231 if (x < 0 || y < 0) {
01232 continue;
01233 }
01234 if (!matrix_get(x, y) || !(matrix_get(x, y)->step)) {
01235 continue;
01236 }
01237 if (((matrix_get(x, y)->table_cost < besttablecost) || (matrix_get(x, y)->multistep < beststeps))) {
01238
01239 ast_format_copy(&best, &cur_src);
01240 ast_format_copy(&bestdst, &cur_dst);
01241 besttablecost = matrix_get(x, y)->table_cost;
01242 beststeps = matrix_get(x, y)->multistep;
01243 }
01244 }
01245 ast_format_cap_iter_end(src_cap);
01246 }
01247
01248 ast_format_cap_iter_end(dst_cap);
01249 AST_RWLIST_UNLOCK(&translators);
01250 if (best.id) {
01251 ast_format_copy(dst_fmt_out, &bestdst);
01252 ast_format_copy(src_fmt_out, &best);
01253 return 0;
01254 }
01255 return -1;
01256 }
01257 }
01258
01259 unsigned int ast_translate_path_steps(struct ast_format *dst_format, struct ast_format *src_format)
01260 {
01261 unsigned int res = -1;
01262 int src, dest;
01263
01264 src = format2index(src_format->id);
01265 dest = format2index(dst_format->id);
01266
01267 if (src == -1 || dest == -1) {
01268 ast_log(LOG_WARNING, "No translator path: (%s codec is not valid)\n", src == -1 ? "starting" : "ending");
01269 return -1;
01270 }
01271 AST_RWLIST_RDLOCK(&translators);
01272
01273 if (matrix_get(src, dest)->step) {
01274 res = matrix_get(src, dest)->multistep + 1;
01275 }
01276
01277 AST_RWLIST_UNLOCK(&translators);
01278
01279 return res;
01280 }
01281
01282 void ast_translate_available_formats(struct ast_format_cap *dest, struct ast_format_cap *src, struct ast_format_cap *result)
01283 {
01284 struct ast_format tmp_fmt;
01285 struct ast_format cur_src;
01286 int src_audio = 0;
01287 int src_video = 0;
01288 int index;
01289
01290 ast_format_cap_copy(result, dest);
01291
01292
01293
01294 if (!src) {
01295 return;
01296 }
01297
01298 ast_format_cap_iter_start(src);
01299 while (!ast_format_cap_iter_next(src, &cur_src)) {
01300
01301 if (AST_FORMAT_GET_TYPE(cur_src.id) == AST_FORMAT_TYPE_AUDIO) {
01302 src_audio = format2index(cur_src.id);
01303 }
01304
01305
01306 if (AST_FORMAT_GET_TYPE(cur_src.id) == AST_FORMAT_TYPE_VIDEO) {
01307 src_video = format2index(cur_src.id);
01308 }
01309
01310 AST_RWLIST_RDLOCK(&translators);
01311
01312
01313
01314
01315
01316 for (index = 0; (src_audio >= 0) && index < cur_max_index; index++) {
01317 ast_format_set(&tmp_fmt, index2format(index), 0);
01318
01319 if (AST_FORMAT_GET_TYPE(tmp_fmt.id) != AST_FORMAT_TYPE_AUDIO) {
01320 continue;
01321 }
01322
01323
01324 if (!ast_format_cap_iscompatible(dest, &tmp_fmt)) {
01325 continue;
01326 }
01327
01328
01329
01330 if (ast_format_cap_iscompatible(src, &tmp_fmt)) {
01331 continue;
01332 }
01333
01334
01335
01336 if (!matrix_get(src_audio, index)->step) {
01337 ast_format_cap_remove_byid(result, tmp_fmt.id);
01338 continue;
01339 }
01340
01341
01342 if (!matrix_get(index, src_audio)->step) {
01343 ast_format_cap_remove_byid(result, tmp_fmt.id);
01344 }
01345 }
01346
01347
01348
01349
01350
01351 for (index = 0; (src_video >= 0) && index < cur_max_index; index++) {
01352 ast_format_set(&tmp_fmt, index2format(index), 0);
01353 if (AST_FORMAT_GET_TYPE(tmp_fmt.id) != AST_FORMAT_TYPE_VIDEO) {
01354 continue;
01355 }
01356
01357
01358 if (!ast_format_cap_iscompatible(dest, &tmp_fmt)) {
01359 continue;
01360 }
01361
01362
01363
01364 if (ast_format_cap_iscompatible(src, &tmp_fmt)) {
01365 continue;
01366 }
01367
01368
01369
01370 if (!matrix_get(src_video, index)->step) {
01371 ast_format_cap_remove_byid(result, tmp_fmt.id);
01372 continue;
01373 }
01374
01375
01376 if (!matrix_get(index, src_video)->step) {
01377 ast_format_cap_remove_byid(result, tmp_fmt.id);
01378 }
01379 }
01380 AST_RWLIST_UNLOCK(&translators);
01381 }
01382 ast_format_cap_iter_end(src);
01383 }
01384
01385 int ast_translate_init(void)
01386 {
01387 int res = 0;
01388 ast_rwlock_init(&tablelock);
01389 res = matrix_resize(1);
01390 res |= ast_cli_register_multiple(cli_translate, ARRAY_LEN(cli_translate));
01391 return res;
01392 }