diff --git a/formats/format_mp3.c b/formats/format_mp3.c new file mode 100755 index 0000000000000000000000000000000000000000..811acbe149a98d9af6c6778f395114803673ef20 --- /dev/null +++ b/formats/format_mp3.c @@ -0,0 +1,268 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Everybody's favorite format: MP3 Files! Yay! + * + * Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC + * + * 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/file.h> +#include <asterisk/logger.h> +#include <asterisk/sched.h> +#include <asterisk/module.h> +#include <arpa/inet.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> +#include <pthread.h> +#include <sys/time.h> +#include "../channels/adtranvofr.h" + + +#define MP3_MAX_SIZE 1400 + +struct ast_filestream { + /* First entry MUST be reserved for the channel type */ + void *reserved[AST_RESERVED_POINTERS]; + /* This is what a filestream means to us */ + int fd; /* Descriptor */ + struct ast_channel *owner; + struct ast_filestream *next; + struct ast_frame *fr; /* Frame representation of buf */ + char buf[sizeof(struct ast_frame) + MP3_MAX_SIZE + AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */ + int pos; +}; + + +static struct ast_filestream *glist = NULL; +static pthread_mutex_t mp3_lock = PTHREAD_MUTEX_INITIALIZER; +static int glistcnt = 0; + +static char *name = "mp3"; +static char *desc = "MPEG-2 Layer 3 File Format Support"; +static char *exts = "mp3|mpeg3"; + +#if 0 +#define MP3_FRAMELEN 417 +#else +#define MP3_FRAMELEN 400 +#endif +#define MP3_OUTPUTLEN 2304 /* Bytes */ +#define MP3_TIMELEN ((MP3_OUTPUTLEN * 1000 / 16000) ) + +static struct ast_filestream *mp3_open(int fd) +{ + /* We don't have any header to read or anything really, but + if we did, it would go here. We also might want to check + and be sure it's a valid file. */ + struct ast_filestream *tmp; + if ((tmp = malloc(sizeof(struct ast_filestream)))) { + if (pthread_mutex_lock(&mp3_lock)) { + ast_log(LOG_WARNING, "Unable to lock mp3 list\n"); + free(tmp); + return NULL; + } + tmp->next = glist; + glist = tmp; + tmp->fd = fd; + tmp->owner = NULL; + tmp->fr = (struct ast_frame *)tmp->buf; + tmp->fr->data = tmp->buf + sizeof(struct ast_frame); + tmp->fr->frametype = AST_FRAME_VOICE; + tmp->fr->subclass = AST_FORMAT_MP3; + /* datalen will vary for each frame */ + tmp->fr->src = name; + tmp->fr->mallocd = 0; + glistcnt++; + pthread_mutex_unlock(&mp3_lock); + ast_update_use_count(); + } + return tmp; +} + +static struct ast_filestream *mp3_rewrite(int fd, char *comment) +{ + /* We don't have any header to read or anything really, but + if we did, it would go here. We also might want to check + and be sure it's a valid file. */ + struct ast_filestream *tmp; + if ((tmp = malloc(sizeof(struct ast_filestream)))) { + if (pthread_mutex_lock(&mp3_lock)) { + ast_log(LOG_WARNING, "Unable to lock mp3 list\n"); + free(tmp); + return NULL; + } + tmp->next = glist; + glist = tmp; + tmp->fd = fd; + tmp->owner = NULL; + tmp->fr = NULL; + glistcnt++; + pthread_mutex_unlock(&mp3_lock); + ast_update_use_count(); + } else + ast_log(LOG_WARNING, "Out of memory\n"); + return tmp; +} + +static struct ast_frame *mp3_read(struct ast_filestream *s) +{ + return NULL; +} + +static void mp3_close(struct ast_filestream *s) +{ + struct ast_filestream *tmp, *tmpl = NULL; + if (pthread_mutex_lock(&mp3_lock)) { + ast_log(LOG_WARNING, "Unable to lock mp3 list\n"); + return; + } + tmp = glist; + while(tmp) { + if (tmp == s) { + if (tmpl) + tmpl->next = tmp->next; + else + glist = tmp->next; + break; + } + tmpl = tmp; + tmp = tmp->next; + } + glistcnt--; + if (s->owner) { + s->owner->stream = NULL; + if (s->owner->streamid > -1) + ast_sched_del(s->owner->sched, s->owner->streamid); + s->owner->streamid = -1; + } + pthread_mutex_unlock(&mp3_lock); + ast_update_use_count(); + if (!tmp) + ast_log(LOG_WARNING, "Freeing a filestream we don't seem to own\n"); + close(s->fd); + free(s); +} + +static int ast_read_callback(void *data) +{ + /* XXX Don't assume frames are this size XXX */ + u_int16_t size=MP3_FRAMELEN; + u_int32_t delay = -1; + int res; + struct ast_filestream *s = data; + /* Send a frame from the file to the appropriate channel */ + /* Read the data into the buffer */ + s->fr->offset = AST_FRIENDLY_OFFSET; + s->fr->datalen = size; + s->fr->data = s->buf + sizeof(struct ast_frame) + AST_FRIENDLY_OFFSET; + if ((res = read(s->fd, s->fr->data , size)) != size) { + ast_log(LOG_WARNING, "Short read (%d of %d bytes) (%s)!\n", res, size, strerror(errno)); + s->owner->streamid = -1; + return 0; + } + delay = MP3_TIMELEN; + s->fr->timelen = delay; + /* Lastly, process the frame */ + if (ast_write(s->owner, s->fr)) { + ast_log(LOG_WARNING, "Failed to write frame\n"); + s->owner->streamid = -1; + return 0; + } + return -1; +} + +static int mp3_apply(struct ast_channel *c, struct ast_filestream *s) +{ + /* Select our owner for this stream, and get the ball rolling. */ + s->owner = c; + s->owner->streamid = ast_sched_add(s->owner->sched, MP3_TIMELEN, ast_read_callback, s); + ast_read_callback(s); + return 0; +} + +static int mp3_write(struct ast_filestream *fs, struct ast_frame *f) +{ + int res; + if (fs->fr) { + ast_log(LOG_WARNING, "Asked to write on a read stream??\n"); + return -1; + } + if (f->frametype != AST_FRAME_VOICE) { + ast_log(LOG_WARNING, "Asked to write non-voice frame!\n"); + return -1; + } + if (f->subclass != AST_FORMAT_MP3) { + ast_log(LOG_WARNING, "Asked to write non-mp3 frame!\n"); + return -1; + } + if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) { + ast_log(LOG_WARNING, "Unable to write frame: res=%d (%s)\n", res, strerror(errno)); + return -1; + } + return 0; +} + +char *mp3_getcomment(struct ast_filestream *s) +{ + return NULL; +} + +int load_module() +{ + return ast_format_register(name, exts, AST_FORMAT_MP3, + mp3_open, + mp3_rewrite, + mp3_apply, + mp3_write, + mp3_read, + mp3_close, + mp3_getcomment); + + +} + +int unload_module() +{ + struct ast_filestream *tmp, *tmpl; + if (pthread_mutex_lock(&mp3_lock)) { + ast_log(LOG_WARNING, "Unable to lock mp3 list\n"); + return -1; + } + tmp = glist; + while(tmp) { + if (tmp->owner) + ast_softhangup(tmp->owner); + tmpl = tmp; + tmp = tmp->next; + free(tmpl); + } + pthread_mutex_unlock(&mp3_lock); + return ast_format_unregister(name); +} + +int usecount() +{ + int res; + if (pthread_mutex_lock(&mp3_lock)) { + ast_log(LOG_WARNING, "Unable to lock mp3 list\n"); + return -1; + } + res = glistcnt; + pthread_mutex_unlock(&mp3_lock); + return res; +} + +char *description() +{ + return desc; +} + diff --git a/frame.c b/frame.c new file mode 100755 index 0000000000000000000000000000000000000000..ba0e48e925411a1811225d1c764c30beb4925a81 --- /dev/null +++ b/frame.c @@ -0,0 +1,89 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Frame manipulation routines + * + * Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#include <asterisk/frame.h> +#include <asterisk/logger.h> +#include <stdlib.h> +#include <string.h> + +/* + * 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) { + free(fr); + } +} + +void ast_frchain(struct ast_frame_chain *fc) +{ + struct ast_frame_chain *last; + while(fc) { + last = fc; + fc = fc->next; + if (last->fr) + ast_frfree(last->fr); + free(last); + } +} + +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 = malloc(sizeof(struct ast_frame)); + 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 + fr->offset); + out->data += fr->offset; + out->offset = fr->offset; + out->datalen = fr->datalen; + memcpy(out->data, fr->data, fr->datalen); + if (!out->data) { + ast_log(LOG_WARNING, "Out of memory\n"); + return NULL; + } + } + out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA; + return out; +} diff --git a/include/asterisk/file.h b/include/asterisk/file.h new file mode 100755 index 0000000000000000000000000000000000000000..da4ffaa42a023285a79de22a02de2a485cd5af58 --- /dev/null +++ b/include/asterisk/file.h @@ -0,0 +1,84 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Generic File Format Support. + * + * Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#ifndef _ASTERISK_FILE_H +#define _ASTERISK_FILE_H + +#include <asterisk/channel.h> +#include <asterisk/frame.h> +#include <fcntl.h> + + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + + +/* Convenient for waiting */ +#define AST_DIGIT_ANY "0123456789#*" + +/* Defined by individual formats. First item MUST be a + pointer for use by the stream manager */ +struct ast_filestream; + +/* Register a new file format capability */ +int ast_format_register(char *name, char *exts, int format, + struct ast_filestream * (*open)(int fd), + struct ast_filestream * (*rewrite)(int fd, char *comment), + int (*apply)(struct ast_channel *, struct ast_filestream *), + int (*write)(struct ast_filestream *, struct ast_frame *), + struct ast_frame * (*read)(struct ast_filestream *), + void (*close)(struct ast_filestream *), + char * (*getcomment)(struct ast_filestream *)); + +int ast_format_unregister(char *name); + +/* Start streaming a file */ +int ast_streamfile(struct ast_channel *c, char *filename); + +/* Stop playback of a stream */ +int ast_stopstream(struct ast_channel *c); + +/* See if a given file exists in a given format. If fmt is NULL, any format is accepted.*/ +int ast_fileexists(char *filename, char *fmt); + +/* Rename a given file in a given format, or if fmt is NULL, then do so for all */ +int ast_filerename(char *oldname, char *newname, char *fmt); + +/* Delete a given file in a given format, or if fmt is NULL, then do so for all */ +int ast_filedelete(char *filename, char *fmt); + +/* Wait for a stream to stop or for any one of a given digit to arrive, Returns + 0 if the stream finishes, the character if it was interrupted, and -1 on error */ +char ast_waitstream(struct ast_channel *c, char *breakon); + +/* Create an outgoing file stream. oflags are flags for the open() command, and + if check is non-zero, then it will not write a file if there are any files that + start with that name and have an extension */ +struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, int oflags, int check, mode_t mode); + +/* Send a frame to a filestream -- note: does NOT free the frame, call ast_frfree manually */ +int ast_writestream(struct ast_filestream *fs, struct ast_frame *f); + +/* Close a playback or recording stream */ +int ast_closestream(struct ast_filestream *f); + +#define AST_RESERVED_POINTERS 4 + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + + + +#endif diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h new file mode 100755 index 0000000000000000000000000000000000000000..d9fb440f0afb3ec538b793ef6622324aa95f532c --- /dev/null +++ b/include/asterisk/frame.h @@ -0,0 +1,101 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Asterisk internal frame definitions. + * + * Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#ifndef _ASTERISK_FRAME_H +#define _ASTERISK_FRAME_H + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +/* A frame of data read used to communicate between + between channels and applications */ +struct ast_frame { + int frametype; /* Kind of frame */ + int subclass; /* Subclass, frame dependent */ + int datalen; /* Length of data */ + int timelen; /* Amount of time associated with this frame */ + int mallocd; /* Was the data malloc'd? i.e. should we + free it when we discard the frame? */ + int offset; /* How far into "data" the data really starts */ + char *src; /* Optional source of frame for debugging */ + void *data; /* Pointer to actual data */ +}; + +struct ast_frame_chain { + /* XXX Should ast_frame chain's be just prt of frames, i.e. should they just link? XXX */ + struct ast_frame *fr; + struct ast_frame_chain *next; +}; + +#define AST_FRIENDLY_OFFSET 64 /* It's polite for a a new frame to + have at least this number of bytes + of offset before your real frame data + so that additional headers can be + added. */ + +#define AST_MALLOCD_HDR (1 << 0) /* Need the header be free'd? */ +#define AST_MALLOCD_DATA (1 << 1) /* Need the data be free'd? */ +#define AST_MALLOCD_SRC (1 << 2) /* Need the source be free'd? (haha!) */ + +/* Frame types */ +#define AST_FRAME_DTMF 1 /* A DTMF digit, subclass is the digit */ +#define AST_FRAME_VOICE 2 /* Voice data, subclass is AST_FORMAT_* */ +#define AST_FRAME_VIDEO 3 /* Video frame, maybe?? :) */ +#define AST_FRAME_CONTROL 4 /* A control frame, subclass is AST_CONTROL_* */ +#define AST_FRAME_NULL 5 /* An empty, useless frame */ + +/* Data formats for capabilities and frames alike */ +#define AST_FORMAT_G723_1 (1 << 0) /* G.723.1 compression */ +#define AST_FORMAT_GSM (1 << 1) /* GSM compression */ +#define AST_FORMAT_ULAW (1 << 2) /* Raw mu-law data (G.711) */ +#define AST_FORMAT_ALAW (1 << 3) /* Raw A-law data (G.711) */ +#define AST_FORMAT_MP3 (1 << 4) /* MPEG-2 layer 3 */ +#define AST_FORMAT_ADPCM (1 << 5) /* ADPCM */ +#define AST_FORMAT_SLINEAR (1 << 6) /* Raw 16-bit Signed Linear (8000 Hz) PCM */ +#define AST_FORMAT_MAX_AUDIO (1 << 15) /* Maximum audio format */ +#define AST_FORMAT_JPEG (1 << 16) /* JPEG Images */ +#define AST_FORMAT_PNG (1 << 17) /* PNG Images */ +#define AST_FORMAT_H261 (1 << 18) /* H.261 Video */ +#define AST_FORMAT_H263 (1 << 19) /* H.263 Video */ + +/* Control frame types */ +#define AST_CONTROL_HANGUP 1 /* Other end has hungup */ +#define AST_CONTROL_RING 2 /* Local ring */ +#define AST_CONTROL_RINGING 3 /* Remote end is ringing */ +#define AST_CONTROL_ANSWER 4 /* Remote end has answered */ +#define AST_CONTROL_BUSY 5 /* Remote end is busy */ +#define AST_CONTROL_TAKEOFFHOOK 6 /* Make it go off hook */ +#define AST_CONTROL_OFFHOOK 7 /* Line is off hook */ + +/* Request a frame be allocated. source is an optional source of the frame, + len is the requested length, or "0" if the caller will supply the buffer */ +struct ast_frame *ast_fralloc(char *source, int len); + +/* Free a frame, and the memory it used if applicable */ +void ast_frfree(struct ast_frame *fr); + +/* Take a frame, and if it's not been malloc'd, make a malloc'd copy + and if the data hasn't been malloced then make the + data malloc'd. If you need to store frames, say for queueing, then + you should call this function. */ +struct ast_frame *ast_frisolate(struct ast_frame *fr); + +void ast_frchain(struct ast_frame_chain *fc); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + + +#endif diff --git a/include/asterisk/translate.h b/include/asterisk/translate.h new file mode 100755 index 0000000000000000000000000000000000000000..68edc046dca6c755fe3a2ccab054552584cf022d --- /dev/null +++ b/include/asterisk/translate.h @@ -0,0 +1,76 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Translate via the use of pseudo channels + * + * Copyright (C) 1999, Adtran Inc. and Linux Support Services, LLC + * + * Mark Spencer <markster@linux-support.net> + * + * This program is free software, distributed under the terms of + * the GNU General Public License + */ + +#ifndef _ASTERISK_TRANSLATE_H +#define _ASTERISK_TRANSLATE_H + +#define MAX_FORMAT 32 + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +#include <asterisk/frame.h> + +/* Declared by individual translators */ +struct ast_translator_pvt; + +struct ast_translator { + char name[80]; + int srcfmt; + int dstfmt; + struct ast_translator_pvt *(*new)(); + int (*framein)(struct ast_translator_pvt *pvt, struct ast_frame *in); + struct ast_frame * (*frameout)(struct ast_translator_pvt *pvt); + void (*destroy)(struct ast_translator_pvt *pvt); + /* For performance measurements */ + /* Generate an example frame */ + struct ast_frame * (*sample)(void); + /* Cost in milliseconds for encoding/decoding 1 second of sound */ + int cost; + /* For linking, not to be modified by the translator */ + struct ast_translator *next; +}; + +struct ast_trans_pvt; + +/* Create a pseudo channel which translates from a real channel into our + desired format. When a translator is installed, you should not use the + sub channel until you have stopped the translator. For all other + actions, use the real channel. Generally, translators should be created + when needed and immediately destroyed when no longer needed. */ + +/* Directions */ +#define AST_DIRECTION_OUT 1 +#define AST_DIRECTION_IN 2 +#define AST_DIRECTION_BOTH 3 + +extern struct ast_channel *ast_translator_create(struct ast_channel *real, int format, int direction); +extern void ast_translator_destroy(struct ast_channel *tran); +/* Register a Codec translator */ +extern int ast_register_translator(struct ast_translator *t); +/* Unregister same */ +extern int ast_unregister_translator(struct ast_translator *t); +/* Given a list of sources, and a designed destination format, which should + I choose? */ +extern int ast_translator_best_choice(int dst, int srcs); +extern struct ast_trans_pvt *ast_translator_build_path(int source, int dest); +extern void ast_translator_free_path(struct ast_trans_pvt *tr); +extern struct ast_frame_chain *ast_translate(struct ast_trans_pvt *tr, struct ast_frame *f); + + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif