dect
/
asterisk
Archived
13
0
Fork 0

Version 0.3.0 from FTP

git-svn-id: http://svn.digium.com/svn/asterisk/trunk@608 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
markster 2003-02-06 22:11:43 +00:00
parent 12b57158d6
commit 8b1f06abb4
10 changed files with 1311 additions and 134 deletions

View File

@ -16,6 +16,7 @@
#include <asterisk/channel.h>
#include <asterisk/pbx.h>
#include <asterisk/module.h>
#include <asterisk/astdb.h>
#include <math.h>
#include <stdlib.h>
#include <unistd.h>
@ -23,12 +24,16 @@
#include <stdlib.h>
#include <sys/signal.h>
#include <sys/time.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <asterisk/cli.h>
#include <asterisk/logger.h>
#include <asterisk/options.h>
#include <asterisk/image.h>
#include <asterisk/say.h>
#include "../asterisk.h"
#include "../astconf.h"
#include <pthread.h>
@ -67,6 +72,9 @@ STANDARD_LOCAL_USER;
LOCAL_USER_DECL;
extern char *pbx_builtin_getvar_helper(struct ast_channel *chan, char *name);
extern void pbx_builtin_setvar_helper(struct ast_channel *chan, char *name, char *value);
#define TONE_BLOCK_SIZE 200
static float loudness = 8192.0;
@ -97,7 +105,7 @@ static int launch_script(char *script, char *args, int *fds, int *opid)
int fromast[2];
int x;
if (script[0] != '/') {
snprintf(tmp, sizeof(tmp), "%s/%s", AST_AGI_DIR, script);
snprintf(tmp, sizeof(tmp), "%s/%s", (char *)ast_config_AST_AGI_DIR, script);
script = tmp;
}
if (pipe(toast)) {
@ -124,7 +132,8 @@ static int launch_script(char *script, char *args, int *fds, int *opid)
close(x);
/* Execute script */
execl(script, script, args, NULL);
ast_log(LOG_WARNING, "Failed to execute '%s': %s\n", script, strerror(errno));
/* Can't use ast_log since FD's are closed */
fprintf(stderr, "Failed to execute '%s': %s\n", script, strerror(errno));
exit(1);
}
if (option_verbose > 2)
@ -151,6 +160,7 @@ static void setup_env(struct ast_channel *chan, char *request, int fd)
/* ANI/DNIS */
fdprintf(fd, "agi_callerid: %s\n", chan->callerid ? chan->callerid : "");
fdprintf(fd, "agi_dnid: %s\n", chan->dnid ? chan->dnid : "");
fdprintf(fd, "agi_rdnis: %s\n", chan->rdnis ? chan->rdnis : "");
/* Context information */
fdprintf(fd, "agi_context: %s\n", chan->context);
@ -266,19 +276,41 @@ static int handle_sendimage(struct ast_channel *chan, int fd, int argc, char *ar
static int handle_streamfile(struct ast_channel *chan, int fd, int argc, char *argv[])
{
int res;
if (argc != 4)
struct ast_filestream *fs;
long sample_offset = 0;
long max_length;
if (argc < 4)
return RESULT_SHOWUSAGE;
res = ast_streamfile(chan, argv[2],chan->language);
if (argc > 5)
return RESULT_SHOWUSAGE;
if ((argc > 4) && (sscanf(argv[4], "%ld", &sample_offset) != 1))
return RESULT_SHOWUSAGE;
fs = ast_openstream(chan, argv[2], chan->language);
if(!fs){
fdprintf(fd, "200 result=%d endpos=%ld\n", 0, sample_offset);
ast_log(LOG_WARNING, "Unable to open %s\n", argv[2]);
return RESULT_FAILURE;
}
ast_seekstream(fs, 0, SEEK_END);
max_length = ast_tellstream(fs);
ast_seekstream(fs, sample_offset, SEEK_SET);
res = ast_applystream(chan, fs);
res = ast_playstream(fs);
if (res) {
fdprintf(fd, "200 result=%d\n", res);
fdprintf(fd, "200 result=%d endpos=%ld\n", res, sample_offset);
if (res >= 0)
return RESULT_SHOWUSAGE;
else
return RESULT_FAILURE;
}
res = ast_waitstream(chan, argv[3]);
/* this is to check for if ast_waitstream closed the stream, we probably are at
* the end of the stream, return that amount, else check for the amount */
sample_offset = (chan->stream)?ast_tellstream(fs):max_length;
ast_stopstream(chan);
fdprintf(fd, "200 result=%d\n", res);
fdprintf(fd, "200 result=%d endpos=%ld\n", res, sample_offset);
if (res >= 0)
return RESULT_SUCCESS;
else
@ -386,6 +418,7 @@ static int handle_recordfile(struct ast_channel *chan, int fd, int argc, char *a
struct ast_filestream *fs;
struct ast_frame *f;
struct timeval tv, start;
long sample_offset = 0;
int res = 0;
int ms;
@ -393,30 +426,42 @@ static int handle_recordfile(struct ast_channel *chan, int fd, int argc, char *a
return RESULT_SHOWUSAGE;
if (sscanf(argv[5], "%i", &ms) != 1)
return RESULT_SHOWUSAGE;
/* backward compatibility, if no offset given, arg[6] would have been
* caught below and taken to be a beep, else if it is a digit then it is a
* offset */
if ((argc >6) && (sscanf(argv[6], "%ld", &sample_offset) != 1))
res = ast_streamfile(chan, "beep", chan->language);
if (argc > 6)
if (argc > 7)
res = ast_streamfile(chan, "beep", chan->language);
if (!res)
res = ast_waitstream(chan, argv[4]);
if (!res) {
fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_TRUNC | O_WRONLY, 0, 0644);
fs = ast_writefile(argv[2], argv[3], NULL, O_CREAT | O_WRONLY, 0, 0644);
if (!fs) {
res = -1;
fdprintf(fd, "200 result=%d (writefile)\n", res);
return RESULT_FAILURE;
}
chan->stream = fs;
ast_applystream(chan,fs);
/* really should have checks */
ast_seekstream(fs, sample_offset, SEEK_SET);
ast_truncstream(fs);
gettimeofday(&start, NULL);
gettimeofday(&tv, NULL);
while ((ms < 0) || (((tv.tv_sec - start.tv_sec) * 1000 + (tv.tv_usec - start.tv_usec)/1000) < ms)) {
res = ast_waitfor(chan, -1);
if (res < 0) {
ast_closestream(fs);
fdprintf(fd, "200 result=%d (waitfor)\n", res);
fdprintf(fd, "200 result=%d (waitfor) endpos=%ld\n", res,sample_offset);
return RESULT_FAILURE;
}
f = ast_read(chan);
if (!f) {
fdprintf(fd, "200 result=%d (hangup)\n", 0);
fdprintf(fd, "200 result=%d (hangup) endpos=%ld\n", 0, sample_offset);
ast_closestream(fs);
return RESULT_FAILURE;
}
@ -424,7 +469,8 @@ static int handle_recordfile(struct ast_channel *chan, int fd, int argc, char *a
case AST_FRAME_DTMF:
if (strchr(argv[4], f->subclass)) {
/* This is an interrupting chracter */
fdprintf(fd, "200 result=%d (dtmf)\n", f->subclass);
sample_offset = ast_tellstream(fs);
fdprintf(fd, "200 result=%d (dtmf) endpos=%ld\n", f->subclass, sample_offset);
ast_closestream(fs);
ast_frfree(f);
return RESULT_SUCCESS;
@ -432,15 +478,19 @@ static int handle_recordfile(struct ast_channel *chan, int fd, int argc, char *a
break;
case AST_FRAME_VOICE:
ast_writestream(fs, f);
/* this is a safe place to check progress since we know that fs
* is valid after a write, and it will then have our current
* location */
sample_offset = ast_tellstream(fs);
break;
}
ast_frfree(f);
}
gettimeofday(&tv, NULL);
fdprintf(fd, "200 result=%d (timeout)\n", res);
gettimeofday(&tv, NULL);
}
fdprintf(fd, "200 result=%d (timeout) endpos=%ld\n", res, sample_offset);
ast_closestream(fs);
} else
fdprintf(fd, "200 result=%d (randomerror)\n", res);
fdprintf(fd, "200 result=%d (randomerror) endpos=%ld\n", res, sample_offset);
return RESULT_SUCCESS;
}
@ -464,9 +514,30 @@ static int handle_autohangup(struct ast_channel *chan, int fd, int argc, char *a
static int handle_hangup(struct ast_channel *chan, int fd, int argc, char **argv)
{
ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
fdprintf(fd, "200 result=1\n");
return RESULT_SUCCESS;
struct ast_channel *c;
if (argc==1) {
/* no argument: hangup the current channel */
ast_softhangup(chan,AST_SOFTHANGUP_EXPLICIT);
fdprintf(fd, "200 result=1\n");
return RESULT_SUCCESS;
} else if (argc==2) {
/* one argument: look for info on the specified channel */
c = ast_channel_walk(NULL);
while (c) {
if (strcasecmp(argv[1],c->name)==0) {
/* we have a matching channel */
ast_softhangup(c,AST_SOFTHANGUP_EXPLICIT);
fdprintf(fd, "200 result=1\n");
return RESULT_SUCCESS;
}
c = ast_channel_walk(c);
}
/* if we get this far no channel name matched the argument given */
fdprintf(fd, "200 result=-1\n");
return RESULT_SUCCESS;
} else {
return RESULT_SHOWUSAGE;
}
}
static int handle_exec(struct ast_channel *chan, int fd, int argc, char **argv)
@ -496,7 +567,7 @@ static int handle_exec(struct ast_channel *chan, int fd, int argc, char **argv)
static int handle_setcallerid(struct ast_channel *chan, int fd, int argc, char **argv)
{
if (argv[2])
ast_set_callerid(chan, argv[2]);
ast_set_callerid(chan, argv[2], 0);
/* strncpy(chan->callerid, argv[2], sizeof(chan->callerid)-1);
*/ fdprintf(fd, "200 result=1\n");
@ -505,13 +576,192 @@ static int handle_setcallerid(struct ast_channel *chan, int fd, int argc, char *
static int handle_channelstatus(struct ast_channel *chan, int fd, int argc, char **argv)
{
fdprintf(fd, "200 result=%d\n", chan->_state);
struct ast_channel *c;
if (argc==2) {
/* no argument: supply info on the current channel */
fdprintf(fd, "200 result=%d\n", chan->_state);
return RESULT_SUCCESS;
} else if (argc==3) {
/* one argument: look for info on the specified channel */
c = ast_channel_walk(NULL);
while (c) {
if (strcasecmp(argv[2],c->name)==0) {
fdprintf(fd, "200 result=%d\n", c->_state);
return RESULT_SUCCESS;
}
c = ast_channel_walk(c);
}
/* if we get this far no channel name matched the argument given */
fdprintf(fd, "200 result=-1\n");
return RESULT_SUCCESS;
} else {
return RESULT_SHOWUSAGE;
}
}
static int handle_setvariable(struct ast_channel *chan, int fd, int argc, char **argv)
{
if (argv[3])
pbx_builtin_setvar_helper(chan, argv[2], argv[3]);
fdprintf(fd, "200 result=1\n");
return RESULT_SUCCESS;
}
static int handle_getvariable(struct ast_channel *chan, int fd, int argc, char **argv)
{
char *tempstr;
if ((tempstr = pbx_builtin_getvar_helper(chan, argv[2])) )
fdprintf(fd, "200 result=1 (%s)\n", tempstr);
else
fdprintf(fd, "200 result=0\n");
return RESULT_SUCCESS;
}
static int handle_verbose(struct ast_channel *chan, int fd, int argc, char **argv)
{
int level = 0;
char *prefix;
if (argc < 2)
return RESULT_SHOWUSAGE;
if (argv[2])
sscanf(argv[2], "%d", &level);
switch (level) {
case 4:
prefix = VERBOSE_PREFIX_4;
break;
case 3:
prefix = VERBOSE_PREFIX_3;
break;
case 2:
prefix = VERBOSE_PREFIX_2;
break;
case 1:
default:
prefix = VERBOSE_PREFIX_1;
break;
}
if (level <= option_verbose)
ast_verbose("%s %s: %s\n", prefix, chan->data, argv[1]);
fdprintf(fd, "200 result=1\n");
return RESULT_SUCCESS;
}
static int handle_dbget(struct ast_channel *chan, int fd, int argc, char **argv)
{
int res;
char tmp[256];
if (argc != 4)
return RESULT_SHOWUSAGE;
res = ast_db_get(argv[2], argv[3], tmp, sizeof(tmp));
if (res)
fdprintf(fd, "200 result=0\n");
else
fdprintf(fd, "200 result=1 (%s)\n", tmp);
return RESULT_SUCCESS;
}
static int handle_dbput(struct ast_channel *chan, int fd, int argc, char **argv)
{
int res;
if (argc != 5)
return RESULT_SHOWUSAGE;
res = ast_db_put(argv[2], argv[3], argv[4]);
if (res)
fdprintf(fd, "200 result=0\n");
else
fdprintf(fd, "200 result=1\n");
return RESULT_SUCCESS;
}
static int handle_dbdel(struct ast_channel *chan, int fd, int argc, char **argv)
{
int res;
if (argc != 4)
return RESULT_SHOWUSAGE;
res = ast_db_del(argv[2], argv[3]);
if (res)
fdprintf(fd, "200 result=0\n");
else
fdprintf(fd, "200 result=1\n");
return RESULT_SUCCESS;
}
static int handle_dbdeltree(struct ast_channel *chan, int fd, int argc, char **argv)
{
int res;
if ((argc < 3) || (argc > 4))
return RESULT_SHOWUSAGE;
if (argc == 4)
res = ast_db_deltree(argv[2], argv[3]);
else
res = ast_db_deltree(argv[2], NULL);
if (res)
fdprintf(fd, "200 result=0\n");
else
fdprintf(fd, "200 result=1\n");
return RESULT_SUCCESS;
}
static char usage_dbput[] =
" Usage: DATABASE PUT <family> <key> <value>\n"
" Adds or updates an entry in the Asterisk database for a\n"
" given family, key, and value.\n"
" Returns 1 if succesful, 0 otherwise\n";
static char usage_dbget[] =
" Usage: DATABASE GET <family> <key>\n"
" Retrieves an entry in the Asterisk database for a\n"
" given family and key.\n"
" Returns 0 if <key> is not set. Returns 1 if <key>\n"
" is set and returns the variable in parenthesis\n"
" example return code: 200 result=1 (testvariable)\n";
static char usage_dbdel[] =
" Usage: DATABASE DEL <family> <key>\n"
" Deletes an entry in the Asterisk database for a\n"
" given family and key.\n"
" Returns 1 if succesful, 0 otherwise\n";
static char usage_dbdeltree[] =
" Usage: DATABASE DELTREE <family> [keytree]\n"
" Deletes a family or specific keytree withing a family\n"
" in the Asterisk database.\n"
" Returns 1 if succesful, 0 otherwise\n";
static char usage_verbose[] =
" Usage: VERBOSE <message> <level>\n"
" Sends <message> to the console via verbose message system.\n"
" <level> is the the verbose level (1-4)\n"
" Always returns 1\n";
static char usage_getvariable[] =
" Usage: GET VARIABLE <variablename>\n"
" Returns 0 if <variablename> is not set. Returns 1 if <variablename>\n"
" is set and returns the variable in parenthesis\n"
" example return code: 200 result=1 (testvariable)\n";
static char usage_setvariable[] =
" Usage: SET VARIABLE <variablename> <value>\n";
static char usage_channelstatus[] =
" Usage: CHANNEL STATUS\n"
" Returns the status of the connected channel. Return values:\n"
" Usage: CHANNEL STATUS [<channelname>]\n"
" Returns the status of the specified channel.\n"
" If no channel name is given the returns the status of the\n"
" current channel.\n"
" Return values:\n"
" 0 Channel is down and available\n"
" 1 Channel is down, but reserved\n"
" 2 Channel is off hook\n"
@ -531,9 +781,9 @@ static char usage_exec[] =
" Returns whatever the application returns, or -2 on failure to find application\n";
static char usage_hangup[] =
" Usage: HANGUP\n"
" Hangs up the current channel.\n";
" Usage: HANGUP [<channelname>]\n"
" Hangs up the specified channel.\n"
" If no channel name is given, hangs up the current channel\n";
static char usage_answer[] =
" Usage: ANSWER\n"
@ -576,13 +826,14 @@ static char usage_sendimage[] =
" should not include extensions.\n";
static char usage_streamfile[] =
" Usage: STREAM FILE <filename> <escape digits>\n"
" Usage: STREAM FILE <filename> <escape digits> [sample offset]\n"
" Send the given file, allowing playback to be interrupted by the given\n"
" digits, if any. Use double quotes for the digits if you wish none to be\n"
" permitted. Returns 0 if playback completes without a digit being pressed, or\n"
" the ASCII numerical value of the digit if one was pressed, or -1 on error or\n"
" if the channel was disconnected. Remember, the file extension must not be\n"
" included in the filename.\n";
" permitted. If sample offset is provided then the audio will seek to sample\n"
" offset before play starts. Returns 0 if playback completes without a digit\n"
" being pressed, or the ASCII numerical value of the digit if one was pressed,\n"
" or -1 on error or if the channel was disconnected. Remember, the file\n"
" extension must not be included in the filename.\n";
static char usage_saynumber[] =
" Usage: SAY NUMBER <number> <escape digits>\n"
@ -616,11 +867,12 @@ static char usage_setpriority[] =
" Changes the priority for continuation upon exiting the application.\n";
static char usage_recordfile[] =
" Usage: RECORD FILE <filename> <format> <escape digits> <timeout> [BEEP]\n"
" Usage: RECORD FILE <filename> <format> <escape digits> <timeout> [offset samples] [BEEP]\n"
" Record to a file until a given dtmf digit in the sequence is received\n"
" Returns -1 on hangup or error. The format will specify what kind of file\n"
" will be recorded. The timeout is the maximum record time in milliseconds, or\n"
" -1 for no timeout\n";
" -1 for no timeout. Offset samples is optional, and if provided will seek to\n"
" the offset without exceeding the end of the file\n";
static char usage_autohangup[] =
" Usage: SET AUTOHANGUP <time>\n"
@ -630,7 +882,6 @@ static char usage_autohangup[] =
agi_command commands[] = {
{ { "answer", NULL }, handle_answer, "Asserts answer", usage_answer },
{ { "answer\n", NULL }, handle_answer, "Asserts answer", usage_answer },
{ { "wait", "for", "digit", NULL }, handle_waitfordigit, "Waits for a digit to be pressed", usage_waitfordigit },
{ { "send", "text", NULL }, handle_sendtext, "Sends text to channels supporting it", usage_sendtext },
{ { "receive", "char", NULL }, handle_recvchar, "Receives text from channels supporting it", usage_recvchar },
@ -648,10 +899,54 @@ agi_command commands[] = {
{ { "hangup", NULL }, handle_hangup, "Hangup the current channel", usage_hangup },
{ { "exec", NULL }, handle_exec, "Executes a given Application", usage_exec },
{ { "set", "callerid", NULL }, handle_setcallerid, "Sets callerid for the current channel", usage_setcallerid },
{ { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus }
{ { "channel", "status", NULL }, handle_channelstatus, "Returns status of the connected channel", usage_channelstatus },
{ { "set", "variable", NULL }, handle_setvariable, "Sets a channel variable", usage_setvariable },
{ { "get", "variable", NULL }, handle_getvariable, "Gets a channel variable", usage_getvariable },
{ { "verbose", NULL }, handle_verbose, "Logs a message to the asterisk verbose log", usage_verbose },
{ { "database", "get", NULL }, handle_dbget, "Gets database value", usage_dbget },
{ { "database", "put", NULL }, handle_dbput, "Adds/updates database value", usage_dbput },
{ { "database", "del", NULL }, handle_dbdel, "Removes database key/value", usage_dbdel },
{ { "database", "deltree", NULL }, handle_dbdeltree, "Removes database keytree/value", usage_dbdeltree }
};
static agi_command *find_command(char *cmds[])
static void join(char *s, int len, char *w[])
{
int x;
/* Join words into a string */
strcpy(s, "");
for (x=0;w[x];x++) {
if (x)
strncat(s, " ", len - strlen(s));
strncat(s, w[x], len - strlen(s));
}
}
static int help_workhorse(int fd, char *match[])
{
char fullcmd[80];
char matchstr[80];
int x;
struct agi_command *e;
if (match)
join(matchstr, sizeof(matchstr), match);
for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
e = &commands[x];
if (e)
join(fullcmd, sizeof(fullcmd), e->cmda);
/* Hide commands that start with '_' */
if (fullcmd[0] == '_')
continue;
if (match) {
if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
continue;
}
}
ast_cli(fd, "%20.20s %s\n", fullcmd, e->summary);
}
return 0;
}
static agi_command *find_command(char *cmds[], int exact)
{
int x;
int y;
@ -663,14 +958,14 @@ static agi_command *find_command(char *cmds[])
/* If there are no more words in the command (and we're looking for
an exact match) or there is a difference between the two words,
then this is not a match */
if (!commands[x].cmda[y])
if (!commands[x].cmda[y] && !exact)
break;
if (strcasecmp(commands[x].cmda[y], cmds[y]))
match = 0;
}
/* If more words are needed to complete the command then this is not
a candidate (unless we're looking for a really inexact answer */
if (commands[x].cmda[y])
if ((exact > -1) && commands[x].cmda[y])
match = 0;
if (match)
return &commands[x];
@ -758,7 +1053,7 @@ static int agi_handle_command(struct ast_channel *chan, int fd, char *buf)
for (x=0;x<argc;x++)
fprintf(stderr, "Got Arg%d: %s\n", x, argv[x]); }
#endif
c = find_command(argv);
c = find_command(argv, 0);
if (c) {
res = c->handler(chan, fd, argc, argv);
switch(res) {
@ -840,6 +1135,84 @@ static int run_agi(struct ast_channel *chan, char *request, int *fds, int pid)
return returnstatus;
}
static int handle_showagi(int fd, int argc, char *argv[]) {
struct agi_command *e;
char fullcmd[80];
if ((argc < 2))
return RESULT_SHOWUSAGE;
if (argc > 2) {
e = find_command(argv + 2, 1);
if (e)
ast_cli(fd, e->usage);
else {
if (find_command(argv + 2, -1)) {
return help_workhorse(fd, argv + 1);
} else {
join(fullcmd, sizeof(fullcmd), argv+1);
ast_cli(fd, "No such command '%s'.\n", fullcmd);
}
}
} else {
return help_workhorse(fd, NULL);
}
return RESULT_SUCCESS;
}
static int handle_dumpagihtml(int fd, int argc, char *argv[]) {
struct agi_command *e;
char fullcmd[80];
char *tempstr;
int x;
FILE *htmlfile;
if ((argc < 3))
return RESULT_SHOWUSAGE;
if (!(htmlfile = fopen(argv[2], "wt"))) {
ast_cli(fd, "Could not create file '%s'\n", argv[2]);
return RESULT_SHOWUSAGE;
}
fprintf(htmlfile, "<HTML>\n<HEAD>\n<TITLE>AGI Commands</TITLE>\n</HEAD>\n");
fprintf(htmlfile, "<BODY>\n<CENTER><B><H1>AGI Commands</H1></B></CENTER>\n\n");
fprintf(htmlfile, "<TABLE BORDER=\"0\" CELLSPACING=\"10\">\n");
for (x=0;x<sizeof(commands)/sizeof(commands[0]);x++) {
char *stringp=NULL;
e = &commands[x];
if (e)
join(fullcmd, sizeof(fullcmd), e->cmda);
/* Hide commands that start with '_' */
if (fullcmd[0] == '_')
continue;
fprintf(htmlfile, "<TR><TD><TABLE BORDER=\"1\" CELLPADDING=\"5\" WIDTH=\"100%%\">\n");
fprintf(htmlfile, "<TR><TH ALIGN=\"CENTER\"><B>%s - %s</B></TD></TR>\n", fullcmd,e->summary);
stringp=e->usage;
tempstr = strsep(&stringp, "\n");
fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">%s</TD></TR>\n", tempstr);
fprintf(htmlfile, "<TR><TD ALIGN=\"CENTER\">\n");
while ((tempstr = strsep(&stringp, "\n")) != NULL) {
fprintf(htmlfile, "%s<BR>\n",tempstr);
}
fprintf(htmlfile, "</TD></TR>\n");
fprintf(htmlfile, "</TABLE></TD></TR>\n\n");
}
fprintf(htmlfile, "</TABLE>\n</BODY>\n</HTML>\n");
fclose(htmlfile);
ast_cli(fd, "AGI HTML Commands Dumped to: %s\n", argv[2]);
return RESULT_SUCCESS;
}
static int agi_exec(struct ast_channel *chan, void *data)
{
int res=0;
@ -848,6 +1221,7 @@ static int agi_exec(struct ast_channel *chan, void *data)
char tmp[256];
int fds[2];
int pid;
char *stringp=tmp;
if (!data || !strlen(data)) {
ast_log(LOG_WARNING, "AGI requires an argument (script)\n");
return -1;
@ -855,9 +1229,9 @@ static int agi_exec(struct ast_channel *chan, void *data)
strncpy(tmp, data, sizeof(tmp)-1);
strtok(tmp, "|");
args = strtok(NULL, "|");
ringy = strtok(NULL,"|");
strsep(&stringp, "|");
args = strsep(&stringp, "|");
ringy = strsep(&stringp,"|");
if (!args)
args = "";
LOCAL_USER_ADD(u);
@ -885,14 +1259,35 @@ static int agi_exec(struct ast_channel *chan, void *data)
return res;
}
static char showagi_help[] =
"Usage: show agi [topic]\n"
" When called with a topic as an argument, displays usage\n"
" information on the given command. If called without a\n"
" topic, it provides a list of AGI commands.\n";
static char dumpagihtml_help[] =
"Usage: dump agihtml <filename>\n"
" Dumps the agi command list in html format to given filename\n";
struct ast_cli_entry showagi =
{ { "show", "agi", NULL }, handle_showagi, "Show AGI commands or specific help", showagi_help };
struct ast_cli_entry dumpagihtml =
{ { "dump", "agihtml", NULL }, handle_dumpagihtml, "Dumps a list of agi command in html format", dumpagihtml_help };
int unload_module(void)
{
STANDARD_HANGUP_LOCALUSERS;
ast_cli_unregister(&showagi);
ast_cli_unregister(&dumpagihtml);
return ast_unregister_application(app);
}
int load_module(void)
{
ast_cli_register(&showagi);
ast_cli_register(&dumpagihtml);
return ast_register_application(app, agi_exec, synopsis, descrip);
}

239
file.c
View File

@ -1,4 +1,4 @@
/*
/*m
* Asterisk -- A telephony toolkit for Linux.
*
* Generic File Format Support.
@ -28,6 +28,7 @@
#include <dirent.h>
#include <sys/stat.h>
#include "asterisk.h"
#include "astconf.h"
struct ast_format {
/* Name of format */
@ -43,8 +44,16 @@ struct ast_format {
struct ast_filestream * (*rewrite)(int fd, char *comment);
/* Apply a reading filestream to a channel */
int (*apply)(struct ast_channel *, struct ast_filestream *);
/* play filestream on a channel */
int (*play)(struct ast_filestream *);
/* Write a frame to a channel */
int (*write)(struct ast_filestream *, struct ast_frame *);
/* seek num samples into file, whence(think normal seek) */
int (*seek)(struct ast_filestream *, long offset, int whence);
/* trunc file to current position */
int (*trunc)(struct ast_filestream *fs);
/* tell current position */
long (*tell)(struct ast_filestream *fs);
/* Read the next frame from the filestream (if available) */
struct ast_frame * (*read)(struct ast_filestream *);
/* Close file, and destroy filestream structure */
@ -71,7 +80,11 @@ 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 (*play)(struct ast_filestream *),
int (*write)(struct ast_filestream *, struct ast_frame *),
int (*seek)(struct ast_filestream *, long sample_offset, int whence),
int (*trunc)(struct ast_filestream *),
long (*tell)(struct ast_filestream *),
struct ast_frame * (*read)(struct ast_filestream *),
void (*close)(struct ast_filestream *),
char * (*getcomment)(struct ast_filestream *))
@ -101,8 +114,12 @@ int ast_format_register(char *name, char *exts, int format,
tmp->open = open;
tmp->rewrite = rewrite;
tmp->apply = apply;
tmp->play = play;
tmp->read = read;
tmp->write = write;
tmp->seek = seek;
tmp->trunc = trunc;
tmp->tell = tell;
tmp->close = close;
tmp->format = format;
tmp->getcomment = getcomment;
@ -151,13 +168,6 @@ int ast_stopstream(struct ast_channel *tmp)
return 0;
}
int ast_closestream(struct ast_filestream *f)
{
/* Stop a running stream if there is one */
f->fmt->close(f);
return 0;
}
int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
{
struct ast_frame *trf;
@ -237,12 +247,14 @@ static int copy(char *infile, char *outfile)
static char *build_filename(char *filename, char *ext)
{
char *fn;
fn = malloc(strlen(AST_SOUNDS) + strlen(filename) + strlen(ext) + 10);
char tmp[AST_CONFIG_MAX_PATH];
snprintf((char *)tmp,sizeof(tmp)-1,"%s/%s",(char *)ast_config_AST_VAR_DIR,"sounds");
fn = malloc(strlen(tmp) + strlen(filename) + strlen(ext) + 10);
if (fn) {
if (filename[0] == '/')
sprintf(fn, "%s.%s", filename, ext);
else
sprintf(fn, "%s/%s.%s", AST_SOUNDS, filename, ext);
sprintf(fn, "%s/%s.%s", (char *)tmp, filename, ext);
}
return fn;
@ -281,9 +293,11 @@ static int ast_filehelper(char *filename, char *filename2, char *fmt, int action
f = formats;
while(f) {
if (!fmt || !strcasecmp(f->name, fmt)) {
char *stringp=NULL;
exts = strdup(f->exts);
/* Try each kind of extension */
ext = strtok(exts, "|");
stringp=exts;
ext = strsep(&stringp, "|");
do {
fn = build_filename(filename, ext);
if (fn) {
@ -327,13 +341,6 @@ static int ast_filehelper(char *filename, char *filename2, char *fmt, int action
s->fmt = f;
s->trans = NULL;
chan->stream = s;
if (f->apply(chan, s)) {
f->close(s);
chan->stream = NULL;
ast_log(LOG_WARNING, "Unable to apply stream to channel %s\n", chan->name);
close(ret);
ret = 0;
}
} else {
close(ret);
ast_log(LOG_WARNING, "Unable to open fd on %s\n", filename);
@ -351,7 +358,7 @@ static int ast_filehelper(char *filename, char *filename2, char *fmt, int action
}
free(fn);
}
ext = strtok(NULL, "|");
ext = strsep(&stringp, "|");
} while(ext);
free(exts);
}
@ -363,19 +370,145 @@ static int ast_filehelper(char *filename, char *filename2, char *fmt, int action
return res;
}
struct ast_filestream *ast_openstream(struct ast_channel *chan, char *filename, char *preflang)
{
/* This is a fairly complex routine. Essentially we should do
the following:
1) Find which file handlers produce our type of format.
2) Look for a filename which it can handle.
3) If we find one, then great.
4) If not, see what files are there
5) See what we can actually support
6) Choose the one with the least costly translator path and
set it up.
*/
int fd = -1;
int fmts = -1;
char filename2[256];
char lang2[MAX_LANGUAGE];
int res;
ast_stopstream(chan);
/* do this first, otherwise we detect the wrong writeformat */
if (chan->generator)
ast_deactivate_generator(chan);
if (preflang && strlen(preflang)) {
snprintf(filename2, sizeof(filename2), "%s-%s", filename, preflang);
fmts = ast_fileexists(filename2, NULL, NULL);
if (fmts < 1) {
strncpy(lang2, preflang, sizeof(lang2)-1);
snprintf(filename2, sizeof(filename2), "%s-%s", filename, lang2);
fmts = ast_fileexists(filename2, NULL, NULL);
}
}
if (fmts < 1) {
strncpy(filename2, filename, sizeof(filename2)-1);
fmts = ast_fileexists(filename2, NULL, NULL);
}
if (fmts < 1) {
ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
return NULL;
}
chan->oldwriteformat = chan->writeformat;
/* Set the channel to a format we can work with */
res = ast_set_write_format(chan, fmts);
fd = ast_filehelper(filename2, (char *)chan, NULL, ACTION_OPEN);
if(fd >= 0)
return chan->stream;
return NULL;
}
int ast_applystream(struct ast_channel *chan, struct ast_filestream *s)
{
if(chan->stream->fmt->apply(chan,s)){
chan->stream->fmt->close(s);
chan->stream = NULL;
ast_log(LOG_WARNING, "Unable to apply stream to channel %s\n", chan->name);
return -1;
}
return 0;
}
int ast_playstream(struct ast_filestream *s)
{
if(s->fmt->play(s)){
ast_closestream(s);
ast_log(LOG_WARNING, "Unable to start playing stream\n");
return -1;
}
return 0;
}
int ast_seekstream(struct ast_filestream *fs, long sample_offset, int whence)
{
return fs->fmt->seek(fs, sample_offset, whence);
}
int ast_truncstream(struct ast_filestream *fs)
{
return fs->fmt->trunc(fs);
}
long ast_tellstream(struct ast_filestream *fs)
{
return fs->fmt->tell(fs);
}
int ast_stream_fastforward(struct ast_filestream *fs, long ms)
{
/* I think this is right, 8000 samples per second, 1000 ms a second so 8
* samples per ms */
long samples = ms * 8;
return ast_seekstream(fs, samples, SEEK_CUR);
}
int ast_stream_rewind(struct ast_filestream *fs, long ms)
{
long samples = ms * 8;
samples = samples * -1;
return ast_seekstream(fs, samples, SEEK_CUR);
}
int ast_closestream(struct ast_filestream *f)
{
/* Stop a running stream if there is one */
f->fmt->close(f);
return 0;
}
int ast_fileexists(char *filename, char *fmt, char *preflang)
{
char filename2[256];
char tmp[256];
char *postfix;
char *prefix;
char *c;
char lang2[MAX_LANGUAGE];
int res = -1;
if (preflang && strlen(preflang)) {
snprintf(filename2, sizeof(filename2), "%s-%s", filename, preflang);
/* Insert the language between the last two parts of the path */
strncpy(tmp, filename, sizeof(tmp) - 1);
c = strrchr(tmp, '/');
if (c) {
*c = '\0';
postfix = c+1;
prefix = tmp;
} else {
postfix = tmp;
prefix="";
}
snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, preflang, postfix);
res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
if (res < 1) {
char *stringp=NULL;
strncpy(lang2, preflang, sizeof(lang2)-1);
strtok(lang2, "_");
stringp=lang2;
strsep(&stringp, "_");
if (strcmp(lang2, preflang)) {
snprintf(filename2, sizeof(filename2), "%s-%s", filename, lang2);
snprintf(filename2, sizeof(filename2), "%s/%s/%s", prefix, lang2, postfix);
res = ast_filehelper(filename2, NULL, fmt, ACTION_EXISTS);
}
}
@ -403,50 +536,17 @@ int ast_filecopy(char *filename, char *filename2, char *fmt)
int ast_streamfile(struct ast_channel *chan, char *filename, char *preflang)
{
/* This is a fairly complex routine. Essentially we should do
the following:
1) Find which file handlers produce our type of format.
2) Look for a filename which it can handle.
3) If we find one, then great.
4) If not, see what files are there
5) See what we can actually support
6) Choose the one with the least costly translator path and
set it up.
*/
int fd = -1;
int fmts = -1;
char filename2[256];
char lang2[MAX_LANGUAGE];
int res;
ast_stopstream(chan);
if (preflang && strlen(preflang)) {
snprintf(filename2, sizeof(filename2), "%s-%s", filename, preflang);
fmts = ast_fileexists(filename2, NULL, NULL);
if (fmts < 1) {
strncpy(lang2, preflang, sizeof(lang2)-1);
snprintf(filename2, sizeof(filename2), "%s-%s", filename, lang2);
fmts = ast_fileexists(filename2, NULL, NULL);
}
}
if (fmts < 1) {
strncpy(filename2, filename, sizeof(filename2)-1);
fmts = ast_fileexists(filename2, NULL, NULL);
}
if (fmts < 1) {
ast_log(LOG_WARNING, "File %s does not exist in any format\n", filename);
return -1;
}
chan->oldwriteformat = chan->writeformat;
/* Set the channel to a format we can work with */
res = ast_set_write_format(chan, fmts);
fd = ast_filehelper(filename2, (char *)chan, NULL, ACTION_OPEN);
if (fd >= 0) {
struct ast_filestream *fs;
fs = ast_openstream(chan, filename, preflang);
if(fs){
if(ast_applystream(chan, fs))
return -1;
if(ast_playstream(fs))
return -1;
#if 1
if (option_verbose > 2)
ast_verbose(VERBOSE_PREFIX_3 "Playing '%s'\n", filename2);
ast_verbose(VERBOSE_PREFIX_3 "Playing '%s'\n", filename);
#endif
return 0;
}
@ -457,7 +557,7 @@ int ast_streamfile(struct ast_channel *chan, char *filename, char *preflang)
struct ast_filestream *ast_writefile(char *filename, char *type, char *comment, int flags, int check, mode_t mode)
{
int fd;
int fd,myflags;
struct ast_format *f;
struct ast_filestream *fs=NULL;
char *fn;
@ -466,14 +566,19 @@ struct ast_filestream *ast_writefile(char *filename, char *type, char *comment,
ast_log(LOG_WARNING, "Unable to lock format list\n");
return NULL;
}
myflags = 0;
/* set the O_TRUNC flag if and only if there is no O_APPEND specified */
//if (!(flags & O_APPEND)) myflags = O_TRUNC;
f = formats;
while(f) {
if (!strcasecmp(f->name, type)) {
char *stringp=NULL;
/* XXX Implement check XXX */
ext = strdup(f->exts);
ext = strtok(ext, "|");
stringp=ext;
ext = strsep(&stringp, "|");
fn = build_filename(filename, ext);
fd = open(fn, flags | O_WRONLY | O_CREAT, mode);
fd = open(fn, flags | myflags | O_WRONLY | O_CREAT, mode);
if (fd >= 0) {
errno = 0;
if ((fs = f->rewrite(fd, comment))) {

View File

@ -150,6 +150,7 @@ static void g723_close(struct ast_filestream *s)
ast_log(LOG_WARNING, "Freeing a filestream we don't seem to own\n");
close(s->fd);
free(s);
s = NULL;
}
static int ast_read_callback(void *data)
@ -199,7 +200,7 @@ static int ast_read_callback(void *data)
else
s->fr->timelen = delay;
#else
s->fr->timelen = 30;
s->fr->samples = 240;
#endif
/* Unless there is no delay, we're going to exit out as soon as we
have processed the current frame. */
@ -229,9 +230,14 @@ static int ast_read_callback(void *data)
static int g723_apply(struct ast_channel *c, struct ast_filestream *s)
{
u_int32_t delay;
/* Select our owner for this stream, and get the ball rolling. */
s->owner = c;
return 0;
}
static int g723_play(struct ast_filestream *s)
{
u_int32_t delay;
/* Read and ignore the first delay */
if (read(s->fd, &delay, 4) != 4) {
/* Empty file */
@ -296,6 +302,21 @@ static int g723_write(struct ast_filestream *fs, struct ast_frame *f)
return 0;
}
static int g723_seek(struct ast_filestream *fs, long sample_offset, int whence)
{
return -1;
}
static int g723_trunc(struct ast_filestream *fs)
{
return -1;
}
static long g723_tell(struct ast_filestream *fs)
{
return -1;
}
static char *g723_getcomment(struct ast_filestream *s)
{
return NULL;
@ -307,7 +328,11 @@ int load_module()
g723_open,
g723_rewrite,
g723_apply,
g723_play,
g723_write,
g723_seek,
g723_trunc,
g723_tell,
g723_read,
g723_close,
g723_getcomment);

351
formats/format_g729.c Executable file
View File

@ -0,0 +1,351 @@
/*
* Asterisk -- A telephony toolkit for Linux.
*
* Save to raw, headerless G729 data.
*
* Copyright (C) 1999, Mark Spencer
*
* Mark Spencer <markster@linux-support.net>
*
* This program is free software, distributed under the terms of
* the GNU General Public License
*/
#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 <arpa/inet.h>
#include <stdlib.h>
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <endian.h>
/* Some Ideas for this code came from makeg729e.c by Jeffery Chilton */
/* Portions of the conversion code are by guido@sienanet.it */
struct ast_filestream {
void *reserved[AST_RESERVED_POINTERS];
/* Believe it or not, we must decode/recode to account for the
weird MS format */
/* This is what a filestream means to us */
int fd; /* Descriptor */
struct ast_channel *owner;
struct ast_frame fr; /* Frame information */
char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */
char empty; /* Empty character */
unsigned char g729[20]; /* Two Real G729 Frames */
int lasttimeout;
struct timeval last;
int adj;
struct ast_filestream *next;
};
static struct ast_filestream *glist = NULL;
static pthread_mutex_t g729_lock = AST_MUTEX_INITIALIZER;
static int glistcnt = 0;
static char *name = "g729";
static char *desc = "Raw G729 data";
static char *exts = "g729";
static struct ast_filestream *g729_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)))) {
memset(tmp, 0, sizeof(struct ast_filestream));
if (ast_pthread_mutex_lock(&g729_lock)) {
ast_log(LOG_WARNING, "Unable to lock g729 list\n");
free(tmp);
return NULL;
}
tmp->next = glist;
glist = tmp;
tmp->fd = fd;
tmp->owner = NULL;
tmp->fr.data = tmp->g729;
tmp->fr.frametype = AST_FRAME_VOICE;
tmp->fr.subclass = AST_FORMAT_G729A;
/* datalen will vary for each frame */
tmp->fr.src = name;
tmp->fr.mallocd = 0;
tmp->lasttimeout = -1;
glistcnt++;
ast_pthread_mutex_unlock(&g729_lock);
ast_update_use_count();
}
return tmp;
}
static struct ast_filestream *g729_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)))) {
memset(tmp, 0, sizeof(struct ast_filestream));
if (ast_pthread_mutex_lock(&g729_lock)) {
ast_log(LOG_WARNING, "Unable to lock g729 list\n");
free(tmp);
return NULL;
}
tmp->next = glist;
glist = tmp;
tmp->fd = fd;
tmp->owner = NULL;
tmp->lasttimeout = -1;
glistcnt++;
ast_pthread_mutex_unlock(&g729_lock);
ast_update_use_count();
} else
ast_log(LOG_WARNING, "Out of memory\n");
return tmp;
}
static struct ast_frame *g729_read(struct ast_filestream *s)
{
return NULL;
}
static void g729_close(struct ast_filestream *s)
{
struct ast_filestream *tmp, *tmpl = NULL;
if (ast_pthread_mutex_lock(&g729_lock)) {
ast_log(LOG_WARNING, "Unable to lock g729 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;
}
ast_pthread_mutex_unlock(&g729_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);
s = NULL;
}
static int ast_read_callback(void *data)
{
int retval = 0;
int res;
int delay = 20;
struct ast_filestream *s = data;
struct timeval tv;
/* Send a frame from the file to the appropriate channel */
s->fr.frametype = AST_FRAME_VOICE;
s->fr.subclass = AST_FORMAT_G729A;
s->fr.offset = AST_FRIENDLY_OFFSET;
s->fr.samples = 160;
s->fr.datalen = 20;
s->fr.mallocd = 0;
s->fr.data = s->g729;
if ((res = read(s->fd, s->g729, 20)) != 20) {
if (res)
ast_log(LOG_WARNING, "Short read (%d) (%s)!\n", res, strerror(errno));
s->owner->streamid = -1;
return 0;
}
/* 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;
}
if (s->last.tv_usec || s->last.tv_usec) {
int ms;
gettimeofday(&tv, NULL);
ms = 1000 * (tv.tv_sec - s->last.tv_sec) +
(tv.tv_usec - s->last.tv_usec) / 1000;
s->last.tv_sec = tv.tv_sec;
s->last.tv_usec = tv.tv_usec;
if ((ms - delay) * (ms - delay) > 4) {
/* Compensate if we're more than 2 ms off */
s->adj -= (ms - delay);
}
#if 0
fprintf(stdout, "Delay is %d, adjustment is %d, last was %d\n", delay, s->adj, ms);
#endif
delay += s->adj;
if (delay < 1)
delay = 1;
} else
gettimeofday(&s->last, NULL);
if (s->lasttimeout != delay) {
/* We'll install the next timeout now. */
s->owner->streamid = ast_sched_add(s->owner->sched,
delay, ast_read_callback, s);
s->lasttimeout = delay;
} else {
/* Just come back again at the same time */
retval = -1;
}
return retval;
}
static int g729_apply(struct ast_channel *c, struct ast_filestream *s)
{
/* Select our owner for this stream, and get the ball rolling. */
s->owner = c;
return 0;
}
static int g729_play(struct ast_filestream *s)
{
ast_read_callback(s);
return 0;
}
static int g729_write(struct ast_filestream *fs, struct ast_frame *f)
{
int res;
if (f->frametype != AST_FRAME_VOICE) {
ast_log(LOG_WARNING, "Asked to write non-voice frame!\n");
return -1;
}
if (f->subclass != AST_FORMAT_G729A) {
ast_log(LOG_WARNING, "Asked to write non-G729 frame (%d)!\n", f->subclass);
return -1;
}
if (f->datalen % 20) {
ast_log(LOG_WARNING, "Invalid data length, %d, should be multiple of 20\n", f->datalen);
return -1;
}
if ((res = write(fs->fd, f->data, f->datalen)) != f->datalen) {
ast_log(LOG_WARNING, "Bad write (%d/20): %s\n", res, strerror(errno));
return -1;
}
return 0;
}
static char *g729_getcomment(struct ast_filestream *s)
{
return NULL;
}
static int g729_seek(struct ast_filestream *fs, long sample_offset, int whence)
{
long bytes;
off_t min,cur,max,offset;
min = 0;
cur = lseek(fs->fd, 0, SEEK_CUR);
max = lseek(fs->fd, 0, SEEK_END);
bytes = 20 * (sample_offset / 160);
if(whence == SEEK_SET)
offset = bytes;
if(whence == SEEK_CUR)
offset = cur + bytes;
if(whence == SEEK_END)
offset = max - bytes;
offset = (offset > max)?max:offset;
offset = (offset < min)?min:offset;
if (lseek(fs->fd, offset, SEEK_SET) < 0)
return -1;
return 0;
}
static int g729_trunc(struct ast_filestream *fs)
{
/* Truncate file to current length */
if (ftruncate(fs->fd, lseek(fs->fd, 0, SEEK_CUR)) < 0)
return -1;
return 0;
}
static long g729_tell(struct ast_filestream *fs)
{
off_t offset;
offset = lseek(fs->fd, 0, SEEK_CUR);
return (offset/20)*160;
}
int load_module()
{
return ast_format_register(name, exts, AST_FORMAT_G729A,
g729_open,
g729_rewrite,
g729_apply,
g729_play,
g729_write,
g729_seek,
g729_trunc,
g729_tell,
g729_read,
g729_close,
g729_getcomment);
}
int unload_module()
{
struct ast_filestream *tmp, *tmpl;
if (ast_pthread_mutex_lock(&g729_lock)) {
ast_log(LOG_WARNING, "Unable to lock g729 list\n");
return -1;
}
tmp = glist;
while(tmp) {
if (tmp->owner)
ast_softhangup(tmp->owner, AST_SOFTHANGUP_APPUNLOAD);
tmpl = tmp;
tmp = tmp->next;
free(tmpl);
}
ast_pthread_mutex_unlock(&g729_lock);
return ast_format_unregister(name);
}
int usecount()
{
int res;
if (ast_pthread_mutex_lock(&g729_lock)) {
ast_log(LOG_WARNING, "Unable to lock g729 list\n");
return -1;
}
res = glistcnt;
ast_pthread_mutex_unlock(&g729_lock);
return res;
}
char *description()
{
return desc;
}
char *key()
{
return ASTERISK_GPL_KEY;
}

View File

@ -151,6 +151,7 @@ static void gsm_close(struct ast_filestream *s)
ast_log(LOG_WARNING, "Freeing a filestream we don't seem to own\n");
close(s->fd);
free(s);
s = NULL;
}
static int ast_read_callback(void *data)
@ -165,7 +166,7 @@ static int ast_read_callback(void *data)
s->fr.frametype = AST_FRAME_VOICE;
s->fr.subclass = AST_FORMAT_GSM;
s->fr.offset = AST_FRIENDLY_OFFSET;
s->fr.timelen = 20;
s->fr.samples = 160;
s->fr.datalen = 33;
s->fr.mallocd = 0;
s->fr.data = s->gsm;
@ -216,6 +217,11 @@ static int gsm_apply(struct ast_channel *c, struct ast_filestream *s)
{
/* Select our owner for this stream, and get the ball rolling. */
s->owner = c;
return 0;
}
static int gsm_play(struct ast_filestream *s)
{
ast_read_callback(s);
return 0;
}
@ -242,6 +248,38 @@ static int gsm_write(struct ast_filestream *fs, struct ast_frame *f)
return 0;
}
static int gsm_seek(struct ast_filestream *fs, long sample_offset, int whence)
{
off_t offset,min,cur,max,distance;
min = 0;
cur = lseek(fs->fd, 0, SEEK_CUR);
max = lseek(fs->fd, 0, SEEK_END);
/* have to fudge to frame here, so not fully to sample */
distance = (sample_offset/160) * 33;
if(whence == SEEK_SET)
offset = distance;
if(whence == SEEK_CUR)
offset = distance + cur;
if(whence == SEEK_END)
offset = max - distance;
offset = (offset > max)?max:offset;
offset = (offset < min)?min:offset;
return lseek(fs->fd, offset, SEEK_SET);
}
static int gsm_trunc(struct ast_filestream *fs)
{
return ftruncate(fs->fd, lseek(fs->fd,0,SEEK_CUR));
}
static long gsm_tell(struct ast_filestream *fs)
{
off_t offset;
offset = lseek(fs->fd, 0, SEEK_CUR);
return (offset/33)*160;
}
static char *gsm_getcomment(struct ast_filestream *s)
{
return NULL;
@ -253,7 +291,11 @@ int load_module()
gsm_open,
gsm_rewrite,
gsm_apply,
gsm_play,
gsm_write,
gsm_seek,
gsm_trunc,
gsm_tell,
gsm_read,
gsm_close,
gsm_getcomment);

View File

@ -149,6 +149,7 @@ static void pcm_close(struct ast_filestream *s)
ast_log(LOG_WARNING, "Freeing a filestream we don't seem to own\n");
close(s->fd);
free(s);
s = NULL;
}
static int ast_read_callback(void *data)
@ -171,9 +172,9 @@ static int ast_read_callback(void *data)
s->owner->streamid = -1;
return 0;
}
s->fr.timelen = res / 8;
s->fr.samples = res;
s->fr.datalen = res;
delay = s->fr.timelen;
delay = s->fr.samples/8;
/* Lastly, process the frame */
if (ast_write(s->owner, &s->fr)) {
ast_log(LOG_WARNING, "Failed to write frame\n");
@ -215,6 +216,11 @@ static int pcm_apply(struct ast_channel *c, struct ast_filestream *s)
{
/* Select our owner for this stream, and get the ball rolling. */
s->owner = c;
return 0;
}
static int pcm_play(struct ast_filestream *s)
{
ast_read_callback(s);
return 0;
}
@ -237,6 +243,36 @@ static int pcm_write(struct ast_filestream *fs, struct ast_frame *f)
return 0;
}
static int pcm_seek(struct ast_filestream *fs, long sample_offset, int whence)
{
off_t offset,min,cur,max;
min = 0;
cur = lseek(fs->fd, 0, SEEK_CUR);
max = lseek(fs->fd, 0, SEEK_END);
if(whence == SEEK_SET)
offset = sample_offset;
if(whence == SEEK_CUR)
offset = sample_offset + cur;
if(whence == SEEK_END)
offset = max - sample_offset;
offset = (offset > max)?max:offset;
offset = (offset < min)?min:offset;
return lseek(fs->fd, offset, SEEK_SET);
}
static int pcm_trunc(struct ast_filestream *fs)
{
return ftruncate(fs->fd, lseek(fs->fd,0,SEEK_CUR));
}
static long pcm_tell(struct ast_filestream *fs)
{
off_t offset;
offset = lseek(fs->fd, 0, SEEK_CUR);
return offset;
}
static char *pcm_getcomment(struct ast_filestream *s)
{
return NULL;
@ -248,7 +284,11 @@ int load_module()
pcm_open,
pcm_rewrite,
pcm_apply,
pcm_play,
pcm_write,
pcm_seek,
pcm_trunc,
pcm_tell,
pcm_read,
pcm_close,
pcm_getcomment);

View File

@ -259,6 +259,7 @@ static void vox_close(struct ast_filestream *s)
ast_log(LOG_WARNING, "Freeing a filestream we don't seem to own\n");
close(s->fd);
free(s);
s = NULL;
}
static int ast_read_callback(void *data)
@ -291,9 +292,9 @@ static int ast_read_callback(void *data)
s->signal += decode(s->buf[x] >> 4, &s->ssindex);
s->signal += decode(s->buf[x] & 0xf, &s->ssindex);
}
s->fr.timelen = res / 4;
s->fr.samples = res * 2;
s->fr.datalen = res + 3;
delay = s->fr.timelen;
delay = s->fr.samples / 8;
/* Lastly, process the frame */
if (ast_write(s->owner, &s->fr)) {
ast_log(LOG_WARNING, "Failed to write frame\n");
@ -335,6 +336,11 @@ static int vox_apply(struct ast_channel *c, struct ast_filestream *s)
{
/* Select our owner for this stream, and get the ball rolling. */
s->owner = c;
return 0;
}
static int vox_play(struct ast_filestream *s)
{
ast_read_callback(s);
return 0;
}
@ -366,13 +372,32 @@ static char *vox_getcomment(struct ast_filestream *s)
return NULL;
}
static int vox_seek(struct ast_filestream *fs, long sample_offset, int whence)
{
return -1;
}
static int vox_trunc(struct ast_filestream *fs)
{
return -1;
}
static long vox_tell(struct ast_filestream *fs)
{
return -1;
}
int load_module()
{
return ast_format_register(name, exts, AST_FORMAT_ADPCM,
vox_open,
vox_rewrite,
vox_apply,
vox_play,
vox_write,
vox_seek,
vox_trunc,
vox_tell,
vox_read,
vox_close,
vox_getcomment);

View File

@ -33,8 +33,6 @@
struct ast_filestream {
void *reserved[AST_RESERVED_POINTERS];
/* Believe it or not, we must decode/recode to account for the
weird MS format */
/* This is what a filestream means to us */
int fd; /* Descriptor */
int bytes;
@ -43,7 +41,7 @@ struct ast_filestream {
struct ast_frame fr; /* Frame information */
char waste[AST_FRIENDLY_OFFSET]; /* Buffer for sending frames, etc */
char empty; /* Empty character */
short buf[160]; /* Two Real GSM Frames */
short buf[160];
int foffset;
int lasttimeout;
struct timeval last;
@ -191,14 +189,20 @@ static int check_header(int fd)
return 0;
}
static int update_header(int fd, int bytes)
static int update_header(int fd)
{
int cur;
int datalen = htoll(bytes);
/* int filelen = htoll(52 + ((bytes + 1) & ~0x1)); */
int filelen = htoll(36 + bytes);
off_t cur,end;
int datalen,filelen,bytes;
cur = lseek(fd, 0, SEEK_CUR);
end = lseek(fd, 0, SEEK_END);
/* data starts 44 bytes in */
bytes = end - 44;
datalen = htoll(bytes);
/* chunk size is bytes of data plus 36 bytes of header */
filelen = htoll(36 + bytes);
if (cur < 0) {
ast_log(LOG_WARNING, "Unable to find our position\n");
return -1;
@ -237,6 +241,7 @@ static int write_header(int fd)
unsigned short bisam = htols(16);
unsigned int size = htoll(0);
/* Write a wav header, ignoring sizes which will be filled in later */
lseek(fd,0,SEEK_SET);
if (write(fd, "RIFF", 4) != 4) {
ast_log(LOG_WARNING, "Unable to write header\n");
return -1;
@ -396,6 +401,7 @@ static void wav_close(struct ast_filestream *s)
write(s->fd, &zero, 1);
close(s->fd);
free(s);
s = NULL;
#if 0
printf("bytes = %d\n", s->bytes);
#endif
@ -418,6 +424,11 @@ static int ast_read_callback(void *data)
s->owner->streamid = -1;
return 0;
}
#if __BYTE_ORDER == __BIG_ENDIAN
for( x = 0; x < sizeof(tmp)/2; x++) tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8);
#endif
if (s->needsgain) {
for (x=0;x<sizeof(tmp)/2;x++)
if (tmp[x] & ((1 << GAIN) - 1)) {
@ -434,15 +445,15 @@ static int ast_read_callback(void *data)
memcpy(s->buf, tmp, sizeof(s->buf));
}
delay = res / 16;
delay = res / 2;
s->fr.frametype = AST_FRAME_VOICE;
s->fr.subclass = AST_FORMAT_SLINEAR;
s->fr.offset = AST_FRIENDLY_OFFSET;
s->fr.datalen = res;
s->fr.data = s->buf;
s->fr.mallocd = 0;
s->fr.timelen = delay;
s->fr.samples = delay;
delay /= 8;
/* Lastly, process the frame */
if (delay != s->lasttimeout) {
s->owner->streamid = ast_sched_add(s->owner->sched, delay, ast_read_callback, s);
@ -465,6 +476,11 @@ static int wav_apply(struct ast_channel *c, struct ast_filestream *s)
{
/* Select our owner for this stream, and get the ball rolling. */
s->owner = c;
return 0;
}
static int wav_play(struct ast_filestream *s)
{
ast_read_callback(s);
return 0;
}
@ -480,7 +496,7 @@ static int wav_write(struct ast_filestream *fs, struct ast_frame *f)
return -1;
}
if (f->subclass != AST_FORMAT_SLINEAR) {
ast_log(LOG_WARNING, "Asked to write non-GSM frame (%d)!\n", f->subclass);
ast_log(LOG_WARNING, "Asked to write non-SLINEAR frame (%d)!\n", f->subclass);
return -1;
}
if (f->datalen > sizeof(tmp)) {
@ -505,6 +521,11 @@ static int wav_write(struct ast_filestream *fs, struct ast_frame *f)
tmpf = -32768.0;
tmp[x] = tmpf;
tmp[x] &= ~((1 << GAIN) - 1);
#if __BYTE_ORDER == __BIG_ENDIAN
tmp[x] = (tmp[x] << 8) | ((tmp[x] & 0xff00) >> 8);
#endif
}
if ((write (fs->fd, tmp, f->datalen) != f->datalen) ) {
ast_log(LOG_WARNING, "Bad write (%d): %s\n", res, strerror(errno));
@ -516,12 +537,47 @@ static int wav_write(struct ast_filestream *fs, struct ast_frame *f)
}
fs->bytes += f->datalen;
update_header(fs->fd, fs->bytes);
update_header(fs->fd);
return 0;
}
static int wav_seek(struct ast_filestream *fs, long sample_offset, int whence)
{
off_t min,max,cur;
long offset,samples;
samples = sample_offset * 2; /* SLINEAR is 16 bits mono, so sample_offset * 2 = bytes */
min = 44; /* wav header is 44 bytes */
cur = lseek(fs->fd, 0, SEEK_CUR);
max = lseek(fs->fd, 0, SEEK_END);
if(whence == SEEK_SET)
offset = samples + min;
if(whence == SEEK_CUR)
offset = samples + cur;
if(whence == SEEK_END)
offset = max - samples;
offset = (offset > max)?max:offset;
offset = (offset < min)?min:offset;
return lseek(fs->fd,offset,SEEK_SET);
}
static int wav_trunc(struct ast_filestream *fs)
{
if(ftruncate(fs->fd, lseek(fs->fd,0,SEEK_CUR)))
return -1;
return update_header(fs->fd);
}
static long wav_tell(struct ast_filestream *fs)
{
off_t offset;
offset = lseek(fs->fd, 0, SEEK_CUR);
/* subtract header size to get samples, then divide by 2 for 16 bit samples */
return (offset - 44)/2;
}
static char *wav_getcomment(struct ast_filestream *s)
{
return NULL;
@ -533,7 +589,11 @@ int load_module()
wav_open,
wav_rewrite,
wav_apply,
wav_play,
wav_write,
wav_seek,
wav_trunc,
wav_tell,
wav_read,
wav_close,
wav_getcomment);

View File

@ -202,12 +202,16 @@ static int check_header(int fd)
return 0;
}
static int update_header(int fd, int bytes)
static int update_header(int fd)
{
int cur;
int datalen = htoll(bytes);
int filelen = htoll(52 + ((bytes + 1) & ~0x1));
off_t cur,end,bytes;
int datalen,filelen;
cur = lseek(fd, 0, SEEK_CUR);
end = lseek(fd, 0, SEEK_END);
bytes = end - 52;
datalen = htoll(bytes);
filelen = htoll(52 + ((bytes + 1) & ~0x1));
if (cur < 0) {
ast_log(LOG_WARNING, "Unable to find our position\n");
return -1;
@ -423,6 +427,7 @@ static void wav_close(struct ast_filestream *s)
write(s->fd, &zero, 1);
close(s->fd);
free(s);
s = NULL;
}
static int ast_read_callback(void *data)
@ -438,7 +443,7 @@ static int ast_read_callback(void *data)
s->fr.frametype = AST_FRAME_VOICE;
s->fr.subclass = AST_FORMAT_GSM;
s->fr.offset = AST_FRIENDLY_OFFSET;
s->fr.timelen = 20;
s->fr.samples = 160;
s->fr.datalen = 33;
s->fr.mallocd = 0;
if (s->secondhalf) {
@ -497,6 +502,11 @@ static int wav_apply(struct ast_channel *c, struct ast_filestream *s)
{
/* Select our owner for this stream, and get the ball rolling. */
s->owner = c;
return 0;
}
static int wav_play(struct ast_filestream *s)
{
ast_read_callback(s);
return 0;
}
@ -523,7 +533,7 @@ static int wav_write(struct ast_filestream *fs, struct ast_frame *f)
return -1;
}
fs->bytes += 65;
update_header(fs->fd, fs->bytes);
update_header(fs->fd);
} else {
/* Copy the data and do nothing */
memcpy(fs->gsm, f->data + len, 33);
@ -534,6 +544,43 @@ static int wav_write(struct ast_filestream *fs, struct ast_frame *f)
return 0;
}
static int wav_seek(struct ast_filestream *fs, long sample_offset, int whence)
{
off_t offset,distance,cur,min,max;
min = 52;
cur = lseek(fs->fd, 0, SEEK_CUR);
max = lseek(fs->fd, 0, SEEK_END);
/* I'm getting sloppy here, I'm only going to go to even splits of the 2
* frames, if you want tighter cuts use format_gsm, format_pcm, or format_wav */
distance = (sample_offset/320) * 65;
if(whence == SEEK_SET)
offset = distance + min;
if(whence == SEEK_CUR)
offset = distance + cur;
if(whence == SEEK_END)
offset = max - distance;
offset = (offset < min)?min:offset;
offset = (offset > max)?max:offset;
fs->secondhalf = 0;
return lseek(fs->fd, offset, SEEK_SET);
}
static int wav_trunc(struct ast_filestream *fs)
{
if(ftruncate(fs->fd, lseek(fs->fd, 0, SEEK_CUR)))
return -1;
return update_header(fs->fd);
}
static long wav_tell(struct ast_filestream *fs)
{
off_t offset;
offset = lseek(fs->fd, 0, SEEK_CUR);
/* since this will most likely be used later in play or record, lets stick
* to that level of resolution, just even frames boundaries */
return (offset - 52)/65/320;
}
static char *wav_getcomment(struct ast_filestream *s)
{
return NULL;
@ -545,7 +592,11 @@ int load_module()
wav_open,
wav_rewrite,
wav_apply,
wav_play,
wav_write,
wav_seek,
wav_trunc,
wav_tell,
wav_read,
wav_close,
wav_getcomment);

View File

@ -40,7 +40,11 @@ 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 (*play)(struct ast_filestream *),
int (*write)(struct ast_filestream *, struct ast_frame *),
int (*seek)(struct ast_filestream *, long offset, int whence),
int (*trunc)(struct ast_filestream *),
long (*tell)(struct ast_filestream *),
struct ast_frame * (*read)(struct ast_filestream *),
void (*close)(struct ast_filestream *),
char * (*getcomment)(struct ast_filestream *));
@ -152,6 +156,85 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f);
*/
int ast_closestream(struct ast_filestream *f);
//! Opens stream for use in seeking, playing, and writing
/*!
* \param chan channel to work with
* \param filename to use
* \param preflang prefered language to use
* Returns a ast_filestream pointer if it opens the file, NULL on error
*/
struct ast_filestream *ast_openstream(struct ast_channel *chan, char *filename, char *preflang);
//! Applys a open stream to a channel.
/*!
* \param chan channel to work
* \param ast_filestream s to apply
* Returns 0 for success, -1 on failure
*/
int ast_applystream(struct ast_channel *chan, struct ast_filestream *s);
//! play a open stream on a channel.
/*!
* \param ast_filestream s to play
* Returns 0 for success, -1 on failure
*/
int ast_playstream(struct ast_filestream *s);
//! Seeks into stream
/*!
* \param ast_filestream to perform seek on
* \param sample_offset numbers of samples to seek
* \param whence SEEK_SET, SEEK_CUR, SEEK_END
* Returns 0 for success, or -1 for error
*/
int ast_seekstream(struct ast_filestream *fs, long sample_offset, int whence);
//! Trunc stream at current location
/*!
* \param ast_filestream fs
* Returns 0 for success, or -1 for error
*/
int ast_truncstream(struct ast_filestream *fs);
//! Fast forward stream ms
/*!
* \param ast_filestream fs filestream to act on
* \param ms milliseconds to move
* Returns 0 for success, or -1 for error
*/
int ast_stream_fastforward(struct ast_filestream *fs, long ms);
//! Rewind stream ms
/*!
* \param ast_filestream fs filestream to act on
* \param ms milliseconds to move
* Returns 0 for success, or -1 for error
*/
int ast_stream_rewind(struct ast_filestream *fs, long ms);
//! Fast forward stream ms
/*!
* \param ast_filestream fs filestream to act on
* \param ms milliseconds to move
* Returns 0 for success, or -1 for error
*/
int ast_stream_fastforward(struct ast_filestream *fs, long ms);
//! Rewind stream ms
/*!
* \param ast_filestream fs filestream to act on
* \param ms milliseconds to move
* Returns 0 for success, or -1 for error
*/
int ast_stream_rewind(struct ast_filestream *fs, long ms);
//! Tell where we are in a stream
/*!
* \param ast_filestream fs to act on
* Returns a long as a sample offset into stream
*/
long ast_tellstream(struct ast_filestream *fs);
#define AST_RESERVED_POINTERS 4
#if defined(__cplusplus) || defined(c_plusplus)