Newer
Older
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Translate via the use of pseudo channels
*
*
* Mark Spencer <markster@linux-support.net>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#include <asterisk/channel.h>
#include <asterisk/channel_pvt.h>
#include <asterisk/logger.h>
#include <asterisk/translate.h>
#include <asterisk/options.h>
#include <asterisk/frame.h>
#include <asterisk/sched.h>
#include <asterisk/cli.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <string.h>
#include <stdio.h>
/* This could all be done more efficiently *IF* we chained packets together
by default, but it would also complicate virtually every application. */
static ast_mutex_t list_lock = AST_MUTEX_INITIALIZER;
static struct ast_translator *list = NULL;
struct ast_translator_dir {
struct ast_translator *step; /* Next step translator */
int cost; /* Complete cost to destination */
};
struct ast_frame_delivery {
struct ast_frame *f;
struct ast_channel *chan;
int fd;
struct translator_pvt *owner;
struct ast_frame_delivery *prev;
struct ast_frame_delivery *next;
};
static struct ast_translator_dir tr_matrix[MAX_FORMAT][MAX_FORMAT];
struct ast_trans_pvt {
struct ast_translator *step;
struct ast_translator_pvt *state;
struct ast_trans_pvt *next;
struct timeval nextin;
struct timeval nextout;
};
static int powerof(int d)
{
int x;
for (x = 0; x < 32; x++)
if ((1 << x) & d)
return x;
ast_log(LOG_WARNING, "Powerof %d: No power??\n", d);
return -1;
}
void ast_translator_free_path(struct ast_trans_pvt *p)
{
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);
}
}
struct ast_trans_pvt *ast_translator_build_path(int dest, int source)
{
struct ast_trans_pvt *tmpr = NULL, *tmp = NULL;
/* One of the hardest parts: Build a set of translators based upon
the given source and destination formats */
source = powerof(source);
dest = powerof(dest);
while(source != dest) {
if (tr_matrix[source][dest].step) {
if (tmp) {
tmp->next = malloc(sizeof(struct ast_trans_pvt));
tmp = tmp->next;
} else
tmp = malloc(sizeof(struct ast_trans_pvt));
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);
}
/* Set the root, if it doesn't exist yet... */
if (!tmpr)
tmpr = tmp;
/* Keep going if this isn't the final destination */
source = tmp->step->dstfmt;
} else {
/* XXX This could leak XXX */
ast_log(LOG_WARNING, "Out of memory\n");
return NULL;
}
} else {
/* We shouldn't have allocated any memory */
ast_log(LOG_WARNING, "No translator path from %s to %s\n",
ast_getformatname(source), ast_getformatname(dest));
struct ast_frame *ast_translate(struct ast_trans_pvt *path, struct ast_frame *f, int consume)
{
struct ast_trans_pvt *p;
struct ast_frame *out;
p = path;
/* Feed the first frame into the first translator */
p->step->framein(p->state, f);
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
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++;
}
out = p->step->frameout(p->state);
/* If we get nothing out, return NULL */
if (!out)
return NULL;
/* If there is a next state, feed it in there. If not,
return this frame */
if (p->next)
p->next->step->framein(p->next->state, out);
else {
/* 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;
}
}
ast_log(LOG_WARNING, "I should never get here...\n");
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
{
struct ast_translator *t;
int changed;
int x,y,z;
if (option_debug)
ast_log(LOG_DEBUG, "Reseting translation matrix\n");
/* Use the list of translators to build a translation matrix */
bzero(tr_matrix, sizeof(tr_matrix));
t = list;
while(t) {
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;
tr_matrix[t->srcfmt][t->dstfmt].cost = t->cost;
}
t = t->next;
}
do {
changed = 0;
/* Don't you just love O(N^3) operations? */
for (x=0; x< MAX_FORMAT; x++) /* For each source format */
for (y=0; y < MAX_FORMAT; y++) /* And each destination format */
if (x != y) /* Except ourselves, of course */
for (z=0; z < MAX_FORMAT; z++) /* And each format it might convert to */
if ((x!=z) && (y!=z)) /* Don't ever convert back to us */
if (tr_matrix[x][y].step && /* We can convert from x to y */
tr_matrix[y][z].step && /* And from y to z and... */
(!tr_matrix[x][z].step || /* Either there isn't an x->z conversion */
(tr_matrix[x][y].cost +
tr_matrix[y][z].cost < /* Or we're cheaper than the existing */
tr_matrix[x][z].cost) /* solution */
)) {
/* We can get from x to z via y with a cost that
is the sum of the transition from x to y and
from y to z */
tr_matrix[x][z].step = tr_matrix[x][y].step;
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 %s to %s, via %d\n", tr_matrix[x][z].cost, ast_getformatname(x), ast_getformatname(z), y);
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
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 */
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))) {
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 11
int x,y;
char line[80];
if (argc != 2)
return RESULT_SHOWUSAGE;
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);
if (((x == -1 && y >= 0) || (y == -1 && x >= 0))) {
snprintf(line + strlen(line), sizeof(line) - strlen(line),
" %5s", ast_getformatname(1<<(x+y+1)) );
snprintf(line + strlen(line), sizeof(line) - strlen(line), " -");
snprintf(line + strlen(line), sizeof(line) - strlen(line), " ");
}
snprintf(line + strlen(line), sizeof(line) - strlen(line), "\n");
ast_cli(fd, line);
}
ast_mutex_unlock(&list_lock);
return RESULT_SUCCESS;
}
static int added_cli = 0;
static char show_trans_usage[] =
"Usage: show translation\n"
" Displays known codec translators and the cost associated\n"
"with each conversion.\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)
{
t->srcfmt = powerof(t->srcfmt);
t->dstfmt = powerof(t->dstfmt);
if ((t->srcfmt >= MAX_FORMAT) || (t->dstfmt >= MAX_FORMAT)) {
ast_log(LOG_WARNING, "Format %s is larger than MAX_FORMAT\n", ast_getformatname(t->srcfmt));
return -1;
}
calc_cost(t);
if (option_verbose > 1)
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_mutex_unlock(&list_lock);
return 0;
}
int ast_unregister_translator(struct ast_translator *t)
{
struct ast_translator *u, *ul = NULL;
ast_mutex_lock(&list_lock);
u = list;
while(u) {
if (u == t) {
if (ul)
ul->next = u->next;
else
list = u->next;
break;
}
ast_mutex_unlock(&list_lock);
int ast_translator_best_choice(int *dst, int *srcs)
{
/* Calculate our best source format, given costs, and a desired destination */
ast_mutex_lock(&list_lock);
if ((cur & *dst) && (cur & *srcs)) {
/* This is a common format to both. Pick it if we don't have one already */
besttime=0;
bestdst = cur;
best = cur;
break;
}
if (cur & *dst)
for (x=0;x<MAX_FORMAT;x++) {
if (tr_matrix[x][y].step && /* There's a step */
(tr_matrix[x][y].cost < besttime) && /* We're better than what exists now */
(*srcs & (1 << x))) /* x is a valid source format */
bestdst = cur;
besttime = tr_matrix[x][y].cost;
if (best > -1) {
*srcs = best;
*dst = bestdst;
best = 0;
}
ast_mutex_unlock(&list_lock);