server_command_lists = server_command_list.man afs_command_list.man
man/man1/para_server.1: para_server $(server_command_lists)
mkdir -p man/man1
- opts="-N `for i in $(server_command_lists); do echo "-i $$i"; done`"; \
+ opts="-h --detailed-help -N `for i in $(server_command_lists); do echo "-i $$i"; done`"; \
help2man $$opts ./para_server > $@
man/man1/para_audiod.1: para_audiod audiod_command_list.man
A small stand-alone program that prints tech info about the given
audio file to stdout. It can be instructed to print a "chunk table",
-an array of offsets within the audio file. This allows third-party
-streaming software that is unaware of the particular audio format
-to send complete frames in real time. Currently, mp3, ogg vorbis and
-aac are supported.
+an array of offsets within the audio file or to write the content of
+the audio file in complete chunks 'just in time'.
+
+This allows third-party streaming software that is unaware of
+the particular audio format to send complete frames in real
+time. Currently, mp3, ogg vorbis and aac are supported.
----------
para_write
--- /dev/null
+/*
+ * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file afh.c Paraslash's standalone audio format handler tool. */
+
+#include <dirent.h>
+#include <sys/time.h>
+
+#include "para.h"
+#include "string.h"
+#include "afh.cmdline.h"
+#include "fd.h"
+#include "afh.h"
+#include "error.h"
+
+static struct afh_args_info conf;
+/** The list of all status items */
+const char *status_item_list[] = {STATUS_ITEM_ARRAY};
+
+INIT_AFH_ERRLISTS;
+INIT_STDERR_LOGGING(conf.loglevel_arg)
+
+static void print_info(int audio_format_num, struct afh_info *afhi)
+{
+ printf("%s: %dkbit/s\n" /* bitrate */
+ "%s: %s\n" /* format */
+ "%s: %dHz\n" /* frequency */
+ "%s: %d\n" /* channels */
+ "%s: %lu\n" /* seconds total */
+ "%s" /* tag info */
+ "%s: %lu: %lu\n" /* chunk time */
+ "%s: %lu\n", /* num chunks */
+ status_item_list[SI_BITRATE], afhi->bitrate,
+ status_item_list[SI_FORMAT], audio_format_name(audio_format_num),
+ status_item_list[SI_FREQUENCY], afhi->frequency,
+ status_item_list[SI_CHANNELS], afhi->channels,
+ status_item_list[SI_SECONDS_TOTAL], afhi->seconds_total,
+ afhi->info_string,
+ status_item_list[SI_CHUNK_TIME], (long unsigned)afhi->chunk_tv.tv_sec,
+ (long unsigned)afhi->chunk_tv.tv_usec,
+ status_item_list[SI_NUM_CHUNKS], afhi->chunks_total
+ );
+}
+
+static void print_chunk_table(struct afh_info *afhi)
+{
+ int i;
+
+ printf("chunk_table: ");
+ for (i = 0; i <= afhi->chunks_total; i++)
+ printf("%u ", afhi->chunk_table[i]);
+ printf("\n");
+}
+
+static int cat_file(void *audio_file_data, struct afh_info *afhi)
+{
+ int ret;
+ struct timeval stream_start;
+ long unsigned i, first_chunk, last_chunk;
+ char *buf;
+ size_t size;
+
+
+ if (conf.begin_chunk_arg < 0)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ first_chunk = conf.begin_chunk_arg;
+
+ if (conf.end_chunk_given) {
+ if (conf.end_chunk_arg < 0)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ if (conf.end_chunk_arg >= afhi->chunks_total)
+ return -ERRNO_TO_PARA_ERROR(EINVAL);
+ last_chunk = conf.end_chunk_arg;
+ } else
+ last_chunk = afhi->chunks_total - 1;
+ if (!afhi->chunks_total)
+ return 1;
+ afh_get_header(afhi, audio_file_data, &buf, &size);
+ if (size && first_chunk && !conf.no_header_given) {
+ PARA_INFO_LOG("writing audio file header (%zu bytes)\n", size);
+ ret = write(STDOUT_FILENO, buf, size);
+ if (ret < 0)
+ return ret;
+ if (ret != size)
+ return -E_AFH_SHORT_WRITE;
+ }
+ PARA_NOTICE_LOG("writing chunks %lu - %lu\n", first_chunk, last_chunk);
+ gettimeofday(&stream_start, NULL);
+ for (i = first_chunk; i <= last_chunk; i++) {
+ struct timeval now, diff, next_chunk;
+ afh_get_chunk(i, afhi, audio_file_data, &buf, &size);
+ PARA_DEBUG_LOG("chunk %lu: size %zu\n", i, size);
+ if (conf.just_in_time_given) {
+ compute_chunk_time(i - first_chunk, &afhi->chunk_tv,
+ &stream_start, &next_chunk);
+ gettimeofday(&now, NULL);
+ ret = tv_diff(&next_chunk, &now, &diff);
+ if (ret > 0) {
+ ret = para_select(1, NULL, NULL, &diff);
+ if (ret < 0)
+ return ret;
+ }
+ }
+ PARA_INFO_LOG("writing chunk %lu\n", i);
+ ret = write_all(STDOUT_FILENO, buf, &size);
+ if (ret < 0)
+ return ret;
+ }
+ return 1;
+}
+
+int main(int argc, char **argv)
+{
+ int ret, audio_format_num;
+ void *audio_file_data;
+ size_t audio_file_size;
+ struct afh_info afhi;
+
+ afh_cmdline_parser(argc, argv, &conf);
+ HANDLE_VERSION_FLAG("afh", conf);
+ ret = -E_AFH_SYNTAX;
+ if (conf.inputs_num != 1)
+ goto out;
+ afh_init();
+ ret = mmap_full_file(conf.inputs[0], O_RDONLY, &audio_file_data,
+ &audio_file_size, NULL);
+ if (ret < 0)
+ goto out;
+ ret = compute_afhi(conf.inputs[0], audio_file_data, audio_file_size, &afhi);
+ if (ret < 0)
+ goto out;
+ audio_format_num = ret;
+ if (conf.stream_given)
+ ret = cat_file(audio_file_data, &afhi);
+ else {
+ print_info(audio_format_num, &afhi);
+ if (conf.chunk_table_given)
+ print_chunk_table(&afhi);
+ ret = 1;
+ }
+out:
+ if (ret < 0)
+ PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ return ret < 0? EXIT_FAILURE : EXIT_SUCCESS;
+}
--- /dev/null
+text "
+para_afh, the audio format handler tool, is a stand-alone program
+contained in the paraslash package for analyzing and streaming audio
+files. It can be used to
+
+ - print tech info about the given audio file to stdout.
+ In particular, the 'chunk table' of the audio file, an array
+ of offsets within the audio file, may be printed. This table
+ can be used by other programs unaware of the particular audio
+ format to stream the audio file.
+
+ - write selected parts of the given audio file in complete
+ chunks without decoding. Thus para_afh can be used to 'cut'
+ an audio file.
+
+ - write selected parts of the given audio files 'just in time'
+ to sdout. This may be useful for third-party software that
+ is capable of reading from stdin.
+"
+
+option "loglevel" l
+#~~~~~~~~~~~~~~~~~~
+"set loglevel (0-6)"
+int typestr="level"
+default="4"
+optional
+
+defgroup "mode"
+#--------------
+groupdesc="
+ There are two modes of operation: Info mode and stream mode,
+ one of which must be selected by the corresponding option.
+ See below.
+"
+required
+
+groupoption "info" i
+#~~~~~~~~~~~~~~~~~~~
+"select info mode"
+group="mode"
+details="
+ In this mode, the program prints technical information about
+ the given audio file to stdout.
+"
+
+groupoption "stream" s
+#~~~~~~~~~~~~~~~~~~~~~
+"select stream mode"
+group="mode"
+details="
+ If this mode is selected, the selected parts of the content
+ of the audio file are written to stdout. Only complete chunks
+ with respect of the underlying audio format are written.
+ For example, only complete frames in case of mp3 files.
+"
+
+section "Options for info mode"
+#==============================
+
+option "chunk_table" c
+#~~~~~~~~~~~~~~~~~~~~~
+"print also the chunk table"
+flag off
+dependon="info"
+
+section "Options for stream mode"
+#================================
+
+
+option "begin_chunk" b
+#~~~~~~~~~~~~~~~~~~~~~
+"skip a number of chunks"
+int typestr="chunk_num"
+default="0"
+dependon="stream"
+optional
+details="
+ The chunk_num argument must be non-negative as chunks start
+ at zero.
+"
+
+option "end_chunk" e
+#~~~~~~~~~~~~~~~~~~~
+"only write up to chunk chunk_num"
+int typestr="chunk_num"
+dependon="stream"
+optional
+details="
+ The chunk_num argument must be less than the total number of
+ chunks. The default is to write up to the last chunk.
+"
+
+option "just_in_time" j
+#~~~~~~~~~~~~~~~~~~~~~~
+"use timed writes"
+flag off
+dependon="stream"
+details="
+ Write the specified chunks of data 'just in time', i.e. the
+ write of each chunk is delayed until the time it is needed
+ by the decoder/player in order to guarantee an uninterupted
+ audio stream.
+"
+
+option "no_header" H
+#~~~~~~~~~~~~~~~~~~~
+"do not write an audio file header"
+flag off
+dependon="stream"
+details="
+ If an audio format needs information about the audio file
+ in a format-specific header in order to be understood by
+ the decoding software, a suitable header is automatically
+ send. This option changes the default behaviour so that no
+ header is sent.
+"
int compute_afhi(const char *path, char *data, size_t size,
struct afh_info *afhi);
const char *audio_format_name(int);
+void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
+ void *map, char **buf, size_t *len);
+void afh_get_header(struct afh_info *afhi, void *map, char **buf, size_t *len);
return i >= 0? afl[i].name : "(none)";
}
+
+void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
+ void *map, char **buf, size_t *len)
+{
+ size_t pos = afhi->chunk_table[chunk_num];
+ *buf = map + pos;
+ *len = afhi->chunk_table[chunk_num + 1] - pos;
+}
+
+/**
+ * Get the header of an audio file.
+ *
+ * \param afhi The audio file handler data describing the file.
+ * \param map The data of the audio file.
+ * \param buf The length of the header is stored here.
+ * \param len Points to a buffer containing the header on return.
+ *
+ * This function sets \a buf to \p NULL and \a len to zero if \a map or \a
+ * afhi is \p NULL, or if the current audio format does not need special
+ * header treamtment.
+ */
+void afh_get_header(struct afh_info *afhi, void *map, char **buf, size_t *len)
+{
+ if (!map || !afhi || ! afhi->header_len) {
+ *buf = NULL;
+ *len = 0;
+ }
+ *len = afhi->header_len;
+ *buf = map + afhi->header_offset;
+}
if (ret < 0)
return ret;
} else
- buf = vss_get_header(&len);
+ vss_get_header(&buf, &len);
if (cq->num_pending + len > cq->max_pending)
return -E_QUEUE;
qc = para_malloc(sizeof(struct queued_chunk));
if (ret < 0)
return ret;
} else
- *buf = vss_get_header(len);
+ vss_get_header(buf, len);
assert(*len > qc->sent);
*buf += qc->sent;
*len -= qc->sent;
#define AFH_ERRORS \
PARA_ERROR(AFH_SYNTAX, "afh syntax error"), \
+ PARA_ERROR(AFH_SHORT_WRITE, "afh short write"), \
#define AFH_COMMON_ERRORS \
#define SIGNAL_ERRORS \
PARA_ERROR(SIGNAL_SIG_ERR, "signal() returned SIG_ERR"), \
- PARA_ERROR(SIGNAL_READ, "read error from signal pipe"), \
#define STRING_ERRORS \
#include "para.h"
#include "error.h"
+/*
+ * Write a buffer to a file descriptor, re-write on short writes.
+ *
+ * \param fd The file descriptor.
+ * \param buf The buffer to be sent.
+ * \param len The length of \a buf.
+ *
+ * \return Standard. In any case, the number of bytes that have been written is
+ * stored in \a len.
+ */
+int write_all(int fd, const char *buf, size_t *len)
+{
+ size_t total = *len;
+
+ assert(total);
+ *len = 0;
+ while (*len < total) {
+ int ret = write(fd, buf + *len, total - *len);
+ if (ret == -1)
+ return -ERRNO_TO_PARA_ERROR(errno);
+ *len += ret;
+ }
+ return 1;
+}
+
/**
* Check whether a file exists.
*
/** \file fd.h exported symbols from fd.c */
+int write_all(int fd, const char *buf, size_t *len);
int file_exists(const char *);
int para_select(int n, fd_set *readfds, fd_set *writefds,
struct timeval *timeout_tv);
#define AI_ADDRCONFIG 0
#endif
+#include <dirent.h>
#include "para.h"
#include "error.h"
#include "net.h"
#include "string.h"
+#include "fd.h"
/** Information about one encrypted connection. */
return ia;
}
-/*
- * Send out a buffer, resend on short writes.
- *
- * \param fd The file descriptor.
- * \param buf The buffer to be sent.
- * \param len The length of \a buf.
- *
- * \return Standard. In any case, the number of bytes actually sent is stored
- * in \a len.
- */
-static int sendall(int fd, const char *buf, size_t *len)
-{
- size_t total = *len;
-
- assert(total);
- *len = 0;
- while (*len < total) {
- int ret = write(fd, buf + *len, total - *len);
- if (ret == -1)
- return -ERRNO_TO_PARA_ERROR(errno);
- *len += ret;
- }
- return 1;
-}
-
/**
* Encrypt and send a binary buffer.
*
/* RC4 may write more than len to the output buffer */
unsigned char *outbuf = para_malloc(ROUND_UP(len, 8));
(*cf)(len, (unsigned char *)buf, outbuf, private);
- ret = sendall(fd, (char *)outbuf, &len);
+ ret = write_all(fd, (char *)outbuf, &len);
free(outbuf);
} else
- ret = sendall(fd, buf, &len);
+ ret = write_all(fd, buf, &len);
return ret;
}
}
if (list_empty(&targets))
return;
- header_buf = vss_get_header(&header_len);
+ vss_get_header(&header_buf, &header_len);
if (!need_extra_header(current_chunk))
header_len = 0;
sendbuf_len = ORTP_AUDIO_HEADER_LEN + header_len + len;
const long b, const struct timeval *tv2,
struct timeval *result);
void ms2tv(const long unsigned n, struct timeval *tv);
+void compute_chunk_time(long unsigned chunk_num,
+ struct timeval *chunk_tv, struct timeval *stream_start,
+ struct timeval *result);
/** The enum of all status items. */
enum status_items {STATUS_ITEM_ENUM NUM_STAT_ITEMS};
if (!sc->header_sent && current_chunk) {
size_t header_len;
- char *header_buf = vss_get_header(&header_len);
+ char *header_buf;
+
+ vss_get_header(&header_buf, &header_len);
if (header_buf && header_len > 0) {
ret = queue_chunk_or_shutdown(sc, ss, -1U, 0);
if (ret < 0)
server_cmdline_parser_config_file(cf, &conf, ¶ms);
conf.daemon_given = tmp;
}
- /* logfile */
- if (!conf.logfile_given && conf.daemon_given) {
- ret = -1;
- PARA_EMERG_LOG("fatal: daemon option, but no log file given\n");
- goto out;
- }
if (conf.logfile_given)
logfile = open_log(conf.logfile_arg);
ret = 1;
+#########################
section "General options"
-#~~~~~~~~~~~~~~~~~~~~~~~~
+#########################
option "loglevel" l
#~~~~~~~~~~~~~~~~~~
-
"set loglevel (0-6)"
-
- int typestr="level"
- default="4"
- optional
+int typestr="level"
+default="4"
+optional
option "port" p
#~~~~~~~~~~~~~~
-
"listening port"
-
- int typestr="portnumber"
- default="2990"
- optional
+int typestr="portnumber"
+default="2990"
+optional
+details="
+ para_server listens on this tcp port for incoming connections
+ from clients such as para_client. If the default port is
+ changed, the corresponding option of para_client must be used
+ to connect to para_server.
+"
option "daemon" d
#~~~~~~~~~~~~~~~~
-
"run as background daemon"
-
- flag off
+flag off
+dependon="logfile"
+details="
+ Note that para_server refuses to start in daemon mode if no
+ logfile was specified.
+"
option "user" u
#~~~~~~~~~~~~~~
-
-"run as user 'name'. para_server does not
-need any special privileges. If started as
-root (EUID == 0) this option must be given at
-the command line (not in the configuration
-file) so that para_server can drop the root
-privileges right after parsing the command
-line options, but before parsing the
-configuration file. In this case,
-real/effective/saved UID are all set to the
-UID of 'name'. As the configuration file
-is read afterwards, those options that have a
-default value depending on the UID (e.g. the
-directory for the configuration file) are
-computed by using the uid of 'name'.
-This option has no effect if para_server is
-started as a non-root user (i.e. EUID != 0)"
-
- string typestr="name"
- optional
+"run as the given user"
+string typestr="name"
+optional
+details="
+ para_server does not need any special privileges. If started
+ as root (EUID == 0) this option must be given at the command
+ line (not in the configuration file) so that para_server
+ can drop the root privileges right after parsing the command
+ line options, but before parsing the configuration file. In
+ this case, real/effective/saved UID are all set to the UID
+ of 'name'. As the configuration file is read afterwards,
+ those options that have a default value depending on the UID
+ (e.g. the directory for the configuration file) are computed
+ by using the uid of 'name'. This option has no effect if
+ para_server is started as a non-root user (i.e. EUID != 0)
+"
option "group" g
#~~~~~~~~~~~~~~~
+"set group id"
+string typestr="group"
+optional
+details="
+ This option sets the group id according to 'group'. This option
+ is silently ignored if EUID != 0. Otherwise, real/effective
+ GID and the saved set-group ID are all set to the GID given by
+ 'group'. Must not be given in the config file.
+"
-"set group id to according to 'group'. This
-option is silently ignored if EUID != 0.
-Otherwise, real/effective GID and the saved
-set-group ID are all set to the GID given by
-'group'. Must not be given in the config file."
-
- string typestr="group"
- optional
-
-
-
+#############################
section "Configuration files"
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#############################
option "logfile" L
#~~~~~~~~~~~~~~~~~
-
-"(default=stdout/stderr)"
-
- string typestr="filename"
- optional
+"where to write log output"
+string typestr="filename"
+optional
+details="
+ If this option is not given, para_server writes the log
+ messages to to stderr
+"
option "config_file" c
#~~~~~~~~~~~~~~~~~~~~~
-
"(default='~/.paraslash/server.conf'"
-
- string typestr="filename"
- optional
+string typestr="filename"
+optional
+details="
+ para_server reads its config file right after parsing
+ the options that were given at the command line. If an
+ option is given both at the command line and in the
+ config file, the value that was specified at the command line
+ takes precedence.
+"
option "user_list" -
#~~~~~~~~~~~~~~~~~~~
-
"(default='~/.paraslash/server.users')"
- string typestr="filename"
- optional
-
+string typestr="filename"
+optional
+##################################
section "virtual streaming system"
-#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+##################################
option "autoplay" a
#~~~~~~~~~~~~~~~~~~
-
"start playing on startup"
-
- flag off
+flag off
option "autoplay_delay" -
#~~~~~~~~~~~~~~~~~~~~~~~~
-"Time to wait before autoplay starts. Ignored if
-autoplay is off."
- int typestr="milliseconds"
- default="0"
- optional
-
-
+"time to wait before streaming"
+int typestr="ms"
+default="0"
+optional
+dependon="autoplay"
+details="
+ If para_server is started with the autoplay option, this option
+ may be used to set up a delay before para_server streams its
+ first audio file. This is useful for example if para_server
+ and para_audiod are started during system startup. The delay
+ time should be choosen large enough so that para_audiod is
+ already up when para_server starts to stream. Of course, this
+ option depends on the autoplay option.
+"
option "announce_time" A
#~~~~~~~~~~~~~~~~~~~~~~~
-
-"Delay betweeen announcing the stream and sending data"
-
- int typestr="milliseconds"
- default="300"
- optional
-
+"grace time for clients"
+
+int typestr="ms"
+default="300"
+optional
+details="
+ Clients such as para_audiod connect to para_server and execute
+ the stat command to find out whether an audio stream is
+ currently available. This sets the delay betweeen announcing
+ the stream via the output of the stat command and sending
+ the first chunk of data.
+"
#############################
section "audio file selector"
option "afs_database_dir" D
#~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-"Directory containing the osl database of the
-audio file selector.
-(default='~/.paraslash/afs_database'"
-
- string typestr="path"
- optional
-
+"location of the database"
+string typestr="path"
+optional
+details="
+ Where para_server should look for the osl
+ database of the audio file selector. The default is
+ '~/.paraslash/afs_database'.
+"
option "afs_socket" s
#~~~~~~~~~~~~~~~~~~~~
-
-"Command socket for the audio file selector"
-
- string typestr="path"
- default="/var/paraslash/afs_command_socket"
- optional
-
+"Command socket for afs"
+string typestr="path"
+default="/var/paraslash/afs_command_socket"
+optional
+details="
+ For each server command that is handled by the audio file
+ selector, the child process of para_server connects to the
+ audio file selector via a local socket. This option specifies
+ the location of that socket in the file system.
+"
option "afs_initial_mode" i
#~~~~~~~~~~~~~~~~~~~~~~~~~~
-"Mood or playlist to load on startup. Must be
-prefixed with either 'p/' or 'm/' to indicate
-whether a playlist or a mood should be
-loaded. Example:
- --afs_initial_mode p/foo
-loads the playlist named 'foo'."
-
- string typestr="<specifier>/<name>"
- optional
+"Mood or playlist to load on startup."
+string typestr="<specifier>/<name>"
+optional
+details="
+ The argument of this option must be prefixed with either 'p/'
+ or 'm/' to indicate whether a playlist or a mood should be
+ loaded. Example:
+ --afs_initial_mode p/foo
+ loads the playlist named 'foo'.
+"
+#####################
section "http sender"
-#~~~~~~~~~~~~~~~~~~~~
+#####################
option "http_port" -
#~~~~~~~~~~~~~~~~~~~
-
"tcp port for http streaming"
-
- int typestr="portnumber"
- default="8000"
- optional
+int typestr="portnumber"
+default="8000"
+optional
+details="
+ The http sender of para_server listens on this port for
+ incoming connections. Clients are expected to send the usual
+ http request message such as 'GET / HTTP/'.
+"
option "http_default_deny" -
#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-"deny connections from hosts which are not
-explicitly allowed"
-
- flag off
+"make the http ACL a whitelist"
+flag off
+details="
+ The default is to use blacklists instead, i.e. connections
+ to the http sender are allowed unless the connecting host
+ matches a pattern given by a http_access option. This allows
+ to use access control the other way round: Connections are
+ denied from hosts which are not explicitly allowed by one or
+ more http_access options.
+"
option "http_access" -
#~~~~~~~~~~~~~~~~~~~~~
-
-"Add given host/network to access control
-list (whitelist if http_default_deny was
-given, blacklist otherwise) before opening
-the tcp port. This option can be given
-multiple times. Example: '192.168.0.0/24'
-whitelists/blacklists the 256 hosts
-192.168.0.x"
-
- string typestr="a.b.c.d/n"
- optional
- multiple
+"add an entry to the http ACL"
+string typestr="a.b.c.d/n"
+optional
+multiple
+details="
+ Add given host/network to access control list (whitelist if
+ http_default_deny was given, blacklist otherwise) before
+ opening the tcp port. This option can be given multiple
+ times. Example: '192.168.0.0/24' whitelists/blacklists the
+ 256 hosts 192.168.0.x
+"
option "http_no_autostart" -
#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-"do not open tcp port on server startup"
-
- flag off
+"do not open tcp port on startup"
+flag off
+details="
+ If this option is given, the http sender does not listen on
+ its tcp port. It may be instructed to open this port at a
+ later time by using the sender command.
+"
option "http_max_clients" -
#~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-"maximal simultaneous connections,
-non-positive value means unlimited"
-
- int typestr="number"
- default="-1"
- optional
-
-
-
-
+"maximal number of connections"
+int typestr="number"
+default="-1"
+optional
+details="
+ The http sender will refuse connections if already that number
+ of clients are currently connected. A non-positive value
+ (the default) allows an unlimited number of simultaneous
+ connections.
+"
+
+#####################
section "dccp sender"
-#~~~~~~~~~~~~~~~~~~~~
+#####################
option "dccp_port" -
#~~~~~~~~~~~~~~~~~~~
-
"port for dccp streaming"
-
- int typestr="portnumber"
- default="8000"
- optional
+int typestr="portnumber"
+default="8000"
+optional
+details="
+ See http_port for details.
+"
option "dccp_default_deny" -
#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-"See http_default_deny"
-
- flag off
+"make the dccp ACL a whitelist"
+flag off
+details="
+ See http_default_deny for details.
+"
option "dccp_access" -
#~~~~~~~~~~~~~~~~~~~~~
-"See http_access"
-
- string typestr="a.b.c.d/n"
- optional
- multiple
+"add an entry to the dccp ACL"
+string typestr="a.b.c.d/n"
+optional
+multiple
+details="
+ See http_access for details.
+"
option "dccp_max_clients" -
#~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-"see http_max_clients"
-
- int typestr="number"
- default="-1"
- optional
-
-
+"maximal number of connections"
+int typestr="number"
+default="-1"
+optional
+details="
+ See http_max_clients for details.
+"
+
+#####################
section "ortp sender"
-#~~~~~~~~~~~~~~~~~~~~
+#####################
option "ortp_target" -
#~~~~~~~~~~~~~~~~~~~~~
-
-"Add given host/port to the list of targets.
-This option can be given multiple times.
-Example: '224.0.1.38:1500' instructs the ortp
-sender to send to udp port 1500 on host
-224.0.1.38 (unassigned ip in the Local
-Network Control Block 224.0.0/24). This is
-useful for LAN-streaming."
-
- string typestr="a.b.c.d:p"
- optional
- multiple
+"add ortp target"
+string typestr="a.b.c.d:p"
+optional
+multiple
+details="
+ Add given host/port to the list of targets. This option
+ can be given multiple times. Example: '224.0.1.38:1500'
+ instructs the ortp sender to send to udp port 1500 on host
+ 224.0.1.38 (unassigned ip in the Local Network Control Block
+ 224.0.0/24). This is useful for LAN-streaming.
+"
option "ortp_no_autostart" -
#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-"do not start to send automatically"
-
- flag off
+"do not start sending"
+flag off
+details="
+ If this option is given, ortp streaming may be activated at
+ a later time by using the sender command.
+"
option "ortp_default_port" -
#~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-"default udp port if not specified"
-
- int typestr="portnumber"
- default="1500"
- optional
+"udp port to send to"
+int typestr="port"
+default="1500"
+optional
option "ortp_header_interval" H
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-"time between extra header sends"
-
- int typestr="milliseconds"
- default="2000"
- optional
+"duration for sending header"
+int typestr="ms"
+default="2000"
+optional
+details="
+ As the ortp sender has no idea about connected clients it
+ sends the audio file header periodically if necessary. This
+ option is used to specify the duration of the interval between
+ sending the header. Shorter values decrease the average time
+ clients have to wait before being able to start playback,
+ but this also increases the amount network traffic. Note
+ that this affects only ogg vorbis streams as this is the only
+ audio format that needs an audio file header.
+"
option "ortp_jitter_compensation" j
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-"non-zero values enable ortp's adaptive
-jitter compensation"
-
- int typestr="milliseconds"
- default="400"
- optional
+"configure jitter compensation"
+int typestr="ms"
+default="400"
+optional
+details="
+ ortp's adaptive jitter compensation gets activated whenever
+ this value is greater than zero. See the ortp documentation
+ about details on this feature.
+"
* one otherwise. If and only if the function returns one, the content of \a
* pid is meaningful.
*
- * \sa waitpid(2)
+ * \sa waitpid(2).
*/
int para_reap_child(pid_t *pid)
{
}
/**
- * wrapper around signal(2)
- * \param sig the number of the signal to catch
+ * Wrapper around signal(2).
+ *
+ * \param sig The number of the signal to catch.
*
* This installs the generic signal handler for the given signal.
+ *
* \return This function returns 1 on success and \p -E_SIGNAL_SIG_ERR on errors.
- * \sa signal(2)
+ *
+ * \sa signal(2).
*/
int para_install_sighandler(int sig)
{
}
/**
- * return number of next pending signal
+ * Return the number of next pending signal.
*
* This should be called if the fd for the signal pipe is ready for reading.
*
- * \return On success, the number of the received signal is returned. \p
- * -E_SIGNAL_READ is returned if a read error occurred while reading the signal
- * pipe. If the read was interrupted by another signal the function returns 0.
+ * \return On success, the number of the received signal is returned. If the
+ * read returned zero or was interrupted by another signal the function returns
+ * 0. Otherwise, a negative error value is returned.
*/
int para_next_signal(void)
{
int s;
- ssize_t r;
+ ssize_t r = read(signal_pipe[0], &s, sizeof(s));
- r = read(signal_pipe[0], &s, sizeof(s));
- if (r == sizeof(s)) {
- PARA_DEBUG_LOG("next signal: %d\n", s);
- return s;
+ if (!r) {
+ PARA_CRIT_LOG("read from signal pipe returned zero\n");
+ return 0;
+ }
+ if (r < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ return 0;
+ return -ERRNO_TO_PARA_ERROR(errno);
}
- return r < 0 && (errno != EAGAIN)? 0 : -E_SIGNAL_READ;
+ assert(r == sizeof(s));
+ PARA_DEBUG_LOG("next signal: %d\n", s);
+ return s;
}
/**
- * Close the signal pipe.
+ * Close the write end of the signal pipe.
*/
void para_signal_shutdown(void)
{
ret = -ret;
return ret;
}
+
+void compute_chunk_time(long unsigned chunk_num,
+ struct timeval *chunk_tv, struct timeval *stream_start,
+ struct timeval *result)
+{
+ struct timeval tmp;
+
+ tv_scale(chunk_num, chunk_tv, &tmp);
+ tv_add(&tmp, stream_start, result);
+}
return -1;
}
-static void vss_next_chunk_time(struct timeval *due)
-{
- struct timeval tmp;
-
- tv_scale(mmd->chunks_sent, &mmd->afd.afhi.chunk_tv, &tmp);
- tv_add(&tmp, &mmd->stream_start, due);
-}
-
/*
* != NULL: timeout for next chunk
* NULL: nothing to do
return &the_timeout;
if (!vss_playing() || !map)
return NULL;
- vss_next_chunk_time(&next_chunk);
+ compute_chunk_time(mmd->chunks_sent, &mmd->afd.afhi.chunk_tv,
+ &mmd->stream_start, &next_chunk);
if (chk_barrier("chunk", &now, &next_chunk, &the_timeout, 0) < 0)
return &the_timeout;
/* chunk is due or bof */
/**
* Get the header of the current audio file.
*
- * \param header_len The length of the header is stored here.
- *
- * \return A pointer to a buffer containing the header, or NULL, if no audio
- * file is selected or if the current audio format does not need special header
- * treamtment.
+ * \param buf The length of the header is stored here.
+ * \param len Points to a buffer containing the header on return.
*
+ * \sa afh_get_header.
*/
-char *vss_get_header(size_t *header_len)
+void vss_get_header(char **buf, size_t *len)
{
- if (!map || !mmd->afd.afhi.header_len)
- return NULL;
- *header_len = mmd->afd.afhi.header_len;
- return map + mmd->afd.afhi.header_offset;
+ afh_get_header(&mmd->afd.afhi, map, buf, len);
}
/**
ret = send_buffer(afs_socket, "new");
afsss = AFS_SOCKET_AFD_PENDING;
}
-
-static void get_chunk(long unsigned chunk_num, char **buf, size_t *len)
-{
- size_t pos = mmd->afd.afhi.chunk_table[chunk_num];
- *buf = map + pos;
- *len = mmd->afd.afhi.chunk_table[chunk_num + 1] - pos;
-}
-
/**
* Get the data of the given chunk.
*
return -E_CHUNK;
if (chunk_num >= mmd->afd.afhi.chunks_total)
return -E_CHUNK;
- get_chunk(chunk_num, buf, len);
+ afh_get_chunk(chunk_num, &mmd->afd.afhi, map, buf, len);
return 1;
}
if (!map || !vss_playing())
return;
gettimeofday(&now, NULL);
- vss_next_chunk_time(&due);
+ compute_chunk_time(mmd->chunks_sent, &mmd->afd.afhi.chunk_tv,
+ &mmd->stream_start, &due);
if (tv_diff(&due, &now, NULL) > 0)
return;
if (chk_barrier("eof", &now, &eof_barrier, &due, 1) < 0)
mmd->offset = tv2ms(&tmp);
mmd->events++;
}
- get_chunk(mmd->current_chunk, &buf, &len);
+ afh_get_chunk(mmd->current_chunk, &mmd->afd.afhi, map, &buf, &len);
for (i = 0; senders[i].name; i++)
senders[i].send(mmd->current_chunk, mmd->chunks_sent, buf, len);
mmd->new_vss_status_flags |= VSS_PLAYING;
unsigned int vss_repos(void);
unsigned int vss_paused(void);
unsigned int vss_stopped(void);
-char *vss_get_header(size_t *header_len);
+void vss_get_header(char **buf, size_t *len);
struct timeval *vss_chunk_time(void);
const char *supported_audio_formats(void);
int vss_get_chunk(long unsigned chunk_num, char **buf, size_t *len);