Version 0.1.0 from FTP
authorMark Spencer <markster@digium.com>
Sun, 21 Nov 1999 02:26:43 +0000 (02:26 +0000)
committerMark Spencer <markster@digium.com>
Sun, 21 Nov 1999 02:26:43 +0000 (02:26 +0000)
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@65 65c4cc65-6c06-0410-ace0-fbb531ad65f3

formats/format_mp3.c [new file with mode: 0755]
frame.c [new file with mode: 0755]
include/asterisk/file.h [new file with mode: 0755]
include/asterisk/frame.h [new file with mode: 0755]
include/asterisk/translate.h [new file with mode: 0755]

diff --git a/formats/format_mp3.c b/formats/format_mp3.c
new file mode 100755 (executable)
index 0000000..811acbe
--- /dev/null
@@ -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 (executable)
index 0000000..ba0e48e
--- /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 (executable)
index 0000000..da4ffaa
--- /dev/null
@@ -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 (executable)
index 0000000..d9fb440
--- /dev/null
@@ -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 (executable)
index 0000000..68edc04
--- /dev/null
@@ -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