From e2ecedfa707d23bc11091d4694dab3b4f2b00a64 Mon Sep 17 00:00:00 2001 From: Mark Spencer <markster@digium.com> Date: Tue, 17 Feb 2004 07:03:14 +0000 Subject: [PATCH] Add Icecast streaming support git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@2185 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- CHANGES | 2 + apps/Makefile | 2 +- apps/app_ices.c | 197 ++++++++++++++++++++++++++++++++++++++ contrib/asterisk-ices.xml | 93 ++++++++++++++++++ doc/README.ices | 12 +++ 5 files changed, 305 insertions(+), 1 deletion(-) create mode 100755 apps/app_ices.c create mode 100755 contrib/asterisk-ices.xml create mode 100755 doc/README.ices diff --git a/CHANGES b/CHANGES index d83f7cc42b..62bc5d9da2 100755 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,5 @@ + -- Add ices/icecast support + -- Numerous bug fixes Asterisk 0.7.2 -- Countless small bug fixes from bug tracker -- DSP Fixes diff --git a/apps/Makefile b/apps/Makefile index 0fcdd76364..25bd566943 100755 --- a/apps/Makefile +++ b/apps/Makefile @@ -25,7 +25,7 @@ APPS=app_dial.so app_playback.so app_voicemail.so app_directory.so app_mp3.so\ app_waitforring.so app_privacy.so app_db.so app_chanisavail.so \ app_enumlookup.so app_transfer.so app_setcidnum.so app_cdr.so \ app_hasnewvoicemail.so app_sayunixtime.so app_cut.so app_read.so \ - app_setcdruserfield.so app_random.so + app_setcdruserfield.so app_random.so app_ices.so ifneq (${OSARCH},Darwin) APPS+=app_intercom.so diff --git a/apps/app_ices.c b/apps/app_ices.c new file mode 100755 index 0000000000..294ecc6047 --- /dev/null +++ b/apps/app_ices.c @@ -0,0 +1,197 @@ +/* + * Asterisk -- A telephony toolkit for Linux. + * + * Stream to an icecast server via ICES (see contrib/asterisk-ices.xml) + * + * 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/file.h> +#include <asterisk/logger.h> +#include <asterisk/channel.h> +#include <asterisk/frame.h> +#include <asterisk/pbx.h> +#include <asterisk/module.h> +#include <asterisk/translate.h> +#include <string.h> +#include <stdio.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <pthread.h> +#include <sys/time.h> +#include <errno.h> +#include "../astconf.h" + +#define ICES "/usr/bin/ices" +#define LOCAL_ICES "/usr/local/bin/ices" + +static char *tdesc = "Encode and Stream via icecast and ices"; + +static char *app = "ICES"; + +static char *synopsis = "Encode and stream using 'ices'"; + +static char *descrip = +" ICES(config.xml) Streams to an icecast server using ices\n" +"(available separately). A configuration file must be supplied\n" +"for ices (see examples/asterisk-ices.conf). Returns -1 on\n" +"hangup or 0 otherwise.\n"; + +STANDARD_LOCAL_USER; + +LOCAL_USER_DECL; + +static int icesencode(char *filename, int fd) +{ + int res; + int x; + res = fork(); + if (res < 0) + ast_log(LOG_WARNING, "Fork failed\n"); + if (res) + return res; + dup2(fd, STDIN_FILENO); + for (x=STDERR_FILENO + 1;x<256;x++) { + if ((x != STDIN_FILENO) && (x != STDOUT_FILENO)) + close(x); + } + /* Most commonly installed in /usr/local/bin */ + execl(ICES, "ices", filename, (char *)NULL); + /* But many places has it in /usr/bin */ + execl(LOCAL_ICES, "ices", filename, (char *)NULL); + /* As a last-ditch effort, try to use PATH */ + execlp("ices", "ices", filename, (char *)NULL); + ast_log(LOG_WARNING, "Execute of ices failed\n"); + return -1; +} + +static int ices_exec(struct ast_channel *chan, void *data) +{ + int res=0; + struct localuser *u; + int fds[2]; + int ms = -1; + int pid = -1; + int flags; + int oreadformat; + struct timeval last; + struct ast_frame *f; + char filename[256]=""; + char *c; + last.tv_usec = 0; + last.tv_sec = 0; + if (!data || !strlen(data)) { + ast_log(LOG_WARNING, "ICES requires an argument (configfile.xml)\n"); + return -1; + } + if (pipe(fds)) { + ast_log(LOG_WARNING, "Unable to create pipe\n"); + return -1; + } + flags = fcntl(fds[1], F_GETFL); + fcntl(fds[1], F_SETFL, flags | O_NONBLOCK); + + LOCAL_USER_ADD(u); + ast_stopstream(chan); + + if (chan->_state != AST_STATE_UP) + res = ast_answer(chan); + + if (res) { + close(fds[0]); + close(fds[1]); + ast_log(LOG_WARNING, "Answer failed!\n"); + return -1; + } + + oreadformat = chan->readformat; + res = ast_set_read_format(chan, AST_FORMAT_SLINEAR); + if (res < 0) { + close(fds[0]); + close(fds[1]); + ast_log(LOG_WARNING, "Unable to set write format to signed linear\n"); + return -1; + } + if (((char *)data)[0] == '/') + strncpy(filename, (char *)data, sizeof(filename) - 1); + else + snprintf(filename, sizeof(filename), "%s/%s", (char *)ast_config_AST_CONFIG_DIR, (char *)data); + /* Placeholder for options */ + c = strchr(filename, '|'); + if (c) + *c = '\0'; + res = icesencode(filename, fds[0]); + close(fds[0]); + if (res >= 0) { + pid = res; + for (;;) { + /* Wait for audio, and stream */ + ms = ast_waitfor(chan, -1); + if (ms < 0) { + ast_log(LOG_DEBUG, "Hangup detected\n"); + res = -1; + break; + } + f = ast_read(chan); + if (!f) { + ast_log(LOG_DEBUG, "Null frame == hangup() detected\n"); + res = -1; + break; + } + if (f->frametype == AST_FRAME_VOICE) { + res = write(fds[1], f->data, f->datalen); + if (res < 0) { + if (errno != EAGAIN) { + ast_log(LOG_WARNING, "Write failed to pipe: %s\n", strerror(errno)); + res = -1; + break; + } + } + } + ast_frfree(f); + } + } + close(fds[1]); + LOCAL_USER_REMOVE(u); + if (pid > -1) + kill(pid, SIGKILL); + if (!res && oreadformat) + ast_set_read_format(chan, oreadformat); + return res; +} + +int unload_module(void) +{ + STANDARD_HANGUP_LOCALUSERS; + return ast_unregister_application(app); +} + +int load_module(void) +{ + return ast_register_application(app, ices_exec, synopsis, descrip); +} + +char *description(void) +{ + return tdesc; +} + +int usecount(void) +{ + int res; + STANDARD_USECOUNT(res); + return res; +} + +char *key() +{ + return ASTERISK_GPL_KEY; +} diff --git a/contrib/asterisk-ices.xml b/contrib/asterisk-ices.xml new file mode 100755 index 0000000000..abc028c752 --- /dev/null +++ b/contrib/asterisk-ices.xml @@ -0,0 +1,93 @@ +<?xml version="1.0"?> +<ices> + + <!-- run in background --> + <background>0</background> + <!-- where logs go. --> + <logpath>/var/log/ices</logpath> + <logfile>ices.log</logfile> + <!-- 1=error, 2=warn, 3=infoa ,4=debug --> + <loglevel>4</loglevel> + <!-- logfile is ignored if this is set to 1 --> + <consolelog>0</consolelog> + + <!-- optional filename to write process id to --> + <!-- <pidfile>/home/ices/ices.pid</pidfile> --> + + <stream> + <!-- metadata used for stream listing --> + <metadata> + <name>Example stream name</name> + <genre>Example genre</genre> + <description>A short description of your stream</description> + <url>http://mysite.org</url> + </metadata> + + <!-- Input module. + + This example uses the 'oss' module. It takes input from the + OSS audio device (e.g. line-in), and processes it for live + encoding. --> + <input> + <module>stdinpcm</module> + <param name="rate">8000</param> + <param name="channels">1</param> + <!-- Read metadata (from stdin by default, or --> + <!-- filename defined below (if the latter, only on SIGUSR1) --> + <param name="metadata">1</param> + <param name="metadatafilename">test</param> + </input> + + <!-- Stream instance. + + You may have one or more instances here. This allows you to + send the same input data to one or more servers (or to different + mountpoints on the same server). Each of them can have different + parameters. This is primarily useful for a) relaying to multiple + independent servers, and b) encoding/reencoding to multiple + bitrates. + + If one instance fails (for example, the associated server goes + down, etc), the others will continue to function correctly. + This example defines a single instance doing live encoding at + low bitrate. --> + + <instance> + <!-- Server details. + + You define hostname and port for the server here, along + with the source password and mountpoint. --> + + <hostname>localhost</hostname> + <port>8000</port> + <password>temppass</password> + <mount>/example.ogg</mount> + <yp>1</yp> <!-- allow stream to be advertised on YP, default 0 --> + + <!-- Live encoding/reencoding: + + channels and samplerate currently MUST match the channels + and samplerate given in the parameters to the oss input + module above or the remsaple/downmix section below. --> + + <encode> + <quality>0</quality> + <samplerate>8000</samplerate> + <channels>1</channels> + </encode> + + <!-- stereo->mono downmixing, enabled by setting this to 1 --> + <downmix>0</downmix> + + <!-- resampling. + + Set to the frequency (in Hz) you wish to resample to, --> + + <!-- <resample> + <in-rate>44100</in-rate> + <out-rate>22050</out-rate> + </resample> --> + </instance> + + </stream> +</ices> diff --git a/doc/README.ices b/doc/README.ices new file mode 100755 index 0000000000..d752363578 --- /dev/null +++ b/doc/README.ices @@ -0,0 +1,12 @@ +Icecast + Asterisk +================== +The advent of icecast into Asterisk allows you to do neat things like have +a caller stream right into an ice-cast stream as well as using chan_local +to place things like conferences, music on hold, etc. into the stream. + +You'll need to specify a config file for the ices encoder. An example is +included in contrib/asterisk-ices.xml + +Anyway hope you like it. + +Mark -- GitLab