/* * Asterisk -- A telephony toolkit for Linux. * * Frame manipulation routines * * Copyright (C) 1999, Mark Spencer * * Mark Spencer * * This program is free software, distributed under the terms of * the GNU General Public License */ #include #include #include #include #include #include #include #include #include #include "asterisk.h" #ifdef TRACE_FRAMES static int headers = 0; static struct ast_frame *headerlist = NULL; static pthread_mutex_t framelock = PTHREAD_MUTEX_INITIALIZER; #endif static struct ast_frame *ast_frame_header_new(void) { struct ast_frame *f; f = malloc(sizeof(struct ast_frame)); #ifdef TRACE_FRAMES if (f) { headers++; f->prev = NULL; pthread_mutex_lock(&framelock); f->next = headerlist; if (headerlist) headerlist->prev = f; headerlist = f; pthread_mutex_unlock(&framelock); } #endif return f; } /* * Important: I should be made more efficient. Frame headers should * most definitely be cached */ void ast_frfree(struct ast_frame *fr) { if (fr->mallocd & AST_MALLOCD_DATA) { if (fr->data) free(fr->data - fr->offset); } if (fr->mallocd & AST_MALLOCD_SRC) { if (fr->src) free(fr->src); } if (fr->mallocd & AST_MALLOCD_HDR) { #ifdef TRACE_FRAMES headers--; pthread_mutex_lock(&framelock); if (fr->next) fr->next->prev = fr->prev; if (fr->prev) fr->prev->next = fr->next; else headerlist = fr->next; pthread_mutex_unlock(&framelock); #endif free(fr); } } struct ast_frame *ast_frisolate(struct ast_frame *fr) { struct ast_frame *out; if (!(fr->mallocd & AST_MALLOCD_HDR)) { /* Allocate a new header if needed */ out = ast_frame_header_new(); if (!out) { ast_log(LOG_WARNING, "Out of memory\n"); return NULL; } out->frametype = fr->frametype; out->subclass = fr->subclass; out->datalen = 0; out->timelen = fr->timelen; out->offset = 0; out->src = NULL; out->data = NULL; } else { out = fr; } if (!(fr->mallocd & AST_MALLOCD_SRC)) { if (fr->src) out->src = strdup(fr->src); } else out->src = fr->src; if (!(fr->mallocd & AST_MALLOCD_DATA)) { out->data = malloc(fr->datalen + AST_FRIENDLY_OFFSET); if (!out->data) { free(out); ast_log(LOG_WARNING, "Out of memory\n"); return NULL; } out->data += AST_FRIENDLY_OFFSET; out->offset = AST_FRIENDLY_OFFSET; out->datalen = fr->datalen; memcpy(out->data, fr->data, fr->datalen); } out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA; return out; } struct ast_frame *ast_frdup(struct ast_frame *f) { struct ast_frame *ret; int p; p = f->mallocd; f->mallocd = 0; /* Make frisolate think this is a 100% static frame, and make a duplicate */ ret = ast_frisolate(f); /* Restore its true malloc status */ f->mallocd = p; return ret; } struct ast_frame *ast_fr_fdread(int fd) { char buf[65536]; int res; int ttl = sizeof(struct ast_frame); struct ast_frame *f = (struct ast_frame *)buf; /* Read a frame directly from there. They're always in the right format. */ while(ttl) { res = read(fd, buf, ttl); if (res < 0) { ast_log(LOG_WARNING, "Bad read on %d: %s\n", fd, strerror(errno)); return NULL; } ttl -= res; } /* read the frame header */ f->mallocd = 0; /* Re-write data position */ f->data = buf + sizeof(struct ast_frame); f->offset = 0; /* Forget about being mallocd */ f->mallocd = 0; /* Re-write the source */ f->src = __FUNCTION__; if (f->datalen > sizeof(buf) - sizeof(struct ast_frame)) { /* Really bad read */ ast_log(LOG_WARNING, "Strange read (%d bytes)\n", f->datalen); return NULL; } if (f->datalen) { if ((res = read(fd, f->data, f->datalen)) != f->datalen) { /* Bad read */ ast_log(LOG_WARNING, "How very strange, expected %d, got %d\n", f->datalen, res); return NULL; } } if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) { return NULL; } return ast_frisolate(f); } /* Some convenient routines for sending frames to/from stream or datagram sockets, pipes, etc (maybe even files) */ int ast_fr_fdwrite(int fd, struct ast_frame *frame) { /* Write the frame exactly */ if (write(fd, frame, sizeof(struct ast_frame)) != sizeof(struct ast_frame)) { ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno)); return -1; } if (write(fd, frame->data, frame->datalen) != frame->datalen) { ast_log(LOG_WARNING, "Write error: %s\n", strerror(errno)); return -1; } return 0; } int ast_fr_fdhangup(int fd) { struct ast_frame hangup = { AST_FRAME_CONTROL, AST_CONTROL_HANGUP }; return ast_fr_fdwrite(fd, &hangup); } int ast_getformatbyname(char *name) { if (!strcasecmp(name, "g723.1")) return AST_FORMAT_G723_1; else if (!strcasecmp(name, "gsm")) return AST_FORMAT_GSM; else if (!strcasecmp(name, "ulaw")) return AST_FORMAT_ULAW; else if (!strcasecmp(name, "alaw")) return AST_FORMAT_ALAW; else if (!strcasecmp(name, "mp3")) return AST_FORMAT_MP3; else if (!strcasecmp(name, "slinear")) return AST_FORMAT_SLINEAR; else if (!strcasecmp(name, "lpc10")) return AST_FORMAT_LPC10; else if (!strcasecmp(name, "adpcm")) return AST_FORMAT_ADPCM; else if (!strcasecmp(name, "all")) return 0x7FFFFFFF; return 0; } #ifdef TRACE_FRAMES static int show_frame_stats(int fd, int argc, char *argv[]) { struct ast_frame *f; int x=1; if (argc != 3) return RESULT_SHOWUSAGE; ast_cli(fd, " Framer Statistics \n"); ast_cli(fd, "---------------------------\n"); ast_cli(fd, "Total allocated headers: %d\n", headers); ast_cli(fd, "Queue Dump:\n"); pthread_mutex_lock(&framelock); for (f=headerlist; f; f = f->next) { ast_cli(fd, "%d. Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : ""); } pthread_mutex_unlock(&framelock); return RESULT_SUCCESS; } static char frame_stats_usage[] = "Usage: show frame stats\n" " Displays debugging statistics from framer\n"; struct ast_cli_entry cli_frame_stats = { { "show", "frame", "stats", NULL }, show_frame_stats, "Shows frame statistics", frame_stats_usage }; #endif int init_framer(void) { #ifdef TRACE_FRAMES ast_cli_register(&cli_frame_stats); #endif return 0; }