354 lines
8.6 KiB
C
354 lines
8.6 KiB
C
|
|
$FreeBSD$
|
|
|
|
--- /dev/null Fri Jan 13 12:18:51 2006
|
|
+++ formats/format_g723_1.c Fri Jan 13 12:18:41 2006
|
|
@@ -0,0 +1,348 @@
|
|
+/*
|
|
+ * Asterisk -- A telephony toolkit for Linux.
|
|
+ *
|
|
+ * Save and read raw, headerless G723.1 Annex A data.
|
|
+ *
|
|
+ * Copyright (C) 1999, Mark Spencer <markster@linux-support.net>
|
|
+ * Copyright (C) 2003, Maxim Sobolev <sobomax@FreeBSD.org>
|
|
+ *
|
|
+ * This program is free software, distributed under the terms of
|
|
+ * the GNU General Public License
|
|
+ */
|
|
+
|
|
+#include <stdio.h>
|
|
+#include <asterisk/lock.h>
|
|
+#include <asterisk/channel.h>
|
|
+#include <asterisk/file.h>
|
|
+#include <asterisk/logger.h>
|
|
+#include <asterisk/sched.h>
|
|
+#include <asterisk/module.h>
|
|
+#include <netinet/in.h>
|
|
+#include <arpa/inet.h>
|
|
+#include <stdlib.h>
|
|
+#include <sys/time.h>
|
|
+#include <unistd.h>
|
|
+#include <errno.h>
|
|
+#include <string.h>
|
|
+#include <pthread.h>
|
|
+#ifdef __linux__
|
|
+#include <endian.h>
|
|
+#else
|
|
+#include <machine/endian.h>
|
|
+#endif
|
|
+
|
|
+/* Based on format_g729.c */
|
|
+
|
|
+#define TYPE_HIGH 0x0
|
|
+#define TYPE_LOW 0x1
|
|
+#define TYPE_SILENCE 0x2
|
|
+#define TYPE_DONTSEND 0x3
|
|
+#define TYPE_MASK 0x3
|
|
+
|
|
+struct ast_filestream {
|
|
+ void *reserved[AST_RESERVED_POINTERS];
|
|
+ /* This is what a filestream means to us */
|
|
+ FILE *f; /* Descriptor */
|
|
+ struct ast_frame fr; /* Frame information */
|
|
+ char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */
|
|
+ char empty; /* Empty character */
|
|
+ unsigned char g723[24]; /* One Real G723.1 Frame */
|
|
+};
|
|
+
|
|
+static long g723_tell(struct ast_filestream *);
|
|
+
|
|
+AST_MUTEX_DEFINE_STATIC(g723_lock);
|
|
+static int glistcnt = 0;
|
|
+
|
|
+static char *name = "g723";
|
|
+static char *desc = "Raw G723.1 Annex A data";
|
|
+static char *exts = "g723";
|
|
+
|
|
+static int g723_len(unsigned char buf)
|
|
+{
|
|
+ switch(buf & TYPE_MASK) {
|
|
+ case TYPE_DONTSEND:
|
|
+ return 2;
|
|
+ break;
|
|
+ case TYPE_SILENCE:
|
|
+ return 4;
|
|
+ break;
|
|
+ case TYPE_HIGH:
|
|
+ return 24;
|
|
+ break;
|
|
+ case TYPE_LOW:
|
|
+ return 20;
|
|
+ break;
|
|
+ default:
|
|
+ ast_log(LOG_WARNING, "Badly encoded G723.1 frame (%d)\n", buf & TYPE_MASK);
|
|
+ }
|
|
+ return -1;
|
|
+}
|
|
+
|
|
+static struct ast_filestream *g723_open(FILE *f)
|
|
+{
|
|
+ /* 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)))) {
|
|
+ memset(tmp, 0, sizeof(struct ast_filestream));
|
|
+ if (ast_mutex_lock(&g723_lock)) {
|
|
+ ast_log(LOG_WARNING, "Unable to lock g723 list\n");
|
|
+ free(tmp);
|
|
+ return NULL;
|
|
+ }
|
|
+ tmp->f = f;
|
|
+ tmp->fr.data = tmp->g723;
|
|
+ tmp->fr.frametype = AST_FRAME_VOICE;
|
|
+ tmp->fr.subclass = AST_FORMAT_G723_1;
|
|
+ /* datalen will vary for each frame */
|
|
+ tmp->fr.src = name;
|
|
+ tmp->fr.mallocd = 0;
|
|
+ glistcnt++;
|
|
+ ast_mutex_unlock(&g723_lock);
|
|
+ ast_update_use_count();
|
|
+ }
|
|
+ return tmp;
|
|
+}
|
|
+
|
|
+static struct ast_filestream *g723_rewrite(FILE *f, const 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)))) {
|
|
+ memset(tmp, 0, sizeof(struct ast_filestream));
|
|
+ if (ast_mutex_lock(&g723_lock)) {
|
|
+ ast_log(LOG_WARNING, "Unable to lock g723 list\n");
|
|
+ free(tmp);
|
|
+ return NULL;
|
|
+ }
|
|
+ tmp->f = f;
|
|
+ glistcnt++;
|
|
+ ast_mutex_unlock(&g723_lock);
|
|
+ ast_update_use_count();
|
|
+ } else
|
|
+ ast_log(LOG_WARNING, "Out of memory\n");
|
|
+ return tmp;
|
|
+}
|
|
+
|
|
+static void g723_close(struct ast_filestream *s)
|
|
+{
|
|
+ if (ast_mutex_lock(&g723_lock)) {
|
|
+ ast_log(LOG_WARNING, "Unable to lock g723 list\n");
|
|
+ return;
|
|
+ }
|
|
+ glistcnt--;
|
|
+ ast_mutex_unlock(&g723_lock);
|
|
+ ast_update_use_count();
|
|
+ fclose(s->f);
|
|
+ free(s);
|
|
+ s = NULL;
|
|
+}
|
|
+
|
|
+static struct ast_frame *g723_read(struct ast_filestream *s, int *whennext)
|
|
+{
|
|
+ int res;
|
|
+ /* Send a frame from the file to the appropriate channel */
|
|
+ s->fr.frametype = AST_FRAME_VOICE;
|
|
+ s->fr.subclass = AST_FORMAT_G723_1;
|
|
+ s->fr.offset = AST_FRIENDLY_OFFSET;
|
|
+ s->fr.samples = 240;
|
|
+ s->fr.mallocd = 0;
|
|
+ s->fr.data = s->g723;
|
|
+ if ((res = fread(s->g723, 1, 1, s->f)) != 1) {
|
|
+ if (res)
|
|
+ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
|
|
+ return NULL;
|
|
+ }
|
|
+ s->fr.datalen = g723_len(s->g723[0]);
|
|
+ if (s->fr.datalen < 0) {
|
|
+ ast_log(LOG_WARNING, "Invalid G723.1 frame!\n");
|
|
+ return NULL;
|
|
+ }
|
|
+ if (s->fr.datalen > 1 && (res = fread(s->g723 + 1, 1, s->fr.datalen - 1, s->f)) != s->fr.datalen - 1) {
|
|
+ if (res)
|
|
+ ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
|
|
+ return NULL;
|
|
+ }
|
|
+ *whennext = s->fr.samples;
|
|
+ return &s->fr;
|
|
+}
|
|
+
|
|
+static int g723_write(struct ast_filestream *fs, struct ast_frame *f)
|
|
+{
|
|
+ int res;
|
|
+ unsigned char *cp;
|
|
+ if (f->frametype != AST_FRAME_VOICE) {
|
|
+ ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
|
|
+ return -1;
|
|
+ }
|
|
+ if (f->subclass != AST_FORMAT_G723_1) {
|
|
+ ast_log(LOG_WARNING, "Asked to write non-G723.1 frame (%d)!\n", f->subclass);
|
|
+ return -1;
|
|
+ }
|
|
+ for (cp = f->data; cp < (unsigned char *)f->data + f->datalen; cp += res) {
|
|
+ res = g723_len(cp[0]);
|
|
+ if (res < 0) {
|
|
+ ast_log(LOG_WARNING, "Asked to write invalid G723.1 frame!\n");
|
|
+ return -1;
|
|
+ }
|
|
+ }
|
|
+ if (cp != (unsigned char *)f->data + f->datalen) {
|
|
+ ast_log(LOG_WARNING, "Invalid G723.1 data length, %d\n", f->datalen);
|
|
+ return -1;
|
|
+ }
|
|
+ if ((res = fwrite(f->data, 1, f->datalen, fs->f)) != f->datalen) {
|
|
+ ast_log(LOG_WARNING, "Bad write %d: %s\n", res, strerror(errno));
|
|
+ return -1;
|
|
+ }
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static char *g723_getcomment(struct ast_filestream *s)
|
|
+{
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static int g723_seek(struct ast_filestream *fs, long sample_offset, int whence)
|
|
+{
|
|
+ long cur, offset, max;
|
|
+ off_t coffset, moffset, soffset;
|
|
+ int res;
|
|
+ unsigned char c;
|
|
+
|
|
+ offset = 0; /* Shut up gcc warning */
|
|
+ if (whence == SEEK_SET) {
|
|
+ offset = sample_offset;
|
|
+ } else if (whence == SEEK_CUR || whence == SEEK_FORCECUR) {
|
|
+ if ((cur = g723_tell(fs)) == -1) {
|
|
+ ast_log(LOG_WARNING, "Can't get current position!\n");
|
|
+ return -1;
|
|
+ }
|
|
+ offset = cur + sample_offset;
|
|
+ }
|
|
+ if (fseeko(fs->f, 0, SEEK_END) == -1) {
|
|
+ ast_log(LOG_WARNING, "Can't seek stream to an end!\n");
|
|
+ return -1;
|
|
+ }
|
|
+ else {
|
|
+ moffset = ftello(fs->f);
|
|
+ }
|
|
+ if (whence == SEEK_END) {
|
|
+ if ((max = g723_tell(fs)) == -1) {
|
|
+ ast_log(LOG_WARNING, "Can't get maximum position!\n");
|
|
+ return -1;
|
|
+ }
|
|
+ offset = max - sample_offset;
|
|
+ }
|
|
+ if (offset < 0)
|
|
+ offset = 0;
|
|
+ soffset = -1;
|
|
+ for (coffset = 0; coffset < moffset && offset > 0; coffset += res) {
|
|
+ if (fseeko(fs->f, coffset, SEEK_SET) == -1) {
|
|
+ ast_log(LOG_WARNING, "Can't seek to offset %lli!\n", coffset);
|
|
+ return -1;
|
|
+ }
|
|
+ if (fread(&c, 1, 1, fs->f) != 1) {
|
|
+ ast_log(LOG_WARNING, "Can't read from offset %lli!\n", coffset);
|
|
+ return -1;
|
|
+ }
|
|
+ soffset = coffset;
|
|
+ if ((res = g723_len(c)) < 0) {
|
|
+ ast_log(LOG_WARNING, "Invalid G723.1 frame at offset %lli!\n", coffset);
|
|
+ return -1;
|
|
+ }
|
|
+ if (res > 1)
|
|
+ offset -= 240;
|
|
+ }
|
|
+ if (soffset != -1 && fseeko(fs->f, soffset, SEEK_SET) == -1) {
|
|
+ ast_log(LOG_WARNING, "Can't seek to offset %lli!\n", soffset);
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int g723_trunc(struct ast_filestream *fs)
|
|
+{
|
|
+ /* Truncate file to current length */
|
|
+ if (ftruncate(fileno(fs->f), ftello(fs->f)) < 0)
|
|
+ return -1;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static long g723_tell(struct ast_filestream *fs)
|
|
+{
|
|
+ off_t offset, coffset;
|
|
+ int res;
|
|
+ long rval;
|
|
+ unsigned char c;
|
|
+
|
|
+ offset = ftello(fs->f);
|
|
+ rval = 0;
|
|
+ for (coffset = 0; coffset < offset; coffset += res) {
|
|
+ if (fseeko(fs->f, coffset, SEEK_SET) == -1) {
|
|
+ ast_log(LOG_WARNING, "Can't seek to offset %llu!\n", coffset);
|
|
+ return -1;
|
|
+ }
|
|
+ if (fread(&c, 1, 1, fs->f) != 1) {
|
|
+ ast_log(LOG_WARNING, "Can't read from offset %llu!\n", coffset);
|
|
+ return -1;
|
|
+ }
|
|
+ if ((res = g723_len(c)) < 0) {
|
|
+ ast_log(LOG_WARNING, "Invalid G723.1 frame at offset %llu!\n", coffset);
|
|
+ return -1;
|
|
+ }
|
|
+ if (res > 1)
|
|
+ rval += 240;
|
|
+ }
|
|
+ if (fseeko(fs->f, offset, SEEK_SET) == -1) {
|
|
+ ast_log(LOG_WARNING, "Can't seek to offset %llu!\n", offset);
|
|
+ return -1;
|
|
+ }
|
|
+ return rval;
|
|
+}
|
|
+
|
|
+int load_module()
|
|
+{
|
|
+ return ast_format_register(name, exts, AST_FORMAT_G723_1,
|
|
+ g723_open,
|
|
+ g723_rewrite,
|
|
+ g723_write,
|
|
+ g723_seek,
|
|
+ g723_trunc,
|
|
+ g723_tell,
|
|
+ g723_read,
|
|
+ g723_close,
|
|
+ g723_getcomment);
|
|
+}
|
|
+
|
|
+int unload_module()
|
|
+{
|
|
+ return ast_format_unregister(name);
|
|
+}
|
|
+
|
|
+int usecount()
|
|
+{
|
|
+ int res;
|
|
+ if (ast_mutex_lock(&g723_lock)) {
|
|
+ ast_log(LOG_WARNING, "Unable to lock g723 list\n");
|
|
+ return -1;
|
|
+ }
|
|
+ res = glistcnt;
|
|
+ ast_mutex_unlock(&g723_lock);
|
|
+ return res;
|
|
+}
|
|
+
|
|
+char *description()
|
|
+{
|
|
+ return desc;
|
|
+}
|
|
+
|
|
+
|
|
+char *key()
|
|
+{
|
|
+ return ASTERISK_GPL_KEY;
|
|
+}
|