]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge commit 'remotes/meins/master'
authorAndre Noll <maan@systemlinux.org>
Mon, 17 Mar 2008 08:43:33 +0000 (09:43 +0100)
committerAndre Noll <maan@systemlinux.org>
Mon, 17 Mar 2008 08:43:33 +0000 (09:43 +0100)
20 files changed:
Makefile.in
README
afh.c [new file with mode: 0644]
afh.ggo [new file with mode: 0644]
afh.h
afh_common.c
chunk_queue.c
error.h
fd.c
fd.h
net.c
ortp_send.c
para.h
send_common.c
server.c
server.ggo
signal.c
time.c
vss.c
vss.h

index 3d3e5f7171f5449b99ada91ce786fe882298f975..3e5239cfd5868e44c6acebb5459b5d6e5bacc6e4 100644 (file)
@@ -137,7 +137,7 @@ grab_client.cmdline.h grab_client.cmdline.c: grab_client.ggo
 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
diff --git a/README b/README
index 0e1e5cb4df4a5853e340c4fb96cb2af05d6ca6db..3308d0b3f3f0cc45c83af7ca218688c39e3b34d0 100644 (file)
--- a/README
+++ b/README
@@ -102,10 +102,12 @@ para_afh
 
 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
diff --git a/afh.c b/afh.c
new file mode 100644 (file)
index 0000000..86f34ac
--- /dev/null
+++ b/afh.c
@@ -0,0 +1,148 @@
+/*
+ * 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;
+}
diff --git a/afh.ggo b/afh.ggo
new file mode 100644 (file)
index 0000000..7fb9d5a
--- /dev/null
+++ b/afh.ggo
@@ -0,0 +1,116 @@
+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.
+"
diff --git a/afh.h b/afh.h
index bd6784d5ecd83269c4ca0f5aa2c83c903c934fbf..a441d5fecc322726189c43dc45c78ceae108b847 100644 (file)
--- a/afh.h
+++ b/afh.h
@@ -103,3 +103,6 @@ int guess_audio_format(const char *name);
 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);
index 3fa88f14e7015c42b4c2adadaf1977ccc790f774..4f84334c8486d48aee712f6099fd72e847b78af5 100644 (file)
@@ -176,3 +176,33 @@ const char *audio_format_name(int i)
        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;
+}
index 61dd3820706515267c14d83d8e3254d59789a788..84f4a4a88ebfcb5acb4b0cfaf9dc861b87f82947 100644 (file)
@@ -62,7 +62,7 @@ int cq_enqueue(struct chunk_queue *cq, long unsigned chunk_num,
                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));
@@ -133,7 +133,7 @@ int cq_get(struct queued_chunk *qc, char **buf, size_t *len)
                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;
diff --git a/error.h b/error.h
index 415262d0c2fe7507efffbe710aab153ea431fb14..b9e19f55fcabb190123cc559ae342c488403281f 100644 (file)
--- a/error.h
+++ b/error.h
@@ -32,6 +32,7 @@ extern const char **para_errlist[];
 
 #define AFH_ERRORS \
        PARA_ERROR(AFH_SYNTAX, "afh syntax error"), \
+       PARA_ERROR(AFH_SHORT_WRITE, "afh short write"), \
 
 
 #define AFH_COMMON_ERRORS \
@@ -262,7 +263,6 @@ extern const char **para_errlist[];
 
 #define SIGNAL_ERRORS \
        PARA_ERROR(SIGNAL_SIG_ERR, "signal() returned SIG_ERR"), \
-       PARA_ERROR(SIGNAL_READ, "read error from signal pipe"), \
 
 
 #define STRING_ERRORS \
diff --git a/fd.c b/fd.c
index abad80a86ce368660c1ed3b88a1c0e088488a422..48e2faf97c6d3e2bcb1616ebef04db48e2003d9e 100644 (file)
--- a/fd.c
+++ b/fd.c
 #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.
  *
diff --git a/fd.h b/fd.h
index 6472c137e7d0a86007e2161b0f6691740c9f6fe3..22141be2d79a7451188f0d6986b772368248ff71 100644 (file)
--- a/fd.h
+++ b/fd.h
@@ -6,6 +6,7 @@
 
 /** \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);
diff --git a/net.c b/net.c
index 601396477c66715e21bc222d69c0d7e0b1aa9f92..b2c9c43a81ad356ec021373c12d5c764fa5b76f4 100644 (file)
--- a/net.c
+++ b/net.c
 #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. */
@@ -345,31 +347,6 @@ struct in_addr extract_v4_addr(const struct sockaddr_storage *ss)
        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.
  *
@@ -397,10 +374,10 @@ int send_bin_buffer(int fd, const char *buf, size_t len)
                /* 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;
 }
 
index 077e7cf5c6c9aa645d6c944b557288141d92a363..9c166aee289f5b06ed4c2e39e6c0ad100d73c8a8 100644 (file)
@@ -190,7 +190,7 @@ static void ortp_send(long unsigned current_chunk, long unsigned chunks_sent,
        }
        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;
diff --git a/para.h b/para.h
index d69acdf63e0cdfb30c62d24f6c5f335040090507..a39ed97df45502a0f93dcd8217cf5d9d6e792c78 100644 (file)
--- a/para.h
+++ b/para.h
@@ -157,6 +157,9 @@ int tv_convex_combination(const long a, const struct timeval *tv1,
                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};
index 448ce522ea400fd044d2724c78a9d4bcc81b730b..43aeb2b3b7597d4d5e46dfc51b036c5868d0cda1 100644 (file)
@@ -174,7 +174,9 @@ void send_chunk(struct sender_client *sc, struct sender_status *ss,
 
        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)
index a5ae0268cfde442345cd2c0993f6a95ce07f1c2a..90dfc1f58962f69355775fd87e4c08f8253efeac 100644 (file)
--- a/server.c
+++ b/server.c
@@ -263,12 +263,6 @@ static void parse_config(int override)
                server_cmdline_parser_config_file(cf, &conf, &params);
                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;
index e7149145d56f430dee2c6d7a3a4775fc602f2f33..a6ff8c922a51e219685ce190bbe83a608c8b8b9c 100644 (file)
+#########################
 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"
@@ -130,180 +147,211 @@ 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.
+"
index 78e788d19021111b2f8ec712eb1ff8f0fd176375..f8be2f4612002214f669fa74ce92390866b0dc90 100644 (file)
--- a/signal.c
+++ b/signal.c
@@ -73,7 +73,7 @@ static void generic_signal_handler(int s)
  * 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)
 {
@@ -110,12 +110,15 @@ void para_reap_children(void)
 }
 
 /**
- * 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)
 {
@@ -124,29 +127,35 @@ 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)
 {
diff --git a/time.c b/time.c
index b7155cb0566dc9f04de83d4056338c199135da06..1fd7b0981584fc4557079ee62a87fbd713554f3a 100644 (file)
--- a/time.c
+++ b/time.c
@@ -169,3 +169,13 @@ int tv_convex_combination(const long a, const struct timeval *tv1,
                        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);
+}
diff --git a/vss.c b/vss.c
index 8ca37d435a40ce049b2ddfa0875325e41b4d61c9..1be4cd15d1db262dd5f79a43b19e7bba09f2087a 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -143,14 +143,6 @@ static int chk_barrier(const char *bname, const struct timeval *now,
        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
@@ -177,7 +169,8 @@ static struct timeval *vss_compute_timeout(void)
                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 */
@@ -222,19 +215,14 @@ static void vss_eof(void)
 /**
  * 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);
 }
 
 /**
@@ -440,14 +428,6 @@ void vss_post_select(fd_set *rfds, fd_set *wfds)
        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.
  *
@@ -463,7 +443,7 @@ int vss_get_chunk(long unsigned chunk_num, char **buf, size_t *len)
                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;
 }
 
@@ -486,7 +466,8 @@ void vss_send_chunk(void)
        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)
@@ -510,7 +491,7 @@ void vss_send_chunk(void)
                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;
diff --git a/vss.h b/vss.h
index f48e964b2824e7b2b305915e3262df3619b1542e..3b238eb74f5b4e1124cbe38d3db6fc3adc9cbb21 100644 (file)
--- a/vss.h
+++ b/vss.h
@@ -14,7 +14,7 @@ unsigned int vss_next(void);
 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);