Main Page | Alphabetical List | Data Structures | 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 Fri Sep 24 21:03:48 2004 for Asterisk by doxygen 1.3.8