Start para_server
-----------------
+Before starting the server make sure you have write permissions to
+the directory /var/paraslash.
+
+ sudo chown $user /var/paraslash
+
+Alternatively, use the --afs_socket Option to specify a different
+location for the afs command socket.
+
For this first try, we'll use a debug level of two to make the
output of para_server more verbose.
uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS")
uname_rs := $(shell uname -rs)
cc_version := $(shell $(CC) --version | head -n 1)
-codename := axiomatic perspectivity
+codename := elliptic inheritance
DEBUG_CPPFLAGS += -Wno-sign-compare -g -Wunused -Wundef -W
DEBUG_CPPFLAGS += -Wredundant-decls
# invalid option for gcc-3.3.3
# DEBUG_CPPFLAGS += -Wextra
# DEBUG_CPPFLAGS += -Wold-style-definition
+# DEBUG_CPPFLAGS += -Wdeclaration-after-statement
# many warnings about trivial stuff
# CPPFLAGS += -Wconversion
CPPFLAGS += -DCC_VERSION='"$(cc_version)"'
CPPFLAGS += -Werror-implicit-function-declaration
CPPFLAGS += -Wmissing-format-attribute
+CPPFLAGS += -Wmissing-noreturn
CPPFLAGS += -Wunused-macros
CPPFLAGS += -Wbad-function-cast
CPPFLAGS += -DMAIN_INPUT_FILE_IS_$(*F)
audioc.ggo) O="--unamed-opts=command";; \
fsck.ggo) O="--unamed-opts=table";; \
afh.ggo) O="--unamed-opts=audio_file";; \
+ recv.ggo) O="--no-handle-help";; \
+ filter.ggo) O="--no-handle-help";; \
+ write.ggo) O="--no-handle-help";; \
+ audiod.ggo) O="--no-handle-help";; \
esac; \
if test $< != fsck.ggo; then O="$$O --conf-parser "; fi; \
gengetopt $$O \
mkdir -p man/man1
help2man -h --detailed-help -N -i audiod_command_list.man ./para_audiod > $@
+man/man1/para_filter.1: para_filter
+ mkdir -p man/man1
+ help2man -h --detailed-help -N ./$< > $@
+
+man/man1/para_write.1: para_write
+ mkdir -p man/man1
+ help2man -h --detailed-help -N ./$< > $@
+
+man/man1/para_recv.1: para_recv
+ mkdir -p man/man1
+ help2man -h --detailed-help -N ./$< > $@
+
man/man1/%.1: %
mkdir -p man/man1
help2man -N ./$< > $@
ortp_send.o: ortp_send.c
$(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @ortp_cppflags@ $<
-oggdec.o: oggdec.c
+oggdec_filter.o: oggdec_filter.c
$(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @oggvorbis_cppflags@ $<
ogg_afh.o: ogg_afh.c
$(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @oggvorbis_cppflags@ $<
-mp3dec.o: mp3dec.c
+mp3dec_filter.o: mp3dec_filter.c
$(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @mad_cppflags@ $<
-aacdec.o: aacdec.c
+aacdec_filter.o: aacdec_filter.c
$(CC) -c $(CPPFLAGS) $(DEBUG_CPPFLAGS) @faad_cppflags@ $<
aac_common.o: aac_common.c
@PACKAGE_TARNAME@-@PACKAGE_VERSION@.tar.bz2: $(tarball_add)
rm -rf $(tarball_pfx).tar.bz2 $(tarball_pfx)
- git-archive --format=tar --prefix=$(tarball_pfx)/ HEAD \
+ git archive --format=tar --prefix=$(tarball_pfx)/ HEAD \
| tar --delete $(tarball_delete) > $(tarball_pfx).tar
mkdir $(tarball_pfx)
cp -r $(tarball_add) $(tarball_pfx)
NEWS
====
--------------------------------------------------
-0.3.3 (to be announced) "axiomatic perspectivity"
--------------------------------------------------
+----------------------------------------------
+0.3.4 (to be announced) "elliptic inheritance"
+----------------------------------------------
+ - new options for mp3dec: --ignore-crc, --bufsize
+ - Improved help/man pages: The documentation of para_audiod,
+ para_recv, para_filter and para_write now also contains
+ all options of the available receivers/filters/writers.
+ - new audiod option: --config-file.
+
+--------------------------------------------
+0.3.3 (2008-12-01) "axiomatic perspectivity"
+--------------------------------------------
Internal code cleanups, bug fixes, improved tag handling and the new
amplification filter.
- overhaul of the virtual streaming system.
- mp3: id3 version 2 support via libid3tag (optional)
- ogg: vorbis comment support.
+ - aac meta info support.
- mp3 audio format handler cleanups.
- new filter: "amp" to amplify the amplitude of the audio stream
- new status item/database entry: amplification. It is
used by the amp filter to pre-amplify the audio stream.
- fix a close-without-open bug in para_write.
+ - fix a bug in com_init() which was introduced in 0.3.2.
+ - better error diagnostics for para_client.
-----------------------------------------
0.3.2 (2008-04-11) "probabilistic parity"
The audio file selector
=======================
-Paraslash comes with a sophisticated audio file selector called afs.
+Paraslash comes with a sophisticated audio file selector called *afs*.
In the
<<
<a href="INSTALL.html">installation notes</a>,
para_client setatt test+ /path/to/the/audio/file
-Similarly, the "test" bit can be removed from a audio file with
+Similarly, the "test" bit can be removed from an audio file with
para_client setatt test- /path/to/the/audio/file
-Instead of a path you can also use a pattern, and the attribute is
-applied to all audio files matching that pattern:
+Instead of a path you may use a shell wildcard pattern. The attribute
+is applied to all audio files matching that pattern:
para_client setatt test+ '/test/directory/*'
alias para='para_client --'
-and be happy. In the remainder part this alias is being used.
+and be happy. In what follows we shall use this alias.
-Drop the test attribute entirely from the database with
+The "test" attribute can be dropped from the database with
para rmatt test
[skip this part if you don't like formal definitions]
-A mood consists of a unique name and its *mood definition*, which is a set of
-*mood lines* containing expressions in terms of attributes and other data
-contained in the database.
+A mood consists of a unique name and its *mood definition*, which is
+a set of *mood lines* containing expressions in terms of attributes
+and other data contained in the database.
-A mood defines a subset of audio files called the *admissible audio files*
-for that mood. A mood can be *active* which means that para_server
-is going to select only files from that subset of admissible files.
+A mood defines a subset of audio files called the *admissible audio
+files* for that mood. At any time, at most one mood can be *active*
+which means that para_server is going to select only files from that
+subset of admissible files.
So in order to create a mood definition one has to write a set of
mood lines. Mood lines come in three flavours: Accept lines, deny
order in which admissible files are going to be selected, but is of
minor importance for this introduction.
-So we concentrate on the first two forms, that is accept and deny
+So we concentrate on the first two forms, i.e. accept and deny
lines. As usual, everything in square brackets is optional, i.e.
accept/deny lines take the following form when ignoring scores:
The set of admissible files for the whole mood is now defined as those
files which match at least one accept mood line, but no deny mood line.
-More formally, a file is admissible if and only if
+More formally, an audio file F is admissible if and only if
(F ~ AL1 or F ~ AL2...) and not (F ~ DL1 or F ~ DN2 ...)
-where F is the file, AL1, AL2... are the accept lines, DL1, DL2... are
-the deny lines and "~" means "matches".
+where AL1, AL2... are the accept lines, DL1, DL2... are the deny
+lines and "~" means "matches".
The cases where no mood lines of accept/deny type are defined need
special treatment:
A mood can be activated by executing
- para chmood my_mood
+ para select m/my_mood
Once active, the list of admissible files is shown by the ls command
if the "-a" switch is given:
Use loglevel one (option -l 1 for most commands) to show debugging
info. Almost all paraslash executables have a brief online help which
-is displayed by using the -h switch.
+is displayed by using the -h switch. The --detailed-help option prints
+the full help text.
para_fsck tries to fix your database. Use --force (even if your name
-isn't Luke), to clean up after a crash. However, first make sure
+isn't Luke) to clean up after a crash. However, first make sure
para_server isn't running before executing para_fsck if para_fsck
complains about busy (dirty) tables. para_fsck also contains an option
-to dump the contents of your the contents of the database to the file
-system.
+to dump the contents of the database to the file system.
If you don't mind to recreate your database you can start
from scratch by removing the entire database directory, i.e.
Optional features:
- *mp3*: The mp3 decoder of para_filter is based on libmad:
- http://www.underbit.com/products/mad/. If you prefer to use
- the libmad package provided by your distributor, make sure
- to install the corresponding development package as well.
-
- For version 2 id3 tag support, you'll need libid3tag which
- is also available through the above link. Without libid3tag,
- only version 1 tags are recognized.
-
+ http://www.underbit.com/products/mad/. If you prefer to
+ use the libmad package provided by your distributor, make
+ sure to install the corresponding development package as
+ well. It is called libmad0-dev on debian-based systems.
Note that libmad is not necessary for the server side,
i.e. for sending mp3 files.
+ - *id3 tags*:
+ For version-2 id3 tag support, you'll need libid3tag which
+ is also available through the above link (alternatively:
+ install package libid3tag0-dev). Without libid3tag, only
+ version one tags are recognized.
+
- *ogg vorbis*: For ogg vorbis streams you'll need libogg,
libvorbis, libvorbisfile: http://www.xiph.org/downloads/.
The corresponding Debian packages are called libogg-dev
- *aac*:
For aac files (m4a) you'll need libfaad. Get it at
- http://www.audiocoding.com/modules/wiki/?page=AAC
+ http://www.audiocoding.com/.
+ Debian package: libfaad-dev.
- *ortp*:
If you intend to use the optional ortp streamer, you'll
return -E_STSZ;
}
+static int atom_cmp(unsigned char *buf1, char *buf2)
+{
+ unsigned char *b2 = (unsigned char *)buf2;
+
+ if (buf1[0] != b2[0])
+ return 1;
+ if (buf1[1] != b2[1])
+ return 1;
+ if (buf1[2] != b2[2])
+ return 1;
+ if (buf1[3] != b2[3])
+ return 1;
+ return 0;
+}
+
+static int read_atom_header(unsigned char *buf, uint64_t *subsize, unsigned char type[5])
+{
+ int i;
+ uint64_t size = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
+
+ memcpy(type, buf + 4, 4);
+ type[4] = '\0';
+
+ PARA_DEBUG_LOG("size: %llu, type: %s\n", (long long unsigned)size, type);
+ if (size != 1) {
+ *subsize = size;
+ return 8;
+ }
+ buf += 4;
+ size = 0;
+ for (i = 0; i < 8; i++)
+ size |= ((uint64_t)buf[i]) << ((7 - i) * 8);
+ *subsize = size;
+ return 16;
+}
+
+static char *get_tag(unsigned char *p, int size)
+{
+ char *buf;
+
+ assert(size > 0);
+ buf = para_malloc(size + 1);
+
+ memcpy(buf, p, size);
+ buf[size] = '\0';
+ PARA_DEBUG_LOG("size: %d: %s\n", size, buf);
+ return buf;
+}
+
+static char *read_tags(unsigned char *buf, size_t buflen)
+{
+ unsigned char *p = buf;
+ char *title = NULL, *artist = NULL, *album = NULL, *year = NULL,
+ *comment = NULL, *result;
+
+ while (p + 32 < buf + buflen) {
+ unsigned char *q, type1[5], type2[5];
+ uint64_t size1, size2;
+ int ret, ret2;
+
+ ret = read_atom_header(p, &size1, type1);
+ ret2 = read_atom_header(p + ret, &size2, type2);
+
+ if (size2 <= 16 || atom_cmp(type2, "data")) {
+ p += size1;
+ continue;
+ }
+ size2 -= 16;
+ q = p + ret + ret2 + 8;
+ if (q + size2 > buf + buflen)
+ break;
+ if (!atom_cmp(type1, "©ART"))
+ artist = get_tag(q, size2);
+ else if (!atom_cmp(type1, "©alb"))
+ album = get_tag(q, size2);
+ else if (!atom_cmp(type1, "©nam"))
+ title = get_tag(q, size2);
+ else if (!atom_cmp(type1, "©cmt"))
+ comment = get_tag(q, size2);
+ else if (!atom_cmp(type1, "©day"))
+ year = get_tag(q, size2);
+ p += size1;
+ }
+ result = make_taginfo(title, artist, album, year, comment);
+ free(title);
+ free(artist);
+ free(album);
+ free(year);
+ free(comment);
+ return result;
+}
+
+static char *read_meta(unsigned char *buf, size_t buflen)
+{
+ unsigned char *p = buf;
+
+ while (p + 4 < buf + buflen) {
+
+ if (p[0] != 'i' || p[1] != 'l' || p[2] != 's' || p[3] != 't') {
+ p++;
+ continue;
+ }
+ p += 4;
+ return read_tags(p, buflen - (p - buf));
+ }
+ return make_taginfo(NULL, NULL, NULL, NULL, NULL);
+}
+
+static char *aac_get_taginfo(unsigned char *buf, size_t buflen)
+{
+ int i;
+ uint64_t subsize;
+ unsigned char type[5];
+
+ for (i = 0; i + 24 < buflen; i++) {
+ unsigned char *p = buf + i;
+ if (p[0] != 'm' || p[1] != 'e' || p[2] != 't' || p[3] != 'a')
+ continue;
+ PARA_INFO_LOG("found metadata at offset %d\n", i);
+ i += 8;
+ p = buf + i;
+ i += read_atom_header(p, &subsize, type);
+ p = buf + i;
+ return read_meta(p, buflen - i);
+ }
+ PARA_INFO_LOG("no meta data\n");
+ return make_taginfo(NULL, NULL, NULL, NULL, NULL);
+}
+
static ssize_t aac_compute_chunk_table(struct afh_info *afhi,
unsigned char *map, size_t numbytes)
{
mp4AudioSpecificConfig mp4ASC;
NeAACDecHandle handle = NULL;
unsigned char *umap = (unsigned char *) map;
+ char *taginfo;
ret = aac_find_esds(umap, numbytes, &skip, &decoder_len);
if (ret < 0)
goto out;
+ taginfo = aac_get_taginfo(umap, numbytes);
handle = aac_open();
ret = -E_AAC_AFH_INIT;
if (NeAACDecInit(handle, umap + skip, decoder_len, &rate, &channels))
ret = (afhi->chunk_table[afhi->chunks_total] - afhi->chunk_table[0]) * 8; /* bits */
ret += (channels * afhi->seconds_total * 500); /* avoid rounding error */
afhi->bitrate = ret / (channels * afhi->seconds_total * 1000);
- afhi->info_string = make_message("%s:\n%s:\n%s:\n",
+ afhi->info_string = make_message("%s:\n%s",
status_item_list[SI_AUDIO_FILE_INFO],
- status_item_list[SI_TAGINFO1],
- status_item_list[SI_TAGINFO2]
- );
+ taginfo);
+ free(taginfo);
tv_scale(20, &afhi->chunk_tv, &afhi->eof_tv);
ret = 1;
out:
+++ /dev/null
-/*
- * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-/*
- * based in parts on libfaad, Copyright (C) 2003-2005 M. Bakker,
- * Ahead Software AG
- */
-
-/** \file aacdec.c paraslash's aac (m4a) decoder */
-
-#include "para.h"
-
-#include "list.h"
-#include "sched.h"
-#include "filter.h"
-#include "error.h"
-#include "string.h"
-#include "aac.h"
-
-/** the output buffer size */
-#define AAC_OUTBUF_SIZE (32 * 1024)
-
-/** give up decoding after that many errors */
-#define MAX_ERRORS 20
-
-/**
- * data specific to the aacdec filter
- *
- * \sa filter, filter_node
- */
-struct private_aacdec_data {
- /** the return value of aac_open */
- NeAACDecHandle handle;
- /** info about the currently decoded frame */
- NeAACDecFrameInfo frame_info;
- /** whether this instance of the aac decoder is already initialized */
- int initialized;
- /**
- * return value of aac_find_esds(). Used to call the right aacdec
- * init function
- */
- unsigned long decoder_length;
- /** number of times the decoder returned an error */
- unsigned error_count;
- /** number of bytes already consumed from the imput stream */
- size_t consumed_total;
- /** return value of aac_find_entry_point */
- size_t entry;
-};
-
-static ssize_t aacdec(char *input_buffer, size_t len, struct filter_node *fn)
-{
- struct private_aacdec_data *padd = fn->private_data;
- struct filter_chain *fc = fn->fc;
- int i, ret;
- unsigned char *p, *outbuffer;
- unsigned char *inbuf = (unsigned char*)input_buffer;
- size_t skip, consumed = 0;
-
- if (fn->loaded > fn->bufsize * 3 / 5)
- return 0;
- if (len < 2048 && !*fc->input_error)
- return 0;
-
- if (!padd->initialized) {
- unsigned long rate = 0;
- unsigned char channels = 0;
- ret = aac_find_esds(inbuf, len, &skip, &padd->decoder_length);
- if (ret < 0) {
- PARA_INFO_LOG("%s\n", para_strerror(-ret));
- ret = NeAACDecInit(padd->handle, inbuf,
- len, &rate, &channels);
- PARA_INFO_LOG("decoder init: %d\n", ret);
- if (ret < 0) {
- ret = -E_AACDEC_INIT;
- goto out;
- }
- consumed = ret;
- } else {
- PARA_INFO_LOG("decoder len: %lu\n",
- padd->decoder_length);
- consumed += skip;
- p = inbuf + consumed;
- ret = -E_AACDEC_INIT;
- if (NeAACDecInit2(padd->handle, p,
- padd->decoder_length, &rate,
- &channels) < 0)
- goto out;
- }
- fc->samplerate = rate;
- fc->channels = channels;
- PARA_INFO_LOG("rate: %u, channels: %d\n",
- fc->samplerate, fc->channels);
- padd->initialized = 1;
- }
- if (padd->decoder_length > 0) {
- consumed = 0;
- if (!padd->entry) {
- ret = aac_find_entry_point(inbuf + consumed,
- len - consumed, &skip);
- if (ret < 0) {
- ret = len;
- goto out;
- }
- consumed += skip;
- padd->entry = ret;
- PARA_INFO_LOG("entry: %zu\n", padd->entry);
- }
- ret = len;
- if (padd->consumed_total + len < padd->entry)
- goto out;
- if (padd->consumed_total < padd->entry)
- consumed = padd->entry - padd->consumed_total;
- }
- for (; consumed < len; consumed++)
- if ((inbuf[consumed] & 0xfe) == 0x20)
- break;
- if (consumed >= len)
- goto success;
- p = inbuf + consumed;
- outbuffer = NeAACDecDecode(padd->handle, &padd->frame_info, p,
- len - consumed);
- if (padd->frame_info.error) {
- ret = -E_AAC_DECODE;
- if (padd->error_count++ > MAX_ERRORS)
- goto out;
- PARA_ERROR_LOG("frame_error: %d, consumed: %zu + %zd + %lu\n",
- padd->frame_info.error, padd->consumed_total,
- consumed, padd->frame_info.bytesconsumed);
- PARA_ERROR_LOG("%s\n", NeAACDecGetErrorMessage(
- padd->frame_info.error));
- consumed++; /* catch 21 */
- goto success;
- }
- padd->error_count = 0;
- consumed += padd->frame_info.bytesconsumed;
- ret = consumed;
- if (!padd->frame_info.samples)
- goto out;
- ret = -E_AAC_OVERRUN;
- if (padd->frame_info.samples * 2 + fn->loaded > fn->bufsize)
- goto out;
- for (i = 0; i < padd->frame_info.samples; i++) {
- short *s = (short *)outbuffer;
- write_int16_host_endian(fn->buf + fn->loaded, s[i]);
- fn->loaded += 2;
- }
-success:
- ret = consumed;
-out:
- if (ret > 0)
- padd->consumed_total += ret;
- return ret;
-}
-
-static void aacdec_open(struct filter_node *fn)
-{
- fn->private_data = para_calloc(sizeof(struct private_aacdec_data));
- struct private_aacdec_data *padd = fn->private_data;
-
- fn->bufsize = AAC_OUTBUF_SIZE;
- fn->buf = para_calloc(fn->bufsize);
- padd->handle = aac_open();
-}
-
-static void aacdec_close(struct filter_node *fn)
-{
- struct private_aacdec_data *padd = fn->private_data;
-
- NeAACDecClose(padd->handle);
- free(fn->buf);
- fn->buf = NULL;
- free(padd);
- fn->private_data = NULL;
-}
-
-/**
- * the init function of the aacdec filter
- *
- * \param f pointer to the filter struct to initialize
- *
- * \sa filter::init
- */
-void aacdec_init(struct filter *f)
-{
- f->open = aacdec_open;
- f->convert = aacdec;
- f->close = aacdec_close;
-}
--- /dev/null
+/*
+ * Copyright (C) 2006-2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+/*
+ * based in parts on libfaad, Copyright (C) 2003-2005 M. Bakker,
+ * Ahead Software AG
+ */
+
+/** \file aacdec_filter.c paraslash's aac (m4a) decoder. */
+
+#include "para.h"
+
+#include "list.h"
+#include "sched.h"
+#include "ggo.h"
+#include "filter.h"
+#include "error.h"
+#include "string.h"
+#include "aac.h"
+
+/** the output buffer size */
+#define AAC_OUTBUF_SIZE (32 * 1024)
+
+/** give up decoding after that many errors */
+#define MAX_ERRORS 20
+
+/**
+ * data specific to the aacdec filter
+ *
+ * \sa filter, filter_node
+ */
+struct private_aacdec_data {
+ /** the return value of aac_open */
+ NeAACDecHandle handle;
+ /** info about the currently decoded frame */
+ NeAACDecFrameInfo frame_info;
+ /** whether this instance of the aac decoder is already initialized */
+ int initialized;
+ /**
+ * return value of aac_find_esds(). Used to call the right aacdec
+ * init function
+ */
+ unsigned long decoder_length;
+ /** number of times the decoder returned an error */
+ unsigned error_count;
+ /** number of bytes already consumed from the imput stream */
+ size_t consumed_total;
+ /** return value of aac_find_entry_point */
+ size_t entry;
+};
+
+static ssize_t aacdec(char *input_buffer, size_t len, struct filter_node *fn)
+{
+ struct private_aacdec_data *padd = fn->private_data;
+ struct filter_chain *fc = fn->fc;
+ int i, ret;
+ unsigned char *p, *outbuffer;
+ unsigned char *inbuf = (unsigned char*)input_buffer;
+ size_t skip, consumed = 0;
+
+ if (fn->loaded > fn->bufsize * 3 / 5)
+ return 0;
+ if (len < 2048 && !*fc->input_error)
+ return 0;
+
+ if (!padd->initialized) {
+ unsigned long rate = 0;
+ unsigned char channels = 0;
+ ret = aac_find_esds(inbuf, len, &skip, &padd->decoder_length);
+ if (ret < 0) {
+ PARA_INFO_LOG("%s\n", para_strerror(-ret));
+ ret = NeAACDecInit(padd->handle, inbuf,
+ len, &rate, &channels);
+ PARA_INFO_LOG("decoder init: %d\n", ret);
+ if (ret < 0) {
+ ret = -E_AACDEC_INIT;
+ goto out;
+ }
+ consumed = ret;
+ } else {
+ PARA_INFO_LOG("decoder len: %lu\n",
+ padd->decoder_length);
+ consumed += skip;
+ p = inbuf + consumed;
+ ret = -E_AACDEC_INIT;
+ if (NeAACDecInit2(padd->handle, p,
+ padd->decoder_length, &rate,
+ &channels) < 0)
+ goto out;
+ }
+ fc->samplerate = rate;
+ fc->channels = channels;
+ PARA_INFO_LOG("rate: %u, channels: %d\n",
+ fc->samplerate, fc->channels);
+ padd->initialized = 1;
+ }
+ if (padd->decoder_length > 0) {
+ consumed = 0;
+ if (!padd->entry) {
+ ret = aac_find_entry_point(inbuf + consumed,
+ len - consumed, &skip);
+ if (ret < 0) {
+ ret = len;
+ goto out;
+ }
+ consumed += skip;
+ padd->entry = ret;
+ PARA_INFO_LOG("entry: %zu\n", padd->entry);
+ }
+ ret = len;
+ if (padd->consumed_total + len < padd->entry)
+ goto out;
+ if (padd->consumed_total < padd->entry)
+ consumed = padd->entry - padd->consumed_total;
+ }
+ for (; consumed < len; consumed++)
+ if ((inbuf[consumed] & 0xfe) == 0x20)
+ break;
+ if (consumed >= len)
+ goto success;
+ p = inbuf + consumed;
+ outbuffer = NeAACDecDecode(padd->handle, &padd->frame_info, p,
+ len - consumed);
+ if (padd->frame_info.error) {
+ ret = -E_AAC_DECODE;
+ if (padd->error_count++ > MAX_ERRORS)
+ goto out;
+ PARA_ERROR_LOG("frame_error: %d, consumed: %zu + %zd + %lu\n",
+ padd->frame_info.error, padd->consumed_total,
+ consumed, padd->frame_info.bytesconsumed);
+ PARA_ERROR_LOG("%s\n", NeAACDecGetErrorMessage(
+ padd->frame_info.error));
+ consumed++; /* catch 21 */
+ goto success;
+ }
+ padd->error_count = 0;
+ consumed += padd->frame_info.bytesconsumed;
+ ret = consumed;
+ if (!padd->frame_info.samples)
+ goto out;
+ ret = -E_AAC_OVERRUN;
+ if (padd->frame_info.samples * 2 + fn->loaded > fn->bufsize)
+ goto out;
+ for (i = 0; i < padd->frame_info.samples; i++) {
+ short *s = (short *)outbuffer;
+ write_int16_host_endian(fn->buf + fn->loaded, s[i]);
+ fn->loaded += 2;
+ }
+success:
+ ret = consumed;
+out:
+ if (ret > 0)
+ padd->consumed_total += ret;
+ return ret;
+}
+
+static void aacdec_open(struct filter_node *fn)
+{
+ struct private_aacdec_data *padd = para_calloc(sizeof(*padd));
+
+ fn->private_data = padd;
+ fn->bufsize = AAC_OUTBUF_SIZE;
+ fn->buf = para_calloc(fn->bufsize);
+ padd->handle = aac_open();
+}
+
+static void aacdec_close(struct filter_node *fn)
+{
+ struct private_aacdec_data *padd = fn->private_data;
+
+ NeAACDecClose(padd->handle);
+ free(fn->buf);
+ fn->buf = NULL;
+ free(padd);
+ fn->private_data = NULL;
+}
+
+/**
+ * the init function of the aacdec filter
+ *
+ * \param f pointer to the filter struct to initialize
+ *
+ * \sa filter::init
+ */
+void aacdec_filter_init(struct filter *f)
+{
+ f->open = aacdec_open;
+ f->convert = aacdec;
+ f->close = aacdec_close;
+}
return ret;
}
}
+ if (!size)
+ continue;
PARA_INFO_LOG("writing chunk %lu\n", i);
ret = write_all(STDOUT_FILENO, buf, &size);
if (ret < 0)
return 1;
}
+/**
+ * The main function of para_afh.
+ *
+ * \param argc Usual argument count.
+ * \param argv Usual argument vector.
+ *
+ * \return \p EXIT_FAILURE or \p EXIT_SUCCESS.
+ */
int main(int argc, char **argv)
{
- int ret, audio_format_num, fd;
+ int i, ret, audio_format_num, fd;
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, &fd);
- if (ret < 0)
+ if (conf.inputs_num == 0)
goto out;
- ret = compute_afhi(conf.inputs[0], audio_file_data, audio_file_size,
- fd, &afhi);
- if (ret < 0)
+ if (conf.stream_given && conf.inputs_num != 1)
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;
+ afh_init();
+ for (i = 0; i < conf.inputs_num; i++) {
+ int ret2;
+ ret = mmap_full_file(conf.inputs[i], O_RDONLY, &audio_file_data,
+ &audio_file_size, &fd);
+ if (ret < 0)
+ goto out;
+ ret = compute_afhi(conf.inputs[i], audio_file_data, audio_file_size,
+ fd, &afhi);
+ if (ret < 0)
+ goto out;
+ audio_format_num = ret;
+ if (conf.stream_given)
+ ret = cat_file(audio_file_data, &afhi);
+ else {
+ printf("File %d: %s\n", i + 1, conf.inputs[i]);
+ print_info(audio_format_num, &afhi);
+ if (conf.chunk_table_given)
+ print_chunk_table(&afhi);
+ printf("\n");
+ }
+ ret2 = para_munmap(audio_file_data, audio_file_size);
+ if (ret2 < 0 && ret >= 0)
+ ret = ret2;
+ if (ret < 0)
+ break;
}
out:
if (ret < 0)
}
}
-
/**
* Guess the audio format judging from filename.
*
* \param name The filename.
*
- * \return This function returns -1 if it has no idea what kind of audio
- * file this might be. Otherwise the (non-negative) number of the audio format
- * is returned.
+ * \return This function returns \p -E_AUDIO_FORMAT if it has no idea what kind
+ * of audio file this might be. Otherwise the (non-negative) number of the
+ * audio format is returned.
*/
int guess_audio_format(const char *name)
{
return i;
}
}
- return -1;
+ return -E_AUDIO_FORMAT;
}
+/**
+ * Pretty-print the given meta-info.
+ *
+ * \param title The title of the audio file.
+ * \param artist The artist.
+ * \param album The name of the album.
+ * \param year Year of release.
+ * \param comment Further comments.
+ *
+ * This function is called by each audio format handler to produce the tag info
+ * status items. Usually, the audio format handlers read this info from the
+ * audio file (id3 tags, vorbis comments, ...).
+ *
+ * It is OK to pass \p NULL pointers for any argument in which case a suitable
+ * string is inserted which indicates that this information is not available.
+ *
+ * \return The status item string. It must be freed by the caller.
+ */
char *make_taginfo(char *title, char *artist, char *album, char *year,
char *comment)
{
return make_message("%s: %s, by %s\n" /* taginfo1 */
- "%s: A: %s, Y: %s, C: %s\n", /* taginfo 2*/
+ "%s: A: %s, Y: %s, C: %s\n", /* taginfo2 */
status_item_list[SI_TAGINFO1],
(title && *title)? title : "(title tag not set)",
(artist && *artist)? artist : "(artist tag not set)",
return i >= 0? afl[i].name : "(none)";
}
-
+/**
+ * Get one chunk of audio data.
+ *
+ * \param chunk_num The number of the chunk to get.
+ * \param afhi Describes the audio file.
+ * \param map The memory mapped audio file.
+ * \param buf Result pointer.
+ * \param len The length of the chunk in bytes.
+ *
+ * Upon return, \a buf will point so memory inside \a map. The returned buffer
+ * must therefore not be freed by the caller.
+ */
void afh_get_chunk(long unsigned chunk_num, struct afh_info *afhi,
void *map, const char **buf, size_t *len)
{
query.data = (char *)query_shm + sizeof(*cq);
query.size = cq->query_size;
cq->handler(fd, &query);
- return 1;
+ return shm_detach(query_shm);
}
static int execute_server_command(void)
return -E_BAD_TABLE_NAME;
}
}
- ret = send_callback_request(create_tables_callback, &query, NULL, NULL);
+ ret = send_callback_request(create_tables_callback, &query, &send_result, &fd);
if (ret < 0)
return send_va_buffer(fd, "%s\n", para_strerror(-ret));
return ret;
#include "vss.h"
#include "fd.h"
#include "ipc.h"
+#include "portable_io.h"
static struct osl_table *audio_file_table;
static int save_afd(struct audio_file_data *afd)
{
size_t size = sizeof(*afd) + sizeof_chunk_table(&afd->afhi);
-
- PARA_DEBUG_LOG("size: %zu\n", size);
int shmid, ret = shm_new(size);
void *shm_afd;
char *buf;
GET_NUM_DIGITS(d->afsi.num_played, &num_digits);
w->num_played_width = PARA_MAX(w->num_played_width, num_digits);
/* get the number of chars to print this amount of time */
- tmp = get_duration_width(d->afhi.seconds_total);
- w->duration_width = PARA_MAX(w->duration_width, tmp);
+ num_digits = get_duration_width(d->afhi.seconds_total);
+ w->duration_width = PARA_MAX(w->duration_width, num_digits);
GET_NUM_DIGITS(d->afsi.amp, &num_digits);
w->amp_width = PARA_MAX(w->amp_width, num_digits);
if (options->flags & LS_FLAG_ADMISSIBLE_ONLY) {
struct afs_info default_afsi = {.last_played = 0};
struct para_buffer msg = {.max_size = SHMMAX,
.max_size_handler = pass_buffer_as_shm, .private_data = &fd};
+ uint16_t afhi_offset, chunks_offset;
hash = (HASH_TYPE *)buf + CAB_HASH_OFFSET;
hash_to_asc(hash, asc);;
goto out;
}
/* no hs or force mode, child must have sent afhi */
- uint16_t afhi_offset = read_u16(buf + CAB_AFHI_OFFSET_POS);
- uint16_t chunks_offset = read_u16(buf + CAB_CHUNKS_OFFSET_POS);
+ afhi_offset = read_u16(buf + CAB_AFHI_OFFSET_POS);
+ chunks_offset = read_u16(buf + CAB_CHUNKS_OFFSET_POS);
objs[AFTCOL_AFHI].data = buf + afhi_offset;
objs[AFTCOL_AFHI].size = chunks_offset - afhi_offset;
afhi_ptr = &afhi;
}
munmap(map.data, map.size);
+ close(fd);
if (pad->flags & ADD_FLAG_VERBOSE) {
send_ret = send_va_buffer(pad->fd, "adding %s\n", path);
if (send_ret < 0)
};
int ret;
char *source_path = (char *)query->data + sizeof(cad.flags);
-
- ret = get_afsi_of_path(source_path, &cad.source_afsi);
- if (ret < 0)
- goto out;
struct pattern_match_data pmd = {
.table = audio_file_table,
.loop_col_num = AFTCOL_HASH,
.data = &cad,
.action = copy_selector_info
};
+
+ ret = get_afsi_of_path(source_path, &cad.source_afsi);
+ if (ret < 0)
+ goto out;
ret = for_each_matching_row(&pmd);
out:
if (ret < 0)
#include "string.h"
#include "list.h"
#include "sched.h"
+#include "ggo.h"
#include "write.h"
#include "alsa_write.cmdline.h"
#include "error.h"
/**
* the samplerate given by command line option or the decoder
* of the writer node group
- */
+ */
unsigned samplerate;
/**
* the number of channels, also given by command line option or the
return -E_SW_PARAMS;
pad->bytes_per_frame = snd_pcm_format_physical_width(FORMAT)
* pad->channels / 8;
+ PARA_INFO_LOG("bytes per frame: %zu\n", pad->bytes_per_frame);
if (snd_pcm_nonblock(pad->handle, 1))
- PARA_ERROR_LOG("%s\n", "failed to set nonblock mode");
+ PARA_ERROR_LOG("failed to set nonblock mode\n");
return period_size * pad->bytes_per_frame;
}
frames, (int)ret);
return -E_ALSA_WRITE;
}
- if (ret == -EAGAIN) { /* try again in 5ms */
- PARA_WARNING_LOG("EAGAIN\n");
- ms2tv(5, &tv);
- } else {
+ if (ret == -EAGAIN)
+ PARA_DEBUG_LOG("EAGAIN\n");
+ else
wn->written += ret * pad->bytes_per_frame;
- ms2tv(pad->buffer_time / 4000, &tv);
+ if (ret == frames) /* we wrote everything, try again immediately */
+ pad->next_chunk = *now;
+ else {
+ ms2tv(pad->buffer_time / pad->bytes_per_frame / 1000, &tv);
+ tv_add(now, &tv, &pad->next_chunk);
}
- tv_add(now, &tv, &pad->next_chunk);
return 1;
}
__malloc static void *alsa_parse_config(const char *options)
{
+ int ret;
struct alsa_write_args_info *conf
= para_calloc(sizeof(struct alsa_write_args_info));
+
PARA_INFO_LOG("options: %s, %zd\n", options, strcspn(options, " \t"));
- int ret = alsa_cmdline_parser_string(options, conf, "alsa_write");
+ ret = alsa_cmdline_parser_string(options, conf, "alsa_write");
if (ret)
goto err_out;
PARA_INFO_LOG("help given: %d\n", conf->help_given);
*/
void alsa_write_init(struct writer *w)
{
+ struct alsa_write_args_info dummy;
+
+ alsa_cmdline_parser_init(&dummy);
w->open = alsa_open;
w->close = alsa_close;
w->pre_select = alsa_write_pre_select;
w->post_select = alsa_write_post_select;
w->parse_config = alsa_parse_config;
w->shutdown = NULL; /* nothing to do */
+ w->help = (struct ggo_help) {
+ .short_help = alsa_write_args_info_help,
+ .detailed_help = alsa_write_args_info_detailed_help
+ };
}
-section "alsa options"
-######################
-
option "device" d
#~~~~~~~~~~~~~~~~
"set PCM device"
- string typestr="device"
- default="default"
- optional
+string typestr="device"
+default="default"
+optional
+details="
+ On systems with dmix, a better choice than the default
+ value might be to use \"plug:swmix\".
+"
option "channels" c
#~~~~~~~~~~~~~~~~~~
-"number of channels (only necessary for raw
-audio)"
-
- int typestr="num"
- default="2"
- optional
+"specify number of channels"
+int typestr="num"
+default="2"
+optional
+details="
+ This option is only necessary for playing raw audio with
+ para_write. In all other cases (plaing wav files with
+ para_write or using this writer with para_audiod), the number
+ of channels will be obtained from other resources.
+"
option "samplerate" s
#~~~~~~~~~~~~~~~~~~~~~
-
-"force given sample rate (only necessary for
-raw audio)"
-
- int typestr="num"
- default="44100"
- optional
+"force given sample rate"
+int typestr="num"
+default="44100"
+optional
+details="
+ Again, it is only necessary to specify this when playing raw
+ audio with para_write.
+"
* Licensed under the GPL v2. For licencing details see COPYING.
*/
-/** \file amp.c Paraslash's amplify filter. */
+/** \file amp_filter.c Paraslash's amplify filter. */
#include "para.h"
#include "amp_filter.cmdline.h"
#include "list.h"
#include "sched.h"
+#include "ggo.h"
#include "filter.h"
#include "string.h"
#include "error.h"
free(fn->buf);
}
-static void *amp_parse_config(int argc, char **argv)
+static int amp_parse_config(int argc, char **argv, void **config)
{
- struct amp_filter_args_info *conf = para_calloc(sizeof(*conf));
+ struct amp_filter_args_info *amp_conf = para_calloc(sizeof(*amp_conf));
+ int ret = -E_AMP_SYNTAX;
- if (amp_cmdline_parser(argc, argv, conf))
+ if (amp_cmdline_parser(argc, argv, amp_conf))
goto err;
- if (conf->amp_arg < 0)
+ ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+ if (amp_conf->amp_arg < 0)
goto err;
- PARA_NOTICE_LOG("amplification: %u (scaling factor: %1.2f)\n", conf->amp_arg,
- conf->amp_arg / 64.0 + 1.0);
- return conf;
+ PARA_NOTICE_LOG("amplification: %u (scaling factor: %1.2f)\n",
+ amp_conf->amp_arg, amp_conf->amp_arg / 64.0 + 1.0);
+ *config = amp_conf;
+ return 1;
err:
- free(conf);
- return NULL;
+ free(amp_conf);
+ return ret;
}
static void amp_open(struct filter_node *fn)
*
* \param f Pointer to the struct to initialize.
*/
-void amp_init(struct filter *f)
+void amp_filter_init(struct filter *f)
{
+ struct amp_filter_args_info dummy;
+
+ amp_cmdline_parser_init(&dummy);
f->open = amp_open;
f->close = amp_close;
f->convert = amp_convert;
- f->print_help = amp_cmdline_parser_print_help;
f->parse_config = amp_parse_config;
+ f->help = (struct ggo_help) {
+ .short_help = amp_filter_args_info_help,
+ .detailed_help = amp_filter_args_info_detailed_help
+ };
}
-section "The amplify filter"
-
option "amp" a
+#~~~~~~~~~~~~~
"amplification value"
int typestr="number"
default="32"
which the amplitude of the audio stream is multiplied. The
formula for the scaling factor is
- factor = 1 + amp / 64
+ factor = 1 + amp / 64.
- amp value scaling factor
- ~~~~~~~~~ ~~~~~~~~~~~~~~
- 0 1
- 32 1.5
- 64 2
- 128 3
+ For example, an amplifiction value of zero results in a
+ scaling factor of one while an amplification value of 64
+ means to double the volume.
"
struct osl_object objs[NUM_ATT_COLUMNS];
struct osl_row *row;
unsigned char bitnum;
- len = strlen(p);
struct addatt_event_data aed;
+ len = strlen(p);
if (!len || p[len - 1] == '-' || p[len - 1] == '+') {
ret2 = para_printf(&pb, "invalid attribute name: %s\n", p);
if (ret2 < 0)
aed.name = p;
aed.bitnum = bitnum;
afs_event(ATTRIBUTE_ADD, &pb, &aed);
- greatest_att_bitnum = PARA_MAX(greatest_att_bitnum, bitnum);
+ greatest_att_bitnum = PARA_MAX(greatest_att_bitnum, (int)bitnum);
}
out:
if (ret < 0 && ret2 >= 0)
#include "audiod.cmdline.h"
#include "list.h"
#include "sched.h"
+#include "ggo.h"
#include "recv.h"
#include "filter.h"
#include "grab_client.cmdline.h"
* \param ll loglevel
* \param fmt the format string
*/
-void para_log(int ll, const char* fmt,...)
+__printf_2_3 void para_log(int ll, const char* fmt,...)
{
va_list argp;
FILE *outfd;
va_end(argp);
}
-static char *configfile_exists(void)
+static void parse_config_or_die(void)
{
- char *home = para_homedir();
- char *config_file = make_message("%s/.paraslash/audiod.conf",
- home);
- free(home);
- if (file_exists(config_file))
- return config_file;
+ char *config_file;
+
+ if (conf.config_file_given)
+ config_file = para_strdup(conf.config_file_arg);
+ else {
+ char *home = para_homedir();
+ config_file = make_message("%s/.paraslash/audiod.conf", home);
+ free(home);
+ }
+ if (conf.config_file_given && !file_exists(config_file)) {
+ PARA_EMERG_LOG("can not read config file %s\n", config_file);
+ goto err;
+ }
+ if (config_file) {
+ struct audiod_cmdline_parser_params params = {
+ .override = 0,
+ .initialize = 0,
+ .check_required = 1,
+ .check_ambiguity = 0,
+ .print_errors = 1
+ };
+ if (audiod_cmdline_parser_config_file(config_file, &conf, ¶ms)) {
+ PARA_EMERG_LOG("parse error in config file\n");
+ goto err;
+ }
+ }
free(config_file);
- return NULL;
+ return;
+err:
+ free(config_file);
+ exit(EXIT_FAILURE);
}
static void setup_signal_handling(void)
struct audio_format_info *a;
init_supported_writers();
- nw = PARA_MAX(1, conf.writer_given);
+ nw = PARA_MAX(1U, conf.writer_given);
PARA_INFO_LOG("maximal number of writers: %d\n", nw);
FOR_EACH_AUDIO_FORMAT(i) {
a = &afi[i];
int i, ret, nf;
filter_init(filters);
- nf = PARA_MAX(1, conf.filter_given);
+ nf = PARA_MAX(1U, conf.filter_given);
PARA_INFO_LOG("maximal number of filters: %d\n", nf);
FOR_EACH_AUDIO_FORMAT(i) {
afi[i].filter_conf = para_malloc(nf * sizeof(void *));
stat_task->offset_seconds = 0;
audiod_status_dump();
stat_task->playing = 0;
- stat_item_values[SI_BASENAME] = make_message(
- "%s: no connection to para_server\n",
- status_item_list[SI_BASENAME]);
- stat_client_write(stat_item_values[SI_BASENAME],
- SI_BASENAME);
}
/**
client_open(argc, argv, &st->ct);
set_stat_task_restart_barrier(5);
}
+ free(stat_item_values[SI_BASENAME]);
+ stat_item_values[SI_BASENAME] = make_message(
+ "%s: no connection to para_server\n",
+ status_item_list[SI_BASENAME]);
+ stat_client_write(stat_item_values[SI_BASENAME],
+ SI_BASENAME);
st->last_status_read = *now;
out:
start_stop_decoders(s);
PARA_WARNING_LOG("invalid mode\n");
}
+__noreturn static void print_help_and_die(void)
+{
+ int d = conf.detailed_help_given;
+ const char **p = d? audiod_args_info_detailed_help
+ : audiod_args_info_help;
+
+ printf_or_die("%s\n\n", AUDIOD_CMDLINE_PARSER_PACKAGE "-"
+ AUDIOD_CMDLINE_PARSER_VERSION);
+ printf_or_die("%s\n\n", audiod_args_info_usage);
+ for (; *p; p++)
+ printf_or_die("%s\n", *p);
+ print_receiver_helps(d);
+ print_filter_helps(d);
+ print_writer_helps(d);
+ exit(0);
+}
+
+
/**
* the main function of para_audiod
*
* */
int main(int argc, char *argv[])
{
- char *config_file;
int ret, i;
static struct sched s;
struct command_task command_task_struct, *cmd_task = &command_task_struct;
audiod_cmdline_parser_ext(argc, argv, &conf, ¶ms);
HANDLE_VERSION_FLAG("audiod", conf);
para_drop_privileges(conf.user_arg, conf.group_arg);
- config_file = configfile_exists();
- if (config_file) {
- params.override = 0;
- params.initialize = 0;
- params.check_required = 1;
- params.check_ambiguity = 0;
- params.print_errors = 1;
- if (audiod_cmdline_parser_config_file(config_file, &conf, ¶ms)) {
- PARA_EMERG_LOG("parse error in config file\n");
- exit(EXIT_FAILURE);
- }
- free(config_file);
- }
+ parse_config_or_die();
if (conf.logfile_given)
logfile = open_log(conf.logfile_arg);
- log_welcome("para_audiod", conf.loglevel_arg);
i = init_stream_io();
+ if (conf.help_given || conf.detailed_help_given)
+ print_help_and_die();
if (i < 0) {
PARA_EMERG_LOG("init stream io error: %s\n", para_strerror(-i));
exit(EXIT_FAILURE);
}
+ log_welcome("para_audiod", conf.loglevel_arg);
server_uptime(UPTIME_SET);
set_initial_status();
FOR_EACH_SLOT(i)
default="4"
optional
+option "config_file" c
+#~~~~~~~~~~~~~~~~~~~~~
+"(default='~/.paraslash/audiod.conf'"
+string typestr="filename"
+optional
+
option "logfile" L
#~~~~~~~~~~~~~~~~~
"where to write log output"
option "user_allow" -
#~~~~~~~~~~~~~~~~~~~~
-"allow thus uid"
+"allow this uid"
int typestr="uid"
default="-1"
optional
#include "list.h"
#include "close_on_fork.h"
#include "sched.h"
+#include "ggo.h"
#include "filter.h"
#include "grab_client.cmdline.h"
#include "grab_client.h"
/** Structure passed to the \p print_blob function. */
struct lsblob_action_data {
- /* The flags given at the command line. */
+ /** The flags given at the command line. */
uint32_t flags;
/** Message buffer. */
struct para_buffer pb;
s.default_timeout.tv_sec = 1;
s.default_timeout.tv_usec = 0;
ret = client_open(argc, argv, &ct);
- if (ret < 0) /* can not use PARA_LOG here */
+ if (ret < 0) /* can not use PARA_LOG here because ct is NULL */
exit(EXIT_FAILURE);
register_task(&svt);
ret = schedule(&s);
# file client.conf
+args "--no-handle-error"
option "hostname" i "ip or host to connect" string typestr="host" default="localhost" optional
option "user" u "paraslash username" string typestr="username" default="<current user>" optional
option "server_port" p "port to connect" int typestr="port" default="2990" optional
*ct_ptr = ct;
ct->fd = -1;
- ret = client_cmdline_parser(argc, argv, &ct->conf);
+ ret = -E_CLIENT_SYNTAX;
+ if (client_cmdline_parser(argc, argv, &ct->conf))
+ goto out;
HANDLE_VERSION_FLAG("client", ct->conf);
ret = -E_CLIENT_SYNTAX;
if (!ct->conf.inputs_num)
.override = 0,
.initialize = 0,
.check_required = 0,
- .check_ambiguity = 0
+ .check_ambiguity = 0,
+ .print_errors = 0
};
- client_cmdline_parser_config_file(ct->config_file,
- &ct->conf, ¶ms);
+ ret = -E_BAD_CONFIG;
+ if (client_cmdline_parser_config_file(ct->config_file,
+ &ct->conf, ¶ms))
+ goto out;
}
ret = 1;
PARA_INFO_LOG("loglevel: %d\n", ct->conf.loglevel_arg);
}
return ret;
}
-
+++ /dev/null
-/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file compress.c Paraslash's dynamic audio range compressor. */
-
-/*
- * Uses ideas of AudioCompress, (C) 2002-2004 M. Hari Nezumi <magenta@trikuare.cx>
- */
-
-#include "para.h"
-#include "compress_filter.cmdline.h"
-#include "list.h"
-#include "sched.h"
-#include "filter.h"
-#include "string.h"
-
-/** The size of the output data buffer. */
-#define COMPRESS_CHUNK_SIZE 40960
-
-extern char *stat_item_values[NUM_STAT_ITEMS];
-
-/** Data specific to the compress filter. */
-struct private_compress_data {
- /** The current multiplier. */
- unsigned current_gain;
- /** Points to the configuration data for this instance of the compress filter. */
- struct compress_filter_args_info *conf;
- /** Maximal admissible gain. */
- unsigned max_gain;
- /** Number of samples already seen. */
- unsigned num_samples;
- /** Absolute value of the maximal sample in the current block. */
- unsigned peak;
-};
-
-static ssize_t compress(char *inbuf, size_t inbuf_len, struct filter_node *fn)
-{
- size_t i, length = PARA_MIN((inbuf_len / 2) * 2,
- (fn->bufsize - fn->loaded) / 2 * 2);
- struct private_compress_data *pcd = fn->private_data;
- int16_t *ip = (int16_t *)inbuf, *op = (int16_t *)(fn->buf + fn->loaded);
- unsigned gain_shift = pcd->conf->inertia_arg + pcd->conf->damp_arg,
- mask = (1 << pcd->conf->blocksize_arg) - 1;
-
- if (!length)
- return 0;
- for (i = 0; i < length / 2; i++) {
- /* be careful in that heat, my dear */
- int sample = *ip++, adjusted_sample = (PARA_ABS(sample) *
- pcd->current_gain) >> gain_shift;
- if (unlikely(adjusted_sample > 32767)) { /* clip */
- PARA_NOTICE_LOG("clip: sample: %d, adjusted sample: %d\n",
- sample, adjusted_sample);
- adjusted_sample = 32767;
- pcd->current_gain = (3 * pcd->current_gain +
- (1 << pcd->conf->inertia_arg)) / 4;
- pcd->peak = 0;
- } else
- pcd->peak = PARA_MAX(pcd->peak, adjusted_sample);
- *op++ = sample >= 0? adjusted_sample : -adjusted_sample;
- if (likely(++pcd->num_samples & mask))
- continue;
-// PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain,
-// pcd->peak);
- if (pcd->peak < pcd->conf->target_level_arg) {
- if (pcd->current_gain < pcd->max_gain)
- pcd->current_gain++;
- } else
- pcd->current_gain = PARA_MAX(pcd->current_gain - 2,
- 1 << pcd->conf->inertia_arg);
- pcd->peak = 0;
- }
- fn->loaded += length;
- return length;
-}
-
-static void close_compress(struct filter_node *fn)
-{
- free(fn->private_data);
- free(fn->buf);
-}
-
-static void *compress_parse_config(int argc, char **argv)
-{
- struct compress_filter_args_info *ret = para_calloc(sizeof(struct compress_filter_args_info));
- if (!compress_cmdline_parser(argc, argv, ret))
- return ret;
- free(ret);
- return NULL;
-}
-
-static void open_compress(struct filter_node *fn)
-{
- struct private_compress_data *pcd = para_calloc(
- sizeof(struct private_compress_data));
- pcd->conf = fn->conf;
- fn->private_data = pcd;
- fn->bufsize = COMPRESS_CHUNK_SIZE;
- fn->buf = para_malloc(fn->bufsize);
- pcd->current_gain = 1 << pcd->conf->inertia_arg;
- pcd->max_gain = 1 << (pcd->conf->inertia_arg + pcd->conf->aggressiveness_arg);
-}
-
-/**
- * The init function of the compress filter.
- *
- * \param f Pointer to the struct to initialize.
- */
-void compress_init(struct filter *f)
-{
- f->open = open_compress;
- f->close = close_compress;
- f->convert = compress;
- f->print_help = compress_cmdline_parser_print_help;
- f->parse_config = compress_parse_config;
-}
--- /dev/null
+/*
+ * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file compress_filter.c Paraslash's dynamic audio range compressor. */
+
+/*
+ * Uses ideas of AudioCompress, (C) 2002-2004 M. Hari Nezumi <magenta@trikuare.cx>
+ */
+
+#include "para.h"
+#include "compress_filter.cmdline.h"
+#include "list.h"
+#include "sched.h"
+#include "ggo.h"
+#include "filter.h"
+#include "string.h"
+#include "error.h"
+
+/** The size of the output data buffer. */
+#define COMPRESS_CHUNK_SIZE 40960
+
+extern char *stat_item_values[NUM_STAT_ITEMS];
+
+/** Data specific to the compress filter. */
+struct private_compress_data {
+ /** The current multiplier. */
+ unsigned current_gain;
+ /** Points to the configuration data for this instance of the compress filter. */
+ struct compress_filter_args_info *conf;
+ /** Maximal admissible gain. */
+ unsigned max_gain;
+ /** Number of samples already seen. */
+ unsigned num_samples;
+ /** Absolute value of the maximal sample in the current block. */
+ int peak;
+};
+
+static ssize_t compress(char *inbuf, size_t inbuf_len, struct filter_node *fn)
+{
+ size_t i, length = PARA_MIN((inbuf_len / 2) * 2,
+ (fn->bufsize - fn->loaded) / 2 * 2);
+ struct private_compress_data *pcd = fn->private_data;
+ int16_t *ip = (int16_t *)inbuf, *op = (int16_t *)(fn->buf + fn->loaded);
+ unsigned gain_shift = pcd->conf->inertia_arg + pcd->conf->damp_arg,
+ mask = (1 << pcd->conf->blocksize_arg) - 1;
+
+ if (!length)
+ return 0;
+ for (i = 0; i < length / 2; i++) {
+ /* be careful in that heat, my dear */
+ int sample = *ip++, adjusted_sample = (PARA_ABS(sample) *
+ pcd->current_gain) >> gain_shift;
+ if (unlikely(adjusted_sample > 32767)) { /* clip */
+ PARA_NOTICE_LOG("clip: sample: %d, adjusted sample: %d\n",
+ sample, adjusted_sample);
+ adjusted_sample = 32767;
+ pcd->current_gain = (3 * pcd->current_gain +
+ (1 << pcd->conf->inertia_arg)) / 4;
+ pcd->peak = 0;
+ } else
+ pcd->peak = PARA_MAX(pcd->peak, adjusted_sample);
+ *op++ = sample >= 0? adjusted_sample : -adjusted_sample;
+ if (likely(++pcd->num_samples & mask))
+ continue;
+// PARA_DEBUG_LOG("gain: %u, peak: %u\n", pcd->current_gain,
+// pcd->peak);
+ if (pcd->peak < pcd->conf->target_level_arg) {
+ if (pcd->current_gain < pcd->max_gain)
+ pcd->current_gain++;
+ } else
+ pcd->current_gain = PARA_MAX(pcd->current_gain - 2,
+ 1U << pcd->conf->inertia_arg);
+ pcd->peak = 0;
+ }
+ fn->loaded += length;
+ return length;
+}
+
+static void close_compress(struct filter_node *fn)
+{
+ free(fn->private_data);
+ free(fn->buf);
+}
+
+/** TODO: Add sanity checks */
+static int compress_parse_config(int argc, char **argv, void **config)
+{
+ int ret;
+ struct compress_filter_args_info *compress_conf
+ = para_calloc(sizeof(*compress_conf));
+
+ ret = -E_COMPRESS_SYNTAX;
+ if (compress_cmdline_parser(argc, argv, compress_conf))
+ goto err;
+ *config = compress_conf;
+ return 1;
+err:
+ free(compress_conf);
+ return ret;
+}
+
+static void open_compress(struct filter_node *fn)
+{
+ struct private_compress_data *pcd = para_calloc(
+ sizeof(struct private_compress_data));
+ pcd->conf = fn->conf;
+ fn->private_data = pcd;
+ fn->bufsize = COMPRESS_CHUNK_SIZE;
+ fn->buf = para_malloc(fn->bufsize);
+ pcd->current_gain = 1 << pcd->conf->inertia_arg;
+ pcd->max_gain = 1 << (pcd->conf->inertia_arg + pcd->conf->aggressiveness_arg);
+}
+
+/**
+ * The init function of the compress filter.
+ *
+ * \param f Pointer to the struct to initialize.
+ */
+void compress_filter_init(struct filter *f)
+{
+ struct compress_filter_args_info dummy;
+
+ compress_cmdline_parser_init(&dummy);
+ f->open = open_compress;
+ f->close = close_compress;
+ f->convert = compress;
+ f->parse_config = compress_parse_config;
+ f->help = (struct ggo_help) {
+ .short_help = compress_filter_args_info_help,
+ .detailed_help = compress_filter_args_info_detailed_help
+ };
+}
-section "The dynamic audio range compressor"
+option "blocksize" b
+#~~~~~~~~~~~~~~~~~~~
+"adjust block size"
+int typestr="number"
+default="15"
+optional
+details = "
+ Larger blocksize means fewer volume adjustments per time unit.
+"
-option "blocksize" b "larger blocksize means fewer volume adjustments per time unit" int typestr="number" default="15" optional
-option "aggressiveness" a "controls the maximum amount to amplify by" int typestr="number" default="4" optional
-option "inertia" i "how much inertia ramping has" int typestr="number" default="6" optional
-option "target_level" t "target signal level (0-32768)" int typestr="number" default="20000" optional
-option "damp" d "if non-zero, scale down after normalizing" int typestr="number" default="0" optional
+option "aggressiveness" a
+#~~~~~~~~~~~~~~~~~~~~~~~~
+ "controls the maximum amount to amplify by"
+int typestr="number"
+default="4"
+optional
+
+option "inertia" i
+#~~~~~~~~~~~~~~~~~
+ "how much inertia ramping has"
+ int typestr="number"
+default="6"
+optional
+
+option "target_level" t
+#~~~~~~~~~~~~~~~~~~~~~~
+"target signal level (0-32768)"
+int typestr="number"
+default="20000"
+optional
+
+option "damp" d
+#~~~~~~~~~~~~~~
+"if non-zero, scale down after normalizing"
+int typestr="number"
+default="0"
+optional
all_errlist_objs="server mp3_afh afh_common vss command net string signal time
daemon stat crypt http_send close_on_fork ipc acl afh fade amp_filter
dccp_send fd user_list chunk_queue afs osl aft mood score attribute blob ringbuffer
-playlist sha1 rbtree sched audiod grab_client filter_chain wav compress
+playlist sha1 rbtree sched audiod grab_client filter_common wav_filter compress_filter
http_recv dccp_recv recv_common write_common file_write audiod_command
-client_common recv stdout filter stdin audioc write client fsck exec send_common"
-all_executables="server recv filter audioc write client fsck afh amp_filter"
+client_common recv stdout filter stdin audioc write client fsck exec send_common ggo"
+
+all_executables="server recv filter audioc write client fsck afh"
recv_cmdline_objs="recv.cmdline http_recv.cmdline dccp_recv.cmdline"
recv_errlist_objs="http_recv recv_common recv time string net dccp_recv
- fd sched stdout"
+ fd sched stdout ggo"
recv_ldflags=""
receivers=" http dccp"
senders=" http dccp"
filter_cmdline_objs="filter.cmdline compress_filter.cmdline amp_filter.cmdline"
-filter_errlist_objs="filter_chain wav compress filter string stdin stdout sched fd amp_filter"
+filter_errlist_objs="filter_common wav_filter compress_filter filter string stdin stdout sched fd amp_filter ggo"
filter_ldflags=""
filters=" compress wav amp"
http_recv.cmdline dccp_recv.cmdline file_write.cmdline client.cmdline
audiod_command_list amp_filter.cmdline"
audiod_errlist_objs="audiod signal string daemon stat net
- time grab_client filter_chain wav compress amp_filter http_recv dccp_recv
+ time grab_client filter_common wav_filter compress_filter amp_filter http_recv dccp_recv
recv_common fd sched write_common file_write audiod_command crypt
- client_common"
+ client_common ggo"
audiod_ldflags=""
audiod_audio_formats=""
server_audio_formats=" mp3"
write_cmdline_objs="write.cmdline file_write.cmdline"
-write_errlist_objs="write write_common file_write time fd string sched stdin"
+write_errlist_objs="write write_common file_write time fd string sched stdin ggo"
write_ldflags=""
writers=" file"
default_writer="FILE_WRITE"
AC_CHECK_LIB([vorbis], [vorbis_info_init], [], [ have_ogg="no" ])
AC_CHECK_HEADERS([ogg/ogg.h vorbis/codec.h], [], [ have_ogg="no" ])
if test "$have_ogg" = "yes"; then
- all_errlist_objs="$all_errlist_objs oggdec ogg_afh"
+ all_errlist_objs="$all_errlist_objs oggdec_filter ogg_afh"
AC_DEFINE(HAVE_OGGVORBIS, 1, define to 1 to turn on ogg vorbis support)
filters="$filters oggdec"
if test "$OSTYPE" = "Darwin"; then
audiod_cmdline_objs="$audiod_cmdline_objs oggdec_filter.cmdline"
server_errlist_objs="$server_errlist_objs ogg_afh"
- filter_errlist_objs="$filter_errlist_objs oggdec"
- audiod_errlist_objs="$audiod_errlist_objs oggdec"
+ filter_errlist_objs="$filter_errlist_objs oggdec_filter"
+ audiod_errlist_objs="$audiod_errlist_objs oggdec_filter"
afh_errlist_objs="$afh_errlist_objs ogg_afh"
audiod_audio_formats="ogg"
server_audio_formats="$server_audio_formats ogg"
- filter_filters="$filter_filters oggdec"
AC_SUBST(oggvorbis_cppflags)
AC_SUBST(oggvorbis_libs)
else
AC_CHECK_LIB([faad], [NeAACDecOpen], [], have_faad=no)
if test "$have_faad" = "yes"; then
AC_DEFINE(HAVE_FAAD, 1, define to 1 if you want to build the aacdec filter)
- all_errlist_objs="$all_errlist_objs aac_common aacdec aac_afh"
- filter_errlist_objs="$filter_errlist_objs aacdec aac_common"
+ all_errlist_objs="$all_errlist_objs aac_common aacdec_filter aac_afh"
+ filter_errlist_objs="$filter_errlist_objs aacdec_filter aac_common"
afh_errlist_objs="$afh_errlist_objs aac_common aac_afh"
- filter_filters="$filter_filters aacdec"
- audiod_errlist_objs="$audiod_errlist_objs aacdec aac_common"
+ audiod_errlist_objs="$audiod_errlist_objs aacdec_filter aac_common"
server_errlist_objs="$server_errlist_objs aac_afh aac_common"
server_ldflags="$server_ldflags $faad_libs -lfaad"
filter_ldflags="$filter_ldflags $faad_libs -lfaad"
])
if test "$have_mad" = "yes"; then
AC_DEFINE(HAVE_MAD, 1, define to 1 if you want to build the mp3dec filter)
- all_errlist_objs="$all_errlist_objs mp3dec"
- filter_errlist_objs="$filter_errlist_objs mp3dec"
- audiod_errlist_objs="$audiod_errlist_objs mp3dec"
+ filter_cmdline_objs="$filter_cmdline_objs mp3dec_filter.cmdline"
+ audiod_cmdline_objs="$audiod_cmdline_objs mp3dec_filter.cmdline"
+ all_errlist_objs="$all_errlist_objs mp3dec_filter"
+ filter_errlist_objs="$filter_errlist_objs mp3dec_filter"
+ audiod_errlist_objs="$audiod_errlist_objs mp3dec_filter"
filter_ldflags="$filter_ldflags $mad_libs -lmad"
audiod_ldflags="$audiod_ldflags $mad_libs -lmad"
audiod_audio_formats="$audiod_audio_formats mp3"
AC_DEFINE_UNQUOTED(INIT_FADE_ERRLISTS,
objlist_to_errlist($fade_errlist_objs), errors used by para_fade)
+
+enum="$(for i in $filters; do printf "${i}_FILTER, " | tr '[a-z]' '[A-Z]'; done)"
+AC_DEFINE_UNQUOTED(FILTER_ENUM, $enum NUM_SUPPORTED_FILTERS,
+ enum of supported filters)
+inits="$(for i in $filters; do printf 'extern void '$i'_filter_init(struct filter *f); '; done)"
+AC_DEFINE_UNQUOTED(DECLARE_FILTER_INITS, $inits, init functions of the supported filters)
+array="$(for i in $filters; do printf '{.name = \"'$i'\", .init = '$i'_filter_init},'; done)"
+AC_DEFINE_UNQUOTED(FILTER_ARRAY, $array, array of supported filters)
+
enum="$(for i in $writers; do printf "${i}_WRITE, " | tr '[a-z]' '[A-Z]'; done)"
AC_DEFINE_UNQUOTED(WRITER_ENUM, $enum NUM_SUPPORTED_WRITERS,
enum of supported writers)
AC_DEFINE_UNQUOTED(DECLARE_WRITER_INITS, $inits, init functions of the supported writers)
array="$(for i in $writers; do printf '{.init = '$i'_write_init},'; done)"
AC_DEFINE_UNQUOTED(WRITER_ARRAY, $array, array of supported writers)
+
enum="$(for i in $audiod_audio_formats; do printf "AUDIO_FORMAT_${i}, " | tr '[a-z]' '[A-Z]'; done)"
AC_DEFINE_UNQUOTED(AUDIOD_AUDIO_FORMATS_ENUM, $enum NUM_AUDIO_FORMATS,
enum of audio formats supported by audiod)
#include "error.h"
#include "list.h"
#include "sched.h"
+#include "ggo.h"
#include "recv.h"
#include "string.h"
#include "net.h"
*/
void dccp_recv_init(struct receiver *r)
{
+ struct dccp_recv_args_info dummy;
+
+ dccp_recv_cmdline_parser_init(&dummy);
r->shutdown = dccp_shutdown;
r->open = dccp_recv_open;
r->close = dccp_recv_close;
r->pre_select = dccp_recv_pre_select;
r->post_select = dccp_recv_post_select;
r->parse_config = dccp_recv_parse_config;
+ r->help = (struct ggo_help) {
+ .short_help = dccp_recv_args_info_help,
+ .detailed_help = dccp_recv_args_info_detailed_help
+ };
}
-section "options of the dccp receiver"
-option "host" i "ip or host" string default="localhost" optional
-option "port" p "port to connect to" int default="8000" optional
+option "host" i
+"ip or host"
+string default="localhost"
+optional
+details="
+ Both IPv4 and IPv6 addresses are supported.
+"
+
+option "port" p
+"port to connect to"
+int
+default="8000"
+optional
/* these do not need error handling (yet) */
#define SERVER_ERRORS
-#define WAV_ERRORS
-#define COMPRESS_ERRORS
+#define WAV_FILTER_ERRORS
#define TIME_ERRORS
#define CLOSE_ON_FORK_ERRORS
#define DAEMON_ERRORS
#define SHA1_ERRORS
#define RBTREE_ERRORS
#define RECV_ERRORS
-#define SEND_COMMON_ERRORS
#define STDOUT_ERRORS
#define IPC_ERRORS
-#define AMP_FILTER_ERRORS
-
+#define DCCP_SEND_ERRORS
+#define HTTP_SEND_ERRORS
+#define GGO_ERRORS
extern const char **para_errlist[];
+#define COMPRESS_FILTER_ERRORS \
+ PARA_ERROR(COMPRESS_SYNTAX, "syntax error in compress filter config"), \
+
+
+#define AMP_FILTER_ERRORS \
+ PARA_ERROR(AMP_SYNTAX, "syntax error in amp filter config"), \
+
+
+#define SEND_COMMON_ERRORS \
+ PARA_ERROR(MAX_CLIENTS, "maximal number of clients exceeded"), \
+
+
#define FADE_ERRORS \
PARA_ERROR(FADE_SYNTAX, "fade syntax error"), \
PARA_ERROR(MOOD_SYNTAX, "mood syntax error"), \
PARA_ERROR(NO_MOOD, "no mood available"), \
PARA_ERROR(NOT_ADMISSIBLE, "file is not admissible"), \
+ PARA_ERROR(DUMMY_ROW, "attempted to access blob dummy object"), \
#define ATTRIBUTE_ERRORS \
#define BLOB_ERRORS \
PARA_ERROR(BLOB_SYNTAX, "blob syntax error"), \
- PARA_ERROR(DUMMY_ROW, "attempted to access blob dummy object"), \
#define PLAYLIST_ERRORS \
PARA_ERROR(CLIENT_SYNTAX, "syntax error"), \
PARA_ERROR(INVALID_CHALLENGE, "did not receive valid challenge"), \
PARA_ERROR(NO_CONFIG, "config file not found"), \
+ PARA_ERROR(BAD_CONFIG, "syntax error in config file"), \
PARA_ERROR(CLIENT_AUTH, "authentication failed"), \
PARA_ERROR(SERVER_EOF, "connection closed by para_server"), \
PARA_ERROR(INVALID_AUDIOD_CMD, "invalid command"), \
-#define FILTER_CHAIN_ERRORS \
+#define FILTER_COMMON_ERRORS \
PARA_ERROR(UNSUPPORTED_FILTER, "given filter not supported"), \
PARA_ERROR(BAD_FILTER_OPTIONS, "invalid filter option given"), \
PARA_ERROR(FC_EOF, "filter chain: eof"), \
PARA_ERROR(UNKNOWN_STAT_ITEM, "status item not recognized"), \
-#define OGGDEC_ERRORS \
+#define OGGDEC_FILTER_ERRORS \
PARA_ERROR(OGGDEC_READ, "read from media returned an error"), \
PARA_ERROR(OGGDEC_NOTVORBIS, "bitstream is not vorbis data"), \
PARA_ERROR(OGGDEC_VERSION, "vorbis version mismatch"), \
PARA_ERROR(OGGDEC_BADHEADER, "invalid vorbis bitstream header"), \
PARA_ERROR(OGGDEC_FAULT, "bug or heap/stack corruption"), \
PARA_ERROR(OGGDEC_BADLINK, "invalid stream section or requested link corrupt"), \
+ PARA_ERROR(OGGDEC_SYNTAX, "syntax error in oggdec config"), \
#define GRAB_CLIENT_ERRORS \
PARA_ERROR(GC_VERSION_GIVEN, ""), /* not really an error */ \
-#define MP3DEC_ERRORS \
+#define MP3DEC_FILTER_ERRORS \
PARA_ERROR(MAD_FRAME_DECODE, "mad frame decode error"), \
PARA_ERROR(MP3DEC_OVERRUN, "mp3 output buffer overrun"), \
+ PARA_ERROR(MP3DEC_SYNTAX, "syntax error in mp3dec config"), \
#define FILTER_ERRORS \
#define VSS_ERRORS \
- PARA_ERROR(CHUNK, "unable to get chunk"), \
PARA_ERROR(NOFD, "did not receive open fd from afs"), \
PARA_ERROR(CHALLENGE, "failed to read challenge"), \
-#define HTTP_SEND_ERRORS \
- PARA_ERROR(MAX_CLIENTS, "maximal number of clients exceeded"), \
-
-
#define COMMAND_ERRORS \
PARA_ERROR(COMMAND_SYNTAX, "syntax error in command"), \
PARA_ERROR(AUTH, "did not receive auth request"), \
PARA_ERROR(DCCP_OVERRUN, "dccp output buffer buffer overrun"), \
-#define DCCP_SEND_ERRORS \
-
-
#define FD_ERRORS \
PARA_ERROR(FGETS, "fgets error"), \
PARA_ERROR(EMPTY, "file empty"), \
PARA_ERROR(WRITE_COMMON_SYNTAX, "syntax error in write option"), \
-#define AACDEC_ERRORS \
+#define AACDEC_FILTER_ERRORS \
PARA_ERROR(AACDEC_INIT, "failed to init aac decoder"), \
PARA_ERROR(AAC_DECODE, "aac decode error"), \
PARA_ERROR(AAC_OVERRUN, "aac output buffer overrun"), \
if (fds[2] > 0 && pipe(err) < 0)
goto err_out;
if (!fds[0] || !fds[1] || !fds[2]) {
- ret = para_open("/dev/null", O_RDONLY, 42);
+ ret = para_open("/dev/null", O_RDWR, 42);
if (ret < 0)
goto err_out;
null = ret;
char **argv;
char *tmp = para_strdup(cmdline);
- if (!tmp)
- exit(EXIT_FAILURE);
argc = split_args(tmp, &argv, " \t");
ret = para_exec(pid, argv[0], argv, fds);
free(argv);
INIT_FADE_ERRLISTS;
struct fade_args_info conf;
-void para_log(__a_unused int ll, const char *fmt,...)
+__printf_2_3 void para_log(__a_unused int ll, const char *fmt, ...)
{
va_list argp;
time_t t1;
option "wake_min" M
#~~~~~~~~~~~~~~~~~~
"(0-59)"
- int typestr="seconds"
+ int typestr="minutes"
default="0"
optional
if (*dir)
return 1;
ret = -ERRNO_TO_PARA_ERROR(errno);
-/* Ignore return value of fchdir() and close(). We're busted anyway. */
- if (cwd)
- fchdir(*cwd);
+ /* Ignore return value of fchdir() and close(). We're busted anyway. */
+ if (cwd) {
+ int __a_unused ret2 = fchdir(*cwd); /* STFU, gcc */
+ }
close_cwd:
if (cwd)
close(*cwd);
#include "para.h"
#include "list.h"
#include "sched.h"
+#include "ggo.h"
#include "write.h"
#include "string.h"
#include "fd.h"
__malloc static void *file_write_parse_config(const char *options)
{
- PARA_INFO_LOG("options: %s\n", options);
struct file_write_args_info *conf
= para_calloc(sizeof(struct file_write_args_info));
int ret = file_cmdline_parser_string(options, conf, "file_write");
+
PARA_INFO_LOG("conf->filename_given: %d\n", conf->filename_given);
if (!ret)
return conf;
/** the init function of the file writer */
void file_write_init(struct writer *w)
{
+ struct file_write_args_info dummy;
+
+ file_cmdline_parser_init(&dummy);
w->open = file_write_open;
w->pre_select = file_write_pre_select;
w->post_select = file_write_post_select;
w->parse_config = file_write_parse_config;
w->close = file_write_close;
w->shutdown = NULL; /* nothing to do */
+ w->help = (struct ggo_help) {
+ .short_help = file_write_args_info_help,
+ .detailed_help = file_write_args_info_detailed_help
+ };
}
-section "file writer options"
-
option "filename" f
#~~~~~~~~~~~~~~~~~~
-
-"select output file name. Defaults to a
-random filename in ~/.paraslash."
-
- string typestr="filename"
- optional
+"specify output file name"
+string typestr="filename"
+optional
+details="
+ Defaults to a random filename in ~/.paraslash.
+"
#include "filter.cmdline.h"
#include "list.h"
#include "sched.h"
+#include "ggo.h"
#include "filter.h"
#include "string.h"
#include "stdin.h"
return ret;
}
+__noreturn static void print_help_and_die(void)
+{
+ int d = conf.detailed_help_given;
+ const char **p = d? filter_args_info_detailed_help
+ : filter_args_info_help;
+
+ printf_or_die("%s\n\n", FILTER_CMDLINE_PARSER_PACKAGE "-"
+ FILTER_CMDLINE_PARSER_VERSION);
+ printf_or_die("%s\n\n", filter_args_info_usage);
+ for (; *p; p++)
+ printf_or_die("%s\n", *p);
+ print_filter_helps(d);
+ exit(0);
+}
+
static int parse_config(int argc, char *argv[])
{
static char *cf; /* config file */
struct stat statbuf;
- int i;
if (filter_cmdline_parser(argc, argv, &conf))
return -E_FILTER_SYNTAX;
HANDLE_VERSION_FLAG("filter", conf);
+ if (conf.help_given || conf.detailed_help_given)
+ print_help_and_die();
if (!cf) {
char *home = para_homedir();
cf = make_message("%s/.paraslash/filter.conf", home);
if (filter_cmdline_parser_config_file(cf, &conf, ¶ms))
return -E_FILTER_SYNTAX;
}
- if (!conf.list_filters_given)
- return 1;
- printf("available filters: ");
- for (i = 0; filters[i].name; i++)
- printf("%s%s%s", i? " " : "", filters[i].name,
- filters[i].parse_config? "*": "");
- printf("\nFilters marked with \"*\" have further command line options. Try\n"
- "\tpara_filter -f '<filtername> -h'\nfor more information.\n");
- exit(EXIT_SUCCESS);
+ return 1;
}
/**
ret = init_filter_chain();
if (ret < 0)
goto out;
+ sit->output_error = &fc->task.error;
stdout_set_defaults(sot);
sot->buf = fc->outbuf;
-option "loglevel" l "set loglevel (0-6)" int typestr="level" default="4" optional
-option "filter" f "Specify filter.
+option "loglevel" l
+#~~~~~~~~~~~~~~~~~~
+ "set loglevel (0-6)"
+int typestr="level"
+default="4"
+optional
-May be given multiple times to 'pipe' the stream
-through arbitrary many filters in an efficient
-way. The same filter may appear more than once,
-order matters.
+option "filter" f
+#~~~~~~~~~~~~~~~~
+"Specify filter."
+string typestr="filter_spec"
+optional
+multiple
+details="
+ May be given multiple times to 'pipe' the stream through
+ arbitrary many filters in an efficient way. The same filter
+ may appear more than once, order matters.
-Filter options may be specified for each '-f'
-option separately. Note that you will have to
-quote these options like this:
+ Filter options may be specified for each '-f' option
+ separately. Note that you will have to quote these options
+ like this:
- -f 'compress --inertia 5 --damp 2'
+ -f 'compress --inertia 5 --damp 2'
"
-string typestr="filter_spec" optional multiple
-
-option "list_filters" L "print list of available filters and exit" flag off
* Licensed under the GPL v2. For licencing details see COPYING.
*/
-/** \file filter.h Filter-related structures and exported symbols from filter_chain.c. */
+/** \file filter.h Filter-related structures and exported symbols from filter_common.c. */
+/** The list of supported filters. */
+enum filter_enum {FILTER_ENUM};
/**
* Describes one running instance of a filter.
/** Describes one running instance of a chain of filters */
struct filter_chain {
+ /** The length of the filter chain. */
unsigned int num_filters;
/**
* The number of channels of the current stream.
* Note: As several instances of the same filter may be running at the same
* time, all these filter functions must be reentrant; no static non-constant
* variables may be used.
- * \sa mp3dec.c, oggdec.c, wav.c, compress.c, filter_node
+ * \sa mp3dec_filter.c, oggdec_filter.c, wav_filter.c, compress_filter.c, filter_node
*/
struct filter {
/** The name of the filter. */
* by the open() function.
*/
void (*close)(struct filter_node *fn);
- /**
- * Print the help text for this filter and exit.
- *
- * This is optional and it is not necessary to initialize this pointer if
- * the filter does not have a help text.
- */
- void (*print_help)(void);
/**
* A pointer to the filter's command line parser.
*
- * If this optional function pointer is not NULL, any filter options are passed
- * from the main program to this command line parser once at application
- * startup. The command line parser should check its command line options given
- * by \a argc and \a argv and abort on errors. On success, it should return a
- * pointer to the filter-specific configuration data determined by \a argc and
- * \a argv.
+ * If this optional function pointer is not NULL, any filter options
+ * are passed from the main program to this command line parser once at
+ * application startup. The command line parser should check its
+ * command line options given by \a argc and \a argv and abort on
+ * errors. Success must be indicated by a non-negative return value. In
+ * this case the function should return a pointer to the
+ * filter-specific configuration data determined by \a argc and \a
+ * argv. On failure, a negative paraslash error code must be returned.
*/
- void *(*parse_config)(int argc, char **argv);
+ int (*parse_config)(int argc, char **argv, void **config);
+
+ struct ggo_help help;
};
void close_filters(struct filter_chain *fc);
void filter_init(struct filter *all_filters);
int check_filter_arg(char *filter_arg, void **conf);
void filter_pre_select(__a_unused struct sched *s, struct task *t);
-
+void print_filter_helps(int detailed);
static inline void write_int16_host_endian(char *buf, int val)
{
#endif
}
+DECLARE_FILTER_INITS
-/** \cond */
-extern struct filter filters[];
-#define DECLARE_EXTERN_FILTER_INIT(name) \
- extern void name ## _init(struct filter *f)
-
-#define FILTER_INIT(filter) { \
- .name = #filter, \
- .init = filter ## _init, \
- .parse_config = NULL, \
- .print_help = NULL \
-},
-
-/* filters that are always present */
-DECLARE_EXTERN_FILTER_INIT(wav);
-DECLARE_EXTERN_FILTER_INIT(compress);
-DECLARE_EXTERN_FILTER_INIT(amp);
-
-/* next the optional filters */
-#ifdef HAVE_MAD
-DECLARE_EXTERN_FILTER_INIT(mp3dec);
-#define MP3DEC_FILTER FILTER_INIT(mp3dec)
-#else
-#define MP3DEC_FILTER
-#endif
-
-#ifdef HAVE_FAAD
-DECLARE_EXTERN_FILTER_INIT(aacdec);
-#define AACDEC_FILTER FILTER_INIT(aacdec)
-#else
-#define AACDEC_FILTER
-#endif
-
-#ifdef HAVE_OGGVORBIS
-DECLARE_EXTERN_FILTER_INIT(oggdec);
-#define OGGDEC_FILTER FILTER_INIT(oggdec)
-#else
-#define OGGDEC_FILTER
-#endif
-/** \endcond */
-
-/** define an array of all available filters */
-#define DEFINE_FILTER_ARRAY(filters) struct filter filters[] = { \
- FILTER_INIT(wav) \
- FILTER_INIT(compress) \
- FILTER_INIT(amp) \
- MP3DEC_FILTER \
- AACDEC_FILTER \
- OGGDEC_FILTER \
- { .name = NULL } };
-
+#define FOR_EACH_SUPPORTED_FILTER(j) for (j = 0; j < NUM_SUPPORTED_FILTERS; j++)
+/** The filter array, one structure for each supported filter. */
+extern struct filter filters[NUM_SUPPORTED_FILTERS];
+++ /dev/null
-/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file filter_chain.c Common helper functions for filter input/output. */
-
-#include <sys/types.h>
-#include <dirent.h>
-
-#include "para.h"
-#include "list.h"
-#include "sched.h"
-#include "fd.h"
-#include "filter.h"
-#include "error.h"
-#include "string.h"
-
-DEFINE_FILTER_ARRAY(filters);
-
-/**
- * Call the init function of each supported filter.
- *
- * \param all_filters the array of all supported filters.
- *
- * \sa filter::init
- */
-void filter_init(struct filter *all_filters)
-{
- struct filter *f;
-
- for (f = all_filters; f->name; f++)
- f->init(f);
-}
-
-/**
- * Close and destroy a filter callback.
- *
- * \param fcb The filter callback to close.
- *
- * This removes \a fcb from the list of filter callbacks and calls
- * the close callback associated with \a fcb.
- */
-static void close_filter_callback(struct filter_callback *fcb)
-{
- PARA_NOTICE_LOG("closing filter_callback %p, data: %p\n", fcb, fcb->data);
- list_del(&fcb->node);
- fcb->close(fcb);
-}
-
-/**
- * Close all callbacks of a filter node.
- *
- * \param fn The filter node which contains the filter callbacks to be closed.
- *
- * Call close_filter_callback() for each entry in the filter callback list
- * of \a fn.
- */
-static void close_callbacks(struct filter_node *fn)
-{
- struct filter_callback *fcb, *tmp;
-
- list_for_each_entry_safe(fcb, tmp, &fn->callbacks, node) {
- PARA_INFO_LOG("closing %s filter callback\n",
- filters[fn->filter_num].name);
- close_filter_callback(fcb);
- }
-}
-
-static void call_callbacks(struct filter_node *fn, char *inbuf, size_t inlen,
- char *outbuf, size_t outlen)
-{
- struct filter_callback *fcb, *tmp;
- list_for_each_entry_safe(fcb, tmp, &fn->callbacks, node) {
- int ret;
- if (inlen && fcb->input_cb) {
- ret = fcb->input_cb(inbuf, inlen, fcb);
- if (ret < 0) {
- close_filter_callback(fcb);
- continue;
- }
- }
- if (!outlen || !fcb->output_cb)
- continue;
- ret = fcb->output_cb(outbuf, outlen, fcb);
- if (ret < 0)
- close_filter_callback(fcb);
- }
-}
-
-/**
- * Call the convert function of each filter.
- *
- * \param s Unused.
- * \param t The task identifying the filter chain.
- *
- * This is the core function of the filter subsystem. It loops over the list of
- * filter nodes determined by \a t and calls the filter's convert function if
- * there is input available for the filter node in question. If the convert
- * function consumed some or all of its input data, all registered input
- * callbacks are called. Similarly, if a convert function produced output, all
- * registerd output callbacks get called.
- *
- * On errors a (negative) error code is stored in t->error.
- *
- * \sa filter_node, filter#convert, filter_callback.
- */
-void filter_pre_select(__a_unused struct sched *s, struct task *t)
-{
- struct filter_chain *fc = container_of(t, struct filter_chain, task);
- struct filter_node *fn;
- char *ib;
- size_t *loaded;
- int i, conv, conv_total = 0;
-
- if (fc->output_error && *fc->output_error < 0) {
- t->error = *fc->output_error;
- return;
- }
-again:
- ib = fc->inbuf;
- loaded = fc->in_loaded;
- conv = 0;
- FOR_EACH_FILTER_NODE(fn, fc, i) {
- struct filter *f = filters + fn->filter_num;
- if (*loaded && fn->loaded < fn->bufsize) {
- size_t size, old_fn_loaded = fn->loaded;
-// PARA_DEBUG_LOG("fc %p loaded: %zd, calling %s convert\n",
-// fc, *loaded, fn->filter->name);
- t->error = f->convert(ib, *loaded, fn);
- if (t->error < 0)
- return;
- size = t->error;
- call_callbacks(fn, ib, size, fn->buf + old_fn_loaded,
- fn->loaded - old_fn_loaded);
- *loaded -= size;
- conv += size;
- if (*loaded && size) {
-// PARA_DEBUG_LOG("moving %zd bytes in input "
-// "buffer for %s filter\n",
-// *loaded, fn->filter->name);
- memmove(ib, ib + size, *loaded);
- }
- }
- ib = fn->buf;
- loaded = &fn->loaded;
- }
- conv_total += conv;
-// PARA_DEBUG_LOG("eof (in/out/fc): %d/%d/%d out_loaded: %zd, "
-// "conv: %d, conv_total: %d\n", *fc->input_eof,
-// fc->output_eof? *fc->output_eof : -42,
-// fc->eof, *fc->out_loaded, conv, conv_total);
- if (conv)
- goto again;
- if (!*fc->input_error)
- return;
- if (*fc->out_loaded)
- return;
- if (*fc->in_loaded && conv_total)
- return;
- t->error = -E_FC_EOF;
-}
-
-/**
- * Close all filter nodes and their callbacks.
- *
- * \param fc The filter chain to close.
- *
- * For each filter node determined by \a fc, call the close function of each
- * registered filter callback as well as the close function of the
- * corresponding filter. Free all resources and destroy all callback lists and
- * the filter node list.
- *
- * \sa filter::close, filter_callback::close
- */
-void close_filters(struct filter_chain *fc)
-{
- struct filter_node *fn;
- int i;
-
- if (!fc)
- return;
- PARA_NOTICE_LOG("closing filter chain %p\n", fc);
- FOR_EACH_FILTER_NODE(fn, fc, i) {
- struct filter *f = filters + fn->filter_num;
- close_callbacks(fn);
- PARA_INFO_LOG("closing %s filter\n", f->name);
- f->close(fn);
- }
- free(fc->filter_nodes);
-}
-
-/*
- * If the filter has a command line parser and options is not NULL, run it.
- * Returns filter_num on success, negative on errors
- */
-static int parse_filter_args(int filter_num, char *options, void **conf)
-{
- struct filter *f = &filters[filter_num];
- int i, argc = 2;
- char **argv;
-
-// PARA_DEBUG_LOG("%s, options: %s, parser: %p\n", f->name,
-// options? options : "(none)", f->parse_config);
- if (!f->parse_config)
- return strlen(options)? -E_BAD_FILTER_OPTIONS : filter_num;
-// PARA_DEBUG_LOG("options: %s\n", options);
- argc = split_args(options, &argv, " \t");
-// PARA_DEBUG_LOG("argc = %d, argv[0]: %s\n", argc, argv[0]);
- for (i = argc - 1; i >= 0; i--)
- argv[i + 1] = argv[i];
- argv[0] = para_strdup(f->name);
- argc += 1;
- *conf = f->parse_config(argc, argv);
- free(argv[0]);
- free(argv);
- return *conf? filter_num : -E_BAD_FILTER_OPTIONS;
-}
-
-/**
- * Check the filter command line options.
- *
- * \param fa The command line options.
- * \param conf Points to the filter configuration upon successful return.
- *
- * Check if \a fa starts with a the name of a supported filter, followed by
- * a colon. If yes, call the command line parser of that filter.
- *
- * \return On success, the number of the filter is returned and \a conf
- * is initialized to point to the filter configuration determined by \a fa.
- * On errors, a negative value is returned.
- *
- * Note: If \a fa specifies a filter that has no command line parser success is
- * returned, and \a conf is initialized to \p NULL.
- *
- * \sa filter::parse_config
- */
-int check_filter_arg(char *fa, void **conf)
-{
- int j;
-
- *conf = NULL;
-// PARA_DEBUG_LOG("arg: %s\n", fa);
- for (j = 0; filters[j].name; j++) {
- const char *name = filters[j].name;
- size_t len = strlen(name);
- char c;
- if (strlen(fa) < len)
- continue;
- if (strncmp(name, fa, len))
- continue;
- c = fa[len];
- if (c && c != ' ')
- continue;
- if (c && !filters[j].parse_config)
- return -E_BAD_FILTER_OPTIONS;
- return parse_filter_args(j, c? fa + len + 1 :
- fa + strlen(fa), conf);
- }
- return -E_UNSUPPORTED_FILTER;
-}
-
--- /dev/null
+/*
+ * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file filter_common.c Common helper functions for filter input/output. */
+
+#include <sys/types.h>
+#include <dirent.h>
+
+#include "para.h"
+#include "list.h"
+#include "sched.h"
+#include "fd.h"
+#include "ggo.h"
+#include "filter.h"
+#include "error.h"
+#include "string.h"
+
+/** The array of supported filters. */
+struct filter filters[NUM_SUPPORTED_FILTERS] = {FILTER_ARRAY};
+
+/**
+ * Call the init function of each supported filter.
+ *
+ * \param all_filters the array of all supported filters.
+ *
+ * \sa filter::init
+ */
+void filter_init(struct filter *all_filters)
+{
+ int i;
+
+ FOR_EACH_SUPPORTED_FILTER(i)
+ all_filters[i].init(all_filters + i);
+}
+
+/**
+ * Close and destroy a filter callback.
+ *
+ * \param fcb The filter callback to close.
+ *
+ * This removes \a fcb from the list of filter callbacks and calls
+ * the close callback associated with \a fcb.
+ */
+static void close_filter_callback(struct filter_callback *fcb)
+{
+ PARA_NOTICE_LOG("closing filter_callback %p, data: %p\n", fcb, fcb->data);
+ list_del(&fcb->node);
+ fcb->close(fcb);
+}
+
+/**
+ * Close all callbacks of a filter node.
+ *
+ * \param fn The filter node which contains the filter callbacks to be closed.
+ *
+ * Call close_filter_callback() for each entry in the filter callback list
+ * of \a fn.
+ */
+static void close_callbacks(struct filter_node *fn)
+{
+ struct filter_callback *fcb, *tmp;
+
+ list_for_each_entry_safe(fcb, tmp, &fn->callbacks, node) {
+ PARA_INFO_LOG("closing %s filter callback\n",
+ filters[fn->filter_num].name);
+ close_filter_callback(fcb);
+ }
+}
+
+static void call_callbacks(struct filter_node *fn, char *inbuf, size_t inlen,
+ char *outbuf, size_t outlen)
+{
+ struct filter_callback *fcb, *tmp;
+ list_for_each_entry_safe(fcb, tmp, &fn->callbacks, node) {
+ int ret;
+ if (inlen && fcb->input_cb) {
+ ret = fcb->input_cb(inbuf, inlen, fcb);
+ if (ret < 0) {
+ close_filter_callback(fcb);
+ continue;
+ }
+ }
+ if (!outlen || !fcb->output_cb)
+ continue;
+ ret = fcb->output_cb(outbuf, outlen, fcb);
+ if (ret < 0)
+ close_filter_callback(fcb);
+ }
+}
+
+/**
+ * Call the convert function of each filter.
+ *
+ * \param s Unused.
+ * \param t The task identifying the filter chain.
+ *
+ * This is the core function of the filter subsystem. It loops over the list of
+ * filter nodes determined by \a t and calls the filter's convert function if
+ * there is input available for the filter node in question. If the convert
+ * function consumed some or all of its input data, all registered input
+ * callbacks are called. Similarly, if a convert function produced output, all
+ * registerd output callbacks get called.
+ *
+ * On errors a (negative) error code is stored in t->error.
+ *
+ * \sa filter_node, filter#convert, filter_callback.
+ */
+void filter_pre_select(__a_unused struct sched *s, struct task *t)
+{
+ struct filter_chain *fc = container_of(t, struct filter_chain, task);
+ struct filter_node *fn;
+ char *ib;
+ size_t *loaded;
+ int i, conv, conv_total = 0;
+
+ if (fc->output_error && *fc->output_error < 0) {
+ t->error = *fc->output_error;
+ return;
+ }
+again:
+ ib = fc->inbuf;
+ loaded = fc->in_loaded;
+ conv = 0;
+ FOR_EACH_FILTER_NODE(fn, fc, i) {
+ struct filter *f = filters + fn->filter_num;
+ if (fn->loaded < fn->bufsize) {
+ size_t size, old_fn_loaded = fn->loaded;
+// PARA_DEBUG_LOG("fc %p loaded: %zd, calling %s convert\n",
+// fc, *loaded, fn->filter->name);
+ t->error = f->convert(ib, *loaded, fn);
+ if (t->error < 0)
+ return;
+ size = t->error;
+ call_callbacks(fn, ib, size, fn->buf + old_fn_loaded,
+ fn->loaded - old_fn_loaded);
+ *loaded -= size;
+ conv += size;
+ if (*loaded && size) {
+// PARA_DEBUG_LOG("moving %zd bytes in input "
+// "buffer for %s filter\n",
+// *loaded, fn->filter->name);
+ memmove(ib, ib + size, *loaded);
+ }
+ }
+ ib = fn->buf;
+ loaded = &fn->loaded;
+ }
+ conv_total += conv;
+// PARA_DEBUG_LOG("eof (in/out/fc): %d/%d/%d out_loaded: %zd, "
+// "conv: %d, conv_total: %d\n", *fc->input_eof,
+// fc->output_eof? *fc->output_eof : -42,
+// fc->eof, *fc->out_loaded, conv, conv_total);
+ if (conv)
+ goto again;
+ if (!*fc->input_error)
+ return;
+ if (*fc->out_loaded)
+ return;
+ if (*fc->in_loaded && conv_total)
+ return;
+ t->error = -E_FC_EOF;
+}
+
+/**
+ * Close all filter nodes and their callbacks.
+ *
+ * \param fc The filter chain to close.
+ *
+ * For each filter node determined by \a fc, call the close function of each
+ * registered filter callback as well as the close function of the
+ * corresponding filter. Free all resources and destroy all callback lists and
+ * the filter node list.
+ *
+ * \sa filter::close, filter_callback::close
+ */
+void close_filters(struct filter_chain *fc)
+{
+ struct filter_node *fn;
+ int i;
+
+ if (!fc)
+ return;
+ PARA_NOTICE_LOG("closing filter chain %p\n", fc);
+ FOR_EACH_FILTER_NODE(fn, fc, i) {
+ struct filter *f = filters + fn->filter_num;
+ close_callbacks(fn);
+ PARA_INFO_LOG("closing %s filter\n", f->name);
+ f->close(fn);
+ }
+ free(fc->filter_nodes);
+}
+
+/*
+ * If the filter has a command line parser and options is not NULL, run it.
+ * Returns filter_num on success, negative on errors
+ */
+static int parse_filter_args(int filter_num, char *options, void **conf)
+{
+ struct filter *f = &filters[filter_num];
+ int ret, i, argc = 2;
+ char **argv;
+
+// PARA_DEBUG_LOG("%s, options: %s, parser: %p\n", f->name,
+// options? options : "(none)", f->parse_config);
+ if (!f->parse_config)
+ return strlen(options)? -E_BAD_FILTER_OPTIONS : filter_num;
+// PARA_DEBUG_LOG("options: %s\n", options);
+ argc = split_args(options, &argv, " \t");
+// PARA_DEBUG_LOG("argc = %d, argv[0]: %s\n", argc, argv[0]);
+ for (i = argc - 1; i >= 0; i--)
+ argv[i + 1] = argv[i];
+ argv[0] = para_strdup(f->name);
+ argc += 1;
+ ret = f->parse_config(argc, argv, conf);
+ free(argv[0]);
+ free(argv);
+ return ret < 0? ret : filter_num;
+}
+
+/**
+ * Check the filter command line options.
+ *
+ * \param fa The command line options.
+ * \param conf Points to the filter configuration upon successful return.
+ *
+ * Check if \a fa starts with a the name of a supported filter, followed by
+ * a colon. If yes, call the command line parser of that filter.
+ *
+ * \return On success, the number of the filter is returned and \a conf
+ * is initialized to point to the filter configuration determined by \a fa.
+ * On errors, a negative value is returned.
+ *
+ * Note: If \a fa specifies a filter that has no command line parser success is
+ * returned, and \a conf is initialized to \p NULL.
+ *
+ * \sa filter::parse_config
+ */
+int check_filter_arg(char *fa, void **conf)
+{
+ int j;
+
+ *conf = NULL;
+// PARA_DEBUG_LOG("arg: %s\n", fa);
+ FOR_EACH_SUPPORTED_FILTER(j) {
+ const char *name = filters[j].name;
+ size_t len = strlen(name);
+ char c;
+ if (strlen(fa) < len)
+ continue;
+ if (strncmp(name, fa, len))
+ continue;
+ c = fa[len];
+ if (c && c != ' ')
+ continue;
+ if (c && !filters[j].parse_config)
+ return -E_BAD_FILTER_OPTIONS;
+ return parse_filter_args(j, c? fa + len + 1 :
+ fa + strlen(fa), conf);
+ }
+ return -E_UNSUPPORTED_FILTER;
+}
+
+void print_filter_helps(int detailed)
+{
+ int i;
+
+ printf_or_die("\nAvailable filters: \n\t");
+ FOR_EACH_SUPPORTED_FILTER(i)
+ printf_or_die("%s%s", i? " " : "", filters[i].name);
+ printf_or_die("\n\n");
+
+ FOR_EACH_SUPPORTED_FILTER(i) {
+ struct filter *f = filters + i;
+
+ if (!f->help.short_help)
+ continue;
+ printf_or_die("Options for %s:\n", f->name);
+ ggo_print_help(&f->help, detailed);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (C) 2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file ggo.c Function for printing help. */
+
+
+#include "para.h"
+#include "ggo.h"
+
+__printf_1_2 void printf_or_die(const char *fmt, ...)
+{
+ va_list argp;
+ int ret;
+
+ va_start(argp, fmt);
+ ret = vprintf(fmt, argp);
+ va_end(argp);
+ if (ret >= 0)
+ return;
+ fprintf(stderr, "%s: %s\n", __FUNCTION__, strerror(errno));
+}
+
+void ggo_print_help(struct ggo_help *help, int detailed_help)
+{
+ const char **p;
+
+ if (!help)
+ return;
+ if (detailed_help)
+ p = help->detailed_help;
+ else
+ p = help->short_help;
+ if (!p)
+ return;
+ p += 3; /* skip -h and -V */
+ for (; *p; p++)
+ printf_or_die("\t%s\n", *p);
+}
--- /dev/null
+struct ggo_help {
+ const char **short_help;
+ const char **detailed_help;
+};
+
+void ggo_print_help(struct ggo_help *help, int detailed_help);
+void printf_or_die(const char *fmt, ...);
#include "grab_client.cmdline.h"
#include "list.h"
#include "sched.h"
+#include "ggo.h"
#include "filter.h"
#include "grab_client.h"
#include "audiod.h"
return 1;
}
-void para_log(int ll, const char *fmt,...)
+__printf_2_3 void para_log(int ll, const char *fmt,...)
{
int color;
char *msg;
signal(SIGHUP, SIG_IGN);
}
-static void do_exit(int ret)
+__noreturn static void do_exit(int ret)
{
signal(SIGTERM, SIG_IGN);
kill(0, SIGTERM);
endwin();
}
-static void finish(int ret)
+__noreturn static void finish(int ret)
{
shutdown_curses();
do_exit(ret);
/*
* exit curses and print given message to stdout/stderr
*/
-__printf_2_3 static void msg_n_exit(int ret, const char* fmt, ...)
+__noreturn __printf_2_3 static void msg_n_exit(int ret, const char* fmt, ...)
{
va_list argp;
FILE *outfd = ret? stderr: stdout;
CODENAME "\"");
}
-static void com_quit(void)
+__noreturn static void com_quit(void)
{
finish(0);
}
d[SI_NUM_CHUNKS].align = RIGHT;
d[SI_NUM_CHUNKS].x = 73;
d[SI_NUM_CHUNKS].y = 27;
- d[SI_NUM_CHUNKS].len = 12;
+ d[SI_NUM_CHUNKS].len = 11;
- d[SI_CHUNK_TIME].prefix = " ";
+ d[SI_CHUNK_TIME].prefix = "";
d[SI_CHUNK_TIME].postfix = "ms";
d[SI_CHUNK_TIME].fg = COLOR_MAGENTA;
d[SI_CHUNK_TIME].bg = COLOR_BLACK;
d[SI_CHUNK_TIME].align = LEFT;
- d[SI_CHUNK_TIME].x = 85;
+ d[SI_CHUNK_TIME].x = 84;
d[SI_CHUNK_TIME].y = 27;
- d[SI_CHUNK_TIME].len = 15;
+ d[SI_CHUNK_TIME].len = 8;
+
+ d[SI_AMPLIFICATION].prefix = "amp:";
+ d[SI_AMPLIFICATION].postfix = "";
+ d[SI_AMPLIFICATION].fg = COLOR_MAGENTA;
+ d[SI_AMPLIFICATION].bg = COLOR_BLACK;
+ d[SI_AMPLIFICATION].align = RIGHT;
+ d[SI_AMPLIFICATION].x = 92;
+ d[SI_AMPLIFICATION].y = 27;
+ d[SI_AMPLIFICATION].len = 8;
d[SI_AUDIO_FILE_INFO].prefix = "";
d[SI_AUDIO_FILE_INFO].postfix = "";
/** \file hash.h Inline functions for hash values. */
-#include "portable_io.h"
-
/** hash arrays are always unsigned char. */
#define HASH_TYPE unsigned char
#include "http.h"
#include "list.h"
#include "sched.h"
+#include "ggo.h"
#include "recv.h"
#include "http_recv.cmdline.h"
#include "net.h"
if (!FD_ISSET(phd->fd, &s->rfds))
return;
if (phd->status == HTTP_SENT_GET_REQUEST) {
- t->error = recv_pattern(phd->fd, HTTP_OK_MSG, MAXLINE);
+ t->error = recv_pattern(phd->fd, HTTP_OK_MSG, strlen(HTTP_OK_MSG));
if (t->error >= 0) {
PARA_INFO_LOG("received ok msg, streaming\n");
phd->status = HTTP_STREAMING;
*/
void http_recv_init(struct receiver *r)
{
+ struct http_recv_args_info dummy;
+
+ http_recv_cmdline_parser_init(&dummy);
r->open = http_recv_open;
r->close = http_recv_close;
r->pre_select = http_recv_pre_select;
r->post_select = http_recv_post_select;
r->shutdown = http_shutdown;
r->parse_config = http_recv_parse_config;
+ r->help = (struct ggo_help) {
+ .short_help = http_recv_args_info_help,
+ .detailed_help = http_recv_args_info_detailed_help
+ };
}
-section "options of the http receiver"
-option "host" i "ip or host" string default="localhost" optional
-option "port" p "tcp port to connect to" int default="8000" optional
+option "host" i
+#~~~~~~~~~~~~~~
+"ip or host"
+string
+default="localhost"
+optional
+details="
+ Both IPv4 and IPv6 addresses are supported.
+"
+
+option "port" p
+#~~~~~~~~~~~~~~
+"tcp port to connect to"
+int default="8000"
+optional
* Used for scoring and to determine whether a file is admissible.
*/
struct mood_method {
- /* The name of the method. */
+ /** The name of the method. */
const char *name;
/** Pointer to the mood parser. */
mood_parser *parser;
static int compare_headers(struct mp3header *h1,struct mp3header *h2)
{
- if ((*(uint*)h1) == (*(uint*)h2))
+ if ((*(unsigned int*)h1) == (*(unsigned int*)h2))
return 1;
if ((h1->version == h2->version) &&
(h1->layer == h2->layer) &&
+++ /dev/null
-/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file mp3dec.c Paraslash's mp3 decoder. */
-
-#include "para.h"
-#include "list.h"
-#include "sched.h"
-#include "filter.h"
-#include "error.h"
-#include <mad.h>
-#include "string.h"
-
-/** The output buffer size. */
-#define MP3_OUTBUF_SIZE (128 * 1024)
-
-/** Convert a sample value from libmad to a signed short. */
-#define MAD_TO_SHORT(f) (f) >= MAD_F_ONE? SHRT_MAX :\
- (f) <= -MAD_F_ONE? -SHRT_MAX : (signed short) ((f) >> (MAD_F_FRACBITS - 15))
-
-/** Data specific to the mp3dec filter. */
-struct private_mp3dec_data {
- /** Information on the current mp3 stream. */
- struct mad_stream stream;
- /** Information about the frame which is currently decoded. */
- struct mad_frame frame;
- /** Contains the PCM output. */
- struct mad_synth synth;
-};
-
-static ssize_t mp3dec(char *inbuffer, size_t len, struct filter_node *fn)
-{
- int i, ret;
- struct private_mp3dec_data *pmd = fn->private_data;
- size_t copy = PARA_MIN(len, 4096);
-
- if (fn->loaded > fn->bufsize * 4 / 5)
- return 0;
- mad_stream_buffer(&pmd->stream, (unsigned char *) inbuffer, copy);
- pmd->stream.error = 0;
-next_frame:
- ret = mad_header_decode(&pmd->frame.header, &pmd->stream);
- if (ret < 0)
- goto out;
- fn->fc->samplerate = pmd->frame.header.samplerate;
- fn->fc->channels = MAD_NCHANNELS(&pmd->frame.header);
- ret = mad_frame_decode(&pmd->frame, &pmd->stream);
- if (ret) {
- if (MAD_RECOVERABLE(pmd->stream.error) || pmd->stream.error == MAD_ERROR_BUFLEN)
- goto out;
- PARA_ERROR_LOG("fatal: ret = %d, loaded = %zd\n", ret, fn->loaded);
- return -E_MAD_FRAME_DECODE;
- }
- mad_synth_frame(&pmd->synth, &pmd->frame);
-
- for (i = 0; i < pmd->synth.pcm.length; i++) {
- int s = MAD_TO_SHORT(pmd->synth.pcm.samples[0][i]);
- write_int16_host_endian(fn->buf + fn->loaded, s);
- fn->loaded += 2;
- if (MAD_NCHANNELS(&pmd->frame.header) == 2) { /* stereo */
- s = MAD_TO_SHORT(pmd->synth.pcm.samples[1][i]);
- write_int16_host_endian(fn->buf + fn->loaded, s);
- fn->loaded += 2;
- }
- if (fn->loaded != fn->bufsize) /* output buffer not full */
- continue;
- PARA_ERROR_LOG("output buffer full: %zd\n", fn->loaded);
- return -E_MP3DEC_OVERRUN;
- }
- if (fn->loaded <= fn->bufsize * 4 / 5)
- goto next_frame;
-out:
- if (pmd->stream.next_frame) { /* we still have some data */
- size_t off = pmd->stream.bufend - pmd->stream.next_frame;
-// PARA_INFO_LOG("off: %zd, rate: %u, returning %zd\n", off,
-// fn->fc->samplerate, copy - off);
- return copy - off;
- }
- return copy;
-}
-
-static void mp3dec_close(struct filter_node *fn)
-{
- struct private_mp3dec_data *pmd = fn->private_data;
-
- mad_synth_finish(&pmd->synth);
- mad_frame_finish(&pmd->frame);
- mad_stream_finish(&pmd->stream);
-
- free(fn->buf);
- fn->buf = NULL;
- free(pmd);
- fn->private_data = NULL;
-}
-
-static void mp3dec_open(struct filter_node *fn)
-{
- fn->private_data = para_calloc(sizeof(struct private_mp3dec_data));
- struct private_mp3dec_data *pmd = fn->private_data;
-
- mad_stream_init(&pmd->stream);
- mad_frame_init(&pmd->frame);
- mad_synth_init(&pmd->synth);
- fn->loaded = 0;
- fn->bufsize = MP3_OUTBUF_SIZE;
- fn->buf = para_calloc(fn->bufsize);
-}
-
-/**
- * The init function of the mp3dec filter.
- *
- * \param f Pointer to the filter struct to initialize.
- *
- * \sa filter::init.
- */
-void mp3dec_init(struct filter *f)
-{
- f->open = mp3dec_open;
- f->convert = mp3dec;
- f->close = mp3dec_close;
-}
--- /dev/null
+/*
+ * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file mp3dec_filter.c Paraslash's mp3 decoder. */
+
+#include "para.h"
+#include "mp3dec_filter.cmdline.h"
+#include "list.h"
+#include "sched.h"
+#include "ggo.h"
+#include "filter.h"
+#include "error.h"
+#include <mad.h>
+#include "string.h"
+
+/** Convert a sample value from libmad to a signed short. */
+#define MAD_TO_SHORT(f) (f) >= MAD_F_ONE? SHRT_MAX :\
+ (f) <= -MAD_F_ONE? -SHRT_MAX : (signed short) ((f) >> (MAD_F_FRACBITS - 15))
+
+/** Data specific to the mp3dec filter. */
+struct private_mp3dec_data {
+ /** Information on the current mp3 stream. */
+ struct mad_stream stream;
+ /** Information about the frame which is currently decoded. */
+ struct mad_frame frame;
+ /** Contains the PCM output. */
+ struct mad_synth synth;
+};
+
+static ssize_t mp3dec(char *inbuffer, size_t len, struct filter_node *fn)
+{
+ int i, ret;
+ struct private_mp3dec_data *pmd = fn->private_data;
+ size_t copy = PARA_MIN(len, (size_t)4096);
+
+ if (fn->loaded + 16384 > fn->bufsize)
+ return 0;
+ mad_stream_buffer(&pmd->stream, (unsigned char *) inbuffer, copy);
+ pmd->stream.error = 0;
+next_frame:
+ ret = mad_header_decode(&pmd->frame.header, &pmd->stream);
+ if (ret < 0) {
+ if (pmd->stream.error != MAD_ERROR_BUFLEN &&
+ pmd->stream.error != MAD_ERROR_LOSTSYNC)
+ PARA_DEBUG_LOG("header decode: %s\n",
+ mad_stream_errorstr(&pmd->stream));
+ goto out;
+ }
+ fn->fc->samplerate = pmd->frame.header.samplerate;
+ fn->fc->channels = MAD_NCHANNELS(&pmd->frame.header);
+ ret = mad_frame_decode(&pmd->frame, &pmd->stream);
+ if (ret) {
+ if (MAD_RECOVERABLE(pmd->stream.error) ||
+ pmd->stream.error == MAD_ERROR_BUFLEN) {
+ PARA_DEBUG_LOG("frame decode: %s\n",
+ mad_stream_errorstr(&pmd->stream));
+ goto out;
+ }
+ PARA_ERROR_LOG("frame decode: %s\n",
+ mad_stream_errorstr(&pmd->stream));
+ return -E_MAD_FRAME_DECODE;
+ }
+ mad_synth_frame(&pmd->synth, &pmd->frame);
+
+ for (i = 0; i < pmd->synth.pcm.length; i++) {
+ int s = MAD_TO_SHORT(pmd->synth.pcm.samples[0][i]);
+ write_int16_host_endian(fn->buf + fn->loaded, s);
+ fn->loaded += 2;
+ if (MAD_NCHANNELS(&pmd->frame.header) == 2) { /* stereo */
+ s = MAD_TO_SHORT(pmd->synth.pcm.samples[1][i]);
+ write_int16_host_endian(fn->buf + fn->loaded, s);
+ fn->loaded += 2;
+ }
+ if (fn->loaded != fn->bufsize) /* output buffer not full */
+ continue;
+ PARA_ERROR_LOG("output buffer full: %zd\n", fn->loaded);
+ return -E_MP3DEC_OVERRUN;
+ }
+ if (fn->loaded + 16384 <= fn->bufsize)
+ goto next_frame;
+out:
+ if (pmd->stream.next_frame) { /* we still have some data */
+ size_t off = pmd->stream.bufend - pmd->stream.next_frame;
+// PARA_INFO_LOG("off: %zd, rate: %u, returning %zd\n", off,
+// fn->fc->samplerate, copy - off);
+ return copy - off;
+ }
+ return copy;
+}
+
+static void mp3dec_close(struct filter_node *fn)
+{
+ struct private_mp3dec_data *pmd = fn->private_data;
+
+ mad_synth_finish(&pmd->synth);
+ mad_frame_finish(&pmd->frame);
+ mad_stream_finish(&pmd->stream);
+
+ free(fn->buf);
+ fn->buf = NULL;
+ free(pmd);
+ fn->private_data = NULL;
+}
+
+static void mp3dec_open(struct filter_node *fn)
+{
+ struct private_mp3dec_data *pmd = para_calloc(sizeof(*pmd));
+ struct mp3dec_filter_args_info *mp3_conf = fn->conf;
+
+ fn->private_data = pmd;
+ mad_stream_init(&pmd->stream);
+ mad_frame_init(&pmd->frame);
+ mad_synth_init(&pmd->synth);
+ fn->loaded = 0;
+ fn->bufsize = mp3_conf->bufsize_arg * 1024;
+ fn->buf = para_calloc(fn->bufsize);
+ if (mp3_conf->ignore_crc_given)
+ mad_stream_options(&pmd->stream, MAD_OPTION_IGNORECRC);
+}
+
+static int mp3dec_parse_config(int argc, char **argv, void **config)
+{
+ int ret;
+ struct mp3dec_filter_args_info *mp3_conf;
+
+ mp3_conf = para_calloc(sizeof(*mp3_conf));
+ ret = -E_MP3DEC_SYNTAX;
+ if (mp3dec_cmdline_parser(argc, argv, mp3_conf))
+ goto err;
+ ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+ if (mp3_conf->bufsize_arg < 32)
+ goto err;
+ if (mp3_conf->bufsize_arg >= INT_MAX / 1024)
+ goto err;
+ *config = mp3_conf;
+ return 1;
+err:
+ free(mp3_conf);
+ return ret;
+}
+
+/**
+ * The init function of the mp3dec filter.
+ *
+ * \param f Pointer to the filter struct to initialize.
+ *
+ * \sa filter::init.
+ */
+void mp3dec_filter_init(struct filter *f)
+{
+ struct mp3dec_filter_args_info dummy;
+
+ mp3dec_cmdline_parser_init(&dummy);
+ f->open = mp3dec_open;
+ f->convert = mp3dec;
+ f->close = mp3dec_close;
+ f->parse_config = mp3dec_parse_config;
+ f->help = (struct ggo_help) {
+ .short_help = mp3dec_filter_args_info_help,
+ .detailed_help = mp3dec_filter_args_info_detailed_help
+ };
+}
--- /dev/null
+option "bufsize" b
+#~~~~~~~~~~~~~~~~~
+"size of output buffer"
+int typestr="kilobyte"
+default="128"
+optional
+details="
+ Increase this if you encounter output buffer overrun
+ errors. Smaller values make the mp3dec filter use less
+ memory. The minimal size is 32K.
+"
+
+option "ignore-crc" i
+#~~~~~~~~~~~~~~~~~~~~
+"ignore CRC information in the audio stream."
+flag off
+details="
+ This causes frames with CRC errors to be decoded and played
+ anyway. This option is not recommended, but since some encoders
+ have been known to generate bad CRC information, this option
+ is a work-around to play streams from such encoders.
+"
*
* \return Positive if \a pattern was received, negative otherwise.
*
- * This function creates a buffer of size \a bufsize and tries
- * to receive at most \a bufsize bytes from file descriptor \a fd.
- * If at least \p strlen(\a pattern) bytes were received, the beginning of
- * the received buffer is compared with \a pattern, ignoring case.
+ * This function tries to receive at most \a bufsize bytes from file descriptor
+ * \a fd. If at least \p strlen(\a pattern) bytes were received, the beginning
+ * of the received buffer is compared with \a pattern, ignoring case.
*
* \sa recv_buffer(), \sa strncasecmp(3).
*/
{
size_t len = strlen(pattern);
char *buf = para_malloc(bufsize + 1);
- int ret = -E_RECV_PATTERN, n = recv_buffer(fd, buf, bufsize);
+ int ret = -E_RECV_PATTERN, n = recv_buffer(fd, buf, bufsize + 1);
if (n < len)
goto out;
static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource)
{
struct ogg_datasource *ods = datasource;
- size_t copy = PARA_MIN(ods->numbytes - ods->fpos, size * nmemb),
- ret = copy / size;
+ size_t copy, ret;
+
+ if (!size)
+ return 0;
+
+ assert(ods->numbytes >= ods->fpos);
+ ret = ods->numbytes - ods->fpos;
+ copy = PARA_MIN(ret, size * nmemb);
+ ret = copy / size;
if (!ret)
return 0;
memcpy(buf, ods->map + ods->fpos, copy);
struct afh_info *afhi)
{
int ret;
- size_t len = PARA_MIN(numbytes, CHUNK_SIZE);
+ size_t len = PARA_MIN(numbytes, (size_t)CHUNK_SIZE);
int serial;
char *buf;
struct afh_info *afhi, long unsigned time_total)
{
int i, ret, num;
- ssize_t max_chunk_len, pos = 0, min = 0, old_pos;
long unsigned num_chunks;
+ ogg_int64_t max = 0, min = 0, old_pos = 0;
- old_pos = 0;
ret = 0;
num = time_total / chunk_time + 3;
PARA_DEBUG_LOG("chunk time: %g allocating %d chunk pointers\n",
chunk_time, num);
afhi->chunk_table = para_malloc((num + 1) * sizeof(size_t));
afhi->chunk_table[0] = 0;
- max_chunk_len = 0;
- for (i = 1; ret <= num; i++) {
- ogg_int64_t diff;
+ for (i = 1; i <= num; i++) {
+ ogg_int64_t diff, pos;
ret = ov_time_seek(of, i * chunk_time);
if (ret)
break;
pos = ov_raw_tell(of);
diff = pos - old_pos;
- max_chunk_len = PARA_MAX(max_chunk_len, diff);
+ max = PARA_MAX(max, diff);
min = (i == 1)? diff : PARA_MIN(min, diff);
afhi->chunk_table[i] = pos;
-// if (i < 11 || !((i - 1) % 1000)|| i > num - 11)
-// PARA_DEBUG_LOG("chunk #%d: %g secs, pos: %zd, "
-// "size: %zd\n", i - 1,
-// i * chunk_time, pos, pos - old_pos);
old_pos = pos;
}
num_chunks = i - 1;
-//fi->chunk_table[i] = pos;
- PARA_DEBUG_LOG("%lu chunks (%fs), max chunk: %zd, min chunk: %zd\n",
- num_chunks, chunk_time, max_chunk_len, min);
+ PARA_DEBUG_LOG("%lu chunks (%fs), max chunk: %lld, min chunk: %lld\n",
+ num_chunks, chunk_time, (long long)max, (long long)min);
return num_chunks;
}
+++ /dev/null
-/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file oggdec.c Paraslash's ogg vorbis decoder. */
-
-#include "para.h"
-
-#include "oggdec_filter.cmdline.h"
-#include "list.h"
-#include "sched.h"
-#include "filter.h"
-#include "error.h"
-#include "string.h"
-
-#include <vorbis/vorbisfile.h>
-
-/** Determine byte sex. */
-#ifdef WORDS_BIGENDIAN
-#define ENDIAN 1
-#else
-#define ENDIAN 0
-#endif
-
-/** Data specific to the oggdec filter. */
-struct private_oggdec_data {
- /** Describes an ogg vorbis file. */
- OggVorbis_File *vf;
- /** The input buffer. */
- char *inbuf;
- /** The length of \a inbuf. */
- size_t inbuf_len;
- /** The number of bytes consumed from the input buffer. */
- size_t converted;
-};
-
-static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource)
-{
- struct filter_node *fn = datasource;
- struct private_oggdec_data *pod = fn->private_data;
- size_t ret, have = pod->inbuf_len - pod->converted;
- char *p = pod->inbuf + pod->converted;
-
-// PARA_DEBUG_LOG("pod = %p\n", pod);
-// PARA_DEBUG_LOG("vorbis requests %d bytes, have %d\n", size * nmemb, have);
- if (pod->inbuf_len < size) {
- if (*fn->fc->input_error)
- return 0;
- errno = EAGAIN;
- return (size_t)-1;
- }
- ret = PARA_MIN(nmemb, have / size) * size;
- memcpy(buf, p, ret);
- pod->converted += ret;
- return ret;
-}
-
-/*
- * Custom data seeking function.
- *
- * Since we want the data source to be treated as unseekable at all
- * times, the provided seek callback always returns -1 (failure).
- */
-static int cb_seek(__a_unused void *datasource, __a_unused ogg_int64_t offset,
- __a_unused int whence)
-{
- return -1;
-}
-
-static int cb_close(__a_unused void *datasource)
-{
- return 0;
-}
-
-static const ov_callbacks ovc = {
- .read_func = cb_read,
- .seek_func = cb_seek,
- .close_func = cb_close,
- /*
- * The tell function need not be provided if the data IO abstraction is
- * not seekable
- */
- .tell_func = NULL
-};
-
-static void ogg_open(struct filter_node *fn)
-{
- struct private_oggdec_data *pod = para_calloc(
- sizeof(struct private_oggdec_data));
- struct oggdec_filter_args_info *conf = fn->conf;
-
- fn->private_data = pod;
- fn->bufsize = conf->bufsize_arg * 1024;
- fn->buf = para_malloc(fn->bufsize);
-}
-
-static void ogg_close(struct filter_node *fn)
-{
- struct private_oggdec_data *pod = fn->private_data;
- if (pod->vf) {
- PARA_DEBUG_LOG("ov_clearing %p, pod = %p\n", pod->vf, pod);
- ov_clear(pod->vf);
- free(pod->vf);
- pod->vf = NULL;
- } else
- PARA_DEBUG_LOG("nothing to close in fc %p, pod = %p\n", pod->vf, pod);
- free(fn->buf);
- fn->buf = NULL;
- free(fn->private_data);
- fn->private_data = NULL;
-}
-
-static ssize_t ogg_convert(char *inbuffer, size_t len, struct filter_node *fn)
-{
- ssize_t ret;
- struct private_oggdec_data *pod = fn->private_data;
- struct oggdec_filter_args_info *conf = fn->conf;
- /* make the buffer known to the read callback cb_read() */
- pod->inbuf = inbuffer;
- pod->inbuf_len = len;
- pod->converted = 0;
-
- if (!pod->vf) {
- int ib = 1024 * conf->initial_buffer_arg; /* initial buffer */
- if (len <ib && !*fn->fc->input_error) {
- PARA_DEBUG_LOG("initial input buffer %zd/%d, "
- "waiting for more data\n", len, ib);
- return 0;
- }
- pod->vf = para_malloc(sizeof(struct OggVorbis_File));
- PARA_NOTICE_LOG("input buffer: %zd, opening ov callbacks\n", len);
- ret = ov_open_callbacks(fn, pod->vf,
- NULL, /* no initial buffer */
- 0, /* no initial bytes */
- ovc); /* the ov_open_callbacks */
- if (ret == OV_EREAD)
- return -E_OGGDEC_READ;
- if (ret == OV_ENOTVORBIS)
- return -E_OGGDEC_NOTVORBIS;
- if (ret == OV_EVERSION)
- return -E_OGGDEC_VERSION;
- if (ret == OV_EBADHEADER)
- return -E_OGGDEC_BADHEADER;
- if (ret < 0)
- return -E_OGGDEC_FAULT;
- fn->fc->channels = ov_info(pod->vf, 0)->channels;
- fn->fc->samplerate = ov_info(pod->vf, 0)->rate;
- PARA_NOTICE_LOG("%d channels, %d Hz\n", fn->fc->channels,
- fn->fc->samplerate);
- }
- while (!*fn->fc->input_error && fn->loaded < fn->bufsize) {
- int length = fn->bufsize - fn->loaded;
- long read_ret = ov_read(pod->vf, fn->buf + fn->loaded, length,
- ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
- if (read_ret == OV_HOLE || !read_ret)
- return pod->converted;
- if (read_ret < 0)
- return -E_OGGDEC_BADLINK;
- fn->loaded += read_ret;
- }
- return pod->converted;
-}
-
-static void *oggdec_parse_config(int argc, char **argv)
-{
- struct oggdec_filter_args_info *ret = para_calloc(sizeof(struct oggdec_filter_args_info));
- if (!oggdec_cmdline_parser(argc, argv, ret))
- return ret;
- free(ret);
- return NULL;
-}
-
-/**
- * The init function of the ogg vorbis decoder.
- *
- * \param f Its fields are filled in by the function.
- */
-void oggdec_init(struct filter *f)
-{
- f->open = ogg_open;
- f->close = ogg_close;
- f->convert = ogg_convert;
- f->print_help = oggdec_cmdline_parser_print_help;
- f->parse_config = oggdec_parse_config;
-}
--- /dev/null
+/*
+ * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file oggdec_filter.c Paraslash's ogg vorbis decoder. */
+
+#include "para.h"
+
+#include "oggdec_filter.cmdline.h"
+#include "list.h"
+#include "sched.h"
+#include "ggo.h"
+#include "filter.h"
+#include "error.h"
+#include "string.h"
+
+#include <vorbis/vorbisfile.h>
+
+/** Determine byte sex. */
+#ifdef WORDS_BIGENDIAN
+#define ENDIAN 1
+#else
+#define ENDIAN 0
+#endif
+
+/** Data specific to the oggdec filter. */
+struct private_oggdec_data {
+ /** Describes an ogg vorbis file. */
+ OggVorbis_File *vf;
+ /** The input buffer. */
+ char *inbuf;
+ /** The length of \a inbuf. */
+ size_t inbuf_len;
+ /** The number of bytes consumed from the input buffer. */
+ size_t converted;
+};
+
+static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource)
+{
+ struct filter_node *fn = datasource;
+ struct private_oggdec_data *pod = fn->private_data;
+ size_t ret, have = pod->inbuf_len - pod->converted;
+ char *p = pod->inbuf + pod->converted;
+
+// PARA_DEBUG_LOG("pod = %p\n", pod);
+// PARA_DEBUG_LOG("vorbis requests %d bytes, have %d\n", size * nmemb, have);
+ if (pod->inbuf_len < size) {
+ if (*fn->fc->input_error)
+ return 0;
+ errno = EAGAIN;
+ return (size_t)-1;
+ }
+ ret = PARA_MIN(nmemb, have / size) * size;
+ memcpy(buf, p, ret);
+ pod->converted += ret;
+ return ret;
+}
+
+/*
+ * Custom data seeking function.
+ *
+ * Since we want the data source to be treated as unseekable at all
+ * times, the provided seek callback always returns -1 (failure).
+ */
+static int cb_seek(__a_unused void *datasource, __a_unused ogg_int64_t offset,
+ __a_unused int whence)
+{
+ return -1;
+}
+
+static int cb_close(__a_unused void *datasource)
+{
+ return 0;
+}
+
+static const ov_callbacks ovc = {
+ .read_func = cb_read,
+ .seek_func = cb_seek,
+ .close_func = cb_close,
+ /*
+ * The tell function need not be provided if the data IO abstraction is
+ * not seekable
+ */
+ .tell_func = NULL
+};
+
+static void ogg_open(struct filter_node *fn)
+{
+ struct private_oggdec_data *pod = para_calloc(
+ sizeof(struct private_oggdec_data));
+ struct oggdec_filter_args_info *conf = fn->conf;
+
+ fn->private_data = pod;
+ fn->bufsize = conf->bufsize_arg * 1024;
+ fn->buf = para_malloc(fn->bufsize);
+}
+
+static void ogg_close(struct filter_node *fn)
+{
+ struct private_oggdec_data *pod = fn->private_data;
+ if (pod->vf) {
+ PARA_DEBUG_LOG("ov_clearing %p, pod = %p\n", pod->vf, pod);
+ ov_clear(pod->vf);
+ free(pod->vf);
+ pod->vf = NULL;
+ } else
+ PARA_DEBUG_LOG("nothing to close in fc %p, pod = %p\n", pod->vf, pod);
+ free(fn->buf);
+ fn->buf = NULL;
+ free(fn->private_data);
+ fn->private_data = NULL;
+}
+
+static ssize_t ogg_convert(char *inbuffer, size_t len, struct filter_node *fn)
+{
+ ssize_t ret;
+ struct private_oggdec_data *pod = fn->private_data;
+ struct oggdec_filter_args_info *conf = fn->conf;
+ /* make the buffer known to the read callback cb_read() */
+ pod->inbuf = inbuffer;
+ pod->inbuf_len = len;
+ pod->converted = 0;
+
+ if (!pod->vf) {
+ int ib = 1024 * conf->initial_buffer_arg; /* initial buffer */
+ if (len <ib && !*fn->fc->input_error) {
+ PARA_DEBUG_LOG("initial input buffer %zd/%d, "
+ "waiting for more data\n", len, ib);
+ return 0;
+ }
+ pod->vf = para_malloc(sizeof(struct OggVorbis_File));
+ PARA_NOTICE_LOG("input buffer: %zd, opening ov callbacks\n", len);
+ ret = ov_open_callbacks(fn, pod->vf,
+ NULL, /* no initial buffer */
+ 0, /* no initial bytes */
+ ovc); /* the ov_open_callbacks */
+ if (ret == OV_EREAD)
+ return -E_OGGDEC_READ;
+ if (ret == OV_ENOTVORBIS)
+ return -E_OGGDEC_NOTVORBIS;
+ if (ret == OV_EVERSION)
+ return -E_OGGDEC_VERSION;
+ if (ret == OV_EBADHEADER)
+ return -E_OGGDEC_BADHEADER;
+ if (ret < 0)
+ return -E_OGGDEC_FAULT;
+ fn->fc->channels = ov_info(pod->vf, 0)->channels;
+ fn->fc->samplerate = ov_info(pod->vf, 0)->rate;
+ PARA_NOTICE_LOG("%d channels, %d Hz\n", fn->fc->channels,
+ fn->fc->samplerate);
+ }
+ while (fn->loaded < fn->bufsize) {
+ int length = fn->bufsize - fn->loaded;
+ long read_ret = ov_read(pod->vf, fn->buf + fn->loaded, length,
+ ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
+ if (read_ret == OV_HOLE || !read_ret)
+ return pod->converted;
+ if (read_ret < 0)
+ return -E_OGGDEC_BADLINK;
+ fn->loaded += read_ret;
+ }
+ return pod->converted;
+}
+
+static int oggdec_parse_config(int argc, char **argv, void **config)
+{
+ int ret;
+ struct oggdec_filter_args_info *ogg_conf;
+
+ ogg_conf = para_calloc(sizeof(*ogg_conf));
+ ret = -E_OGGDEC_SYNTAX;
+ if (oggdec_cmdline_parser(argc, argv, ogg_conf))
+ goto err;
+ ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+ if (ogg_conf->bufsize_arg < 0)
+ goto err;
+ if (ogg_conf->bufsize_arg >= INT_MAX / 1024)
+ goto err;
+ if (ogg_conf->initial_buffer_arg < 0)
+ goto err;
+ if (ogg_conf->initial_buffer_arg >= INT_MAX / 1024)
+ goto err;
+ *config = ogg_conf;
+ return 1;
+err:
+ free(ogg_conf);
+ return ret;
+}
+
+/**
+ * The init function of the ogg vorbis decoder.
+ *
+ * \param f Its fields are filled in by the function.
+ */
+void oggdec_filter_init(struct filter *f)
+{
+ struct oggdec_filter_args_info dummy;
+
+ oggdec_cmdline_parser_init(&dummy);
+ f->open = ogg_open;
+ f->close = ogg_close;
+ f->convert = ogg_convert;
+ f->parse_config = oggdec_parse_config;
+ f->help = (struct ggo_help) {
+ .short_help = oggdec_filter_args_info_help,
+ .detailed_help = oggdec_filter_args_info_detailed_help
+ };
+}
-option "bufsize" b "size of output buffer" int typestr="kilobyte" default="128" optional
-option "initial_buffer" i "size of initial input buffer" int typestr="kilobyte" default="16" optional
+option "bufsize" b
+#~~~~~~~~~~~~~~~~~
+"size of output buffer"
+int typestr="kilobyte"
+default="128"
+optional
+details="
+ Increase this if you encounter output buffer overrun errors. Smaller
+ values make the oggdec filter use less memory.
+"
+
+option "initial_buffer" i
+#~~~~~~~~~~~~~~~~~~~~~~~~
+"size of initial input buffer"
+int typestr="kilobyte"
+default="16"
+optional
+details="
+ On startup, defer decoding until that many kilobytes are
+ available in the input buffer.
+"
#include "ortp.h"
#include "list.h"
#include "sched.h"
+#include "ggo.h"
#include "recv.h"
#include "ortp_recv.cmdline.h"
*/
void ortp_recv_init(struct receiver *r)
{
+ struct ortp_recv_args_info dummy;
+
+ ortp_recv_cmdline_parser_init(&dummy);
r->shutdown = ortp_shutdown;
r->open = ortp_recv_open;
r->close = ortp_recv_close;
r->pre_select = ortp_recv_pre_select;
r->post_select = ortp_recv_post_select;
r->parse_config = ortp_recv_parse_config;
+ r->help = (struct ggo_help) {
+ .short_help = ortp_recv_args_info_help,
+ .detailed_help = ortp_recv_args_info_detailed_help
+ };
ortp_init();
}
-section "ortp options"
-option "host" i "ip or host to receive rtp packets from" string default="224.0.1.38" optional
-option "port" p "udp port." int typestr="portnumber" default="1500" optional
-option "jitter_compensation" j "set ortp's adaptive jitter compensation" int typestr="milliseconds" default="0" optional
+option "host" i
+"ip or host to receive rtp packets from"
+string default="224.0.1.38"
+optional
+details="
+ The default address resoves to DANTZ.MCAST.NET and activates
+ multicast.
+"
+
+option "port" p "udp port"
+int typestr="portnumber"
+default="1500"
+optional
+
+option "jitter_compensation" j
+"set ortp's adaptive jitter compensation"
+int typestr="milliseconds"
+default="0"
+optional
*/
int para_lseek(int fd, off_t *offset, int whence)
{
- *offset = lseek(fd, *offset, whence);
int ret = -E_LSEEK;
+
+ *offset = lseek(fd, *offset, whence);
if (*offset == -1)
return ret;
return 1;
goto out;
rollback: /* rollback all changes made, ignore further errors */
for (i--; i >= 0; i--) {
+ enum osl_storage_type st;
+
cd = get_column_description(t->desc, i);
- enum osl_storage_type st = cd->storage_type;
+ st = cd->storage_type;
if (st == OSL_NO_STORAGE)
continue;
#include "rbtree.h"
#include "osl.h"
#include "string.h"
+#include "portable_io.h"
#include "hash.h"
/** Internal representation of a column of an osl table. */
return 0;
}
+#ifdef WORDS_BIGENDIAN /* ppc */
+#define ENDIAN_FLAGS kLinearPCMFormatFlagIsBigEndian
+#else
+#define ENDIAN_FLAGS 0
+#endif
+
static int osx_write_open(struct writer_node *wn)
{
struct private_osx_write_data *powd = para_calloc(
/* flags specific to each format */
format.mFormatFlags = kLinearPCMFormatFlagIsFloat
| kLinearPCMFormatFlagIsPacked
- | kLinearPCMFormatFlagIsBigEndian;
+ | ENDIAN_FLAGS;
if (!conf->channels_given && wng->channels)
powd->channels = *wng->channels;
else
/** used in various contexts */
#define MAXLINE 255
-/** compute the minimum of \a a and \a b */
-#define PARA_MIN(a,b) ((a) < (b) ? (a) : (b))
-/** compute the maximum of \a a and \a b */
-#define PARA_MAX(a,b) ((a) > (b) ? (a) : (b))
-/** compute the absolute value of \a a */
-#define PARA_ABS(a) ((a) > 0 ? (a) : -(a))
+/** Compute the minimum of \a x and \a y. */
+#define PARA_MIN(x, y) ({ \
+ typeof(x) _min1 = (x); \
+ typeof(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+
+/** Compute the maximum of \a x and \a y. */
+#define PARA_MAX(x, y) ({ \
+ typeof(x) _max1 = (x); \
+ typeof(y) _max2 = (y); \
+ (void) (&_max1 == &_max2); \
+ _max1 < _max2 ? _max2 : _max1; })
+
+/** Compute the absolute value of \a x. */
+#define PARA_ABS(x) ({ \
+ typeof(x) _x = (x); \
+ _x > 0? _x : -_x; })
/** debug loglevel, gets really noisy */
#define DEBUG 1
*/
int rb_rank(struct rb_node *node, unsigned *rank)
{
- *rank = 1;
struct rb_node *parent;
+ *rank = 1;
if (!node)
return -1;
if (node->rb_left)
#include "para.h"
#include "list.h"
#include "sched.h"
+#include "ggo.h"
#include "recv.h"
#include "recv.cmdline.h"
#include "fd.h"
/** init array of error codes used by para_recv */
INIT_RECV_ERRLISTS;
-static void *parse_config(int argc, char *argv[], int *receiver_num)
+__noreturn static void print_help_and_die(void)
{
- int i;
+ int d = conf.detailed_help_given;
+ const char **p = d? recv_args_info_detailed_help
+ : recv_args_info_help;
+
+ printf_or_die("%s\n\n", RECV_CMDLINE_PARSER_PACKAGE "-"
+ RECV_CMDLINE_PARSER_VERSION);
+ printf_or_die("%s\n\n", recv_args_info_usage);
+ for (; *p; p++)
+ printf_or_die("%s\n", *p);
+ print_receiver_helps(d);
+ exit(0);
+}
+static void *parse_config(int argc, char *argv[], int *receiver_num)
+{
if (recv_cmdline_parser(argc, argv, &conf))
return NULL;
HANDLE_VERSION_FLAG("recv", conf);
- if (conf.list_receivers_given) {
- printf("available receivers: ");
- for (i = 0; receivers[i].name; i++)
- printf("%s%s", i? " " : "", receivers[i].name);
- printf("\nTry\n\tpara_recv -r '<receivername> -h'\n"
- "for help on <receivername>.\n");
- exit(EXIT_SUCCESS);
- }
+ if (conf.help_given || conf.detailed_help_given)
+ print_help_and_die();
return check_receiver_arg(conf.receiver_arg, receiver_num);
}
s.default_timeout.tv_sec = 1;
s.default_timeout.tv_usec = 0;
+ memset(&sot, 0, sizeof(struct stdout_task));
memset(&rn, 0, sizeof(struct receiver_node));
- for (ret = 0; receivers[ret].name; ret++)
+ FOR_EACH_RECEIVER(ret)
receivers[ret].init(&receivers[ret]);
ret = -E_RECV_SYNTAX;
rn.conf = parse_config(argc, argv, &receiver_num);
-option "loglevel" l "set loglevel (0-6)" int typestr="level" default="4" optional
-option "list_receivers" L "print list of available receivers" flag off
-option "receiver" r "Select receiver.
+option "loglevel" l
+#~~~~~~~~~~~~~~~~~~
+"set loglevel (0-6)"
+int typestr="level"
+default="4"
+optional
-Any options for the selected receiver must
-be quoted. Example:
+option "receiver" r
+"Select receiver"
+string typestr="receiver_spec"
+default="http"
+optional
+details="
+ Any options for the selected receiver must
+ be quoted. Example:
- -r 'http -i www.paraslash.org -p 8009'
+ -r 'http -i www.paraslash.org -p 8009'
"
-string typestr="receiver_spec" default="http" optional
* \sa select(2), struct receiver.
*/
void (*post_select)(struct sched *s, struct task *t);
+
+ struct ggo_help help;
};
ORTP_RECEIVER \
{.name = NULL}};
+#define FOR_EACH_RECEIVER(i) for (i = 0; receivers[i].name; i++)
+
void *check_receiver_arg(char *ra, int *receiver_num);
+void print_receiver_helps(int detailed);
#include "list.h"
#include "sched.h"
+#include "ggo.h"
#include "recv.h"
#include "string.h"
PARA_ERROR_LOG("receiver not found\n");
return NULL;
}
+
+void print_receiver_helps(int detailed)
+{
+ int i;
+
+ printf_or_die("\nAvailable receivers: \n\t");
+ FOR_EACH_RECEIVER(i)
+ printf_or_die("%s%s", i? " " : "", receivers[i].name);
+ printf_or_die("\n\n");
+ FOR_EACH_RECEIVER(i) {
+ struct receiver *r = receivers + i;
+
+ if (!r->help.short_help)
+ continue;
+ printf_or_die("Options for %s:\n", r->name);
+ ggo_print_help(&r->help, detailed);
+ }
+}
/** Describes the current status of one paraslash sender. */
struct sender_status {
- /* The file descriptor of the socket this sender is listening on. */
+ /** The file descriptor of the socket this sender is listening on. */
int listen_fd;
/** The TCP/DCCP port used by this sender. */
int port;
* Close the file descriptor given by \a sc, remove it from the close-on-fork
* list, destroy the chunk queue of this client, delete the client from the
* list of connected clients and free the sender_client struct.
+ *
+ * \sa shutdown_clients().
*/
void shutdown_client(struct sender_client *sc, struct sender_status *ss)
{
ss->num_clients--;
}
+/**
+ * Shut down all clients connected to a paraslash sender.
+ *
+ * \param ss The sender whose clients are to be shut down.
+ *
+ * This just loops over all connected clients and calls shutdown_client()
+ * for each client.
+ */
void shutdown_clients(struct sender_status *ss)
{
struct sender_client *sc, *tmp;
* The gory details, listed by topic:
*
* - Audio format handlers: \ref send_common.c \ref mp3_afh.c, \ref ogg_afh.c, \ref aac_afh.c,
- * - Decoders: \ref mp3dec.c, \ref oggdec.c, \ref aacdec.c,
- * - Volume normalizer: \ref compress.c,
+ * - Decoders: \ref mp3dec_filter.c, \ref oggdec_filter.c, \ref aacdec_filter.c,
+ * - Volume normalizer: \ref compress_filter.c,
* - Output: \ref alsa_write.c, \ref osx_write.c,
* - http: \ref http_recv.c, \ref http_send.c,
* - ortp: \ref ortp_recv.c, \ref ortp_send.c,
* \param ll The log level.
* \param fmt The format string describing the log message.
*/
-void para_log(int ll, const char* fmt,...)
+__printf_2_3 void para_log(int ll, const char* fmt,...)
{
va_list argp;
FILE *outfd;
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'.
+ Where para_server should look for the osl database of the audio
+ file selector. The default is '~/.paraslash/afs_database'.
"
option "afs_socket" s
*/
static void generic_signal_handler(int s)
{
- write(signal_pipe[1], &s, sizeof(int));
- //fprintf(stderr, "got sig %i, write returned %d\n", s, ret);
+ ssize_t ret = write(signal_pipe[1], &s, sizeof(int));
+
+ if (ret == sizeof(int))
+ return;
+ if (ret < 0)
+ PARA_EMERG_LOG("%s\n", strerror(errno));
+ else
+ PARA_EMERG_LOG("short write to signal pipe\n");
+ exit(EXIT_FAILURE);
}
/**
static void stdin_pre_select(struct sched *s, struct task *t)
{
struct stdin_task *sit = container_of(t, struct stdin_task, task);
+
+ if (sit->output_error && *sit->output_error < 0) {
+ t->error = *sit->output_error;
+ return;
+ }
t->error = 0;
sit->check_fd = 0;
if (sit->loaded >= sit->bufsize)
struct stdin_task *sit = container_of(t, struct stdin_task, task);
ssize_t ret;
+ if (sit->output_error && *sit->output_error < 0) {
+ t->error = *sit->output_error;
+ return;
+ }
t->error = 0;
if (!sit->check_fd)
return;
ret = mark_fd_nonblocking(STDIN_FILENO);
if (ret >= 0)
return;
+ sit->output_error = NULL;
PARA_EMERG_LOG("%s\n", para_strerror(-ret));
exit(EXIT_FAILURE);
}
size_t bufsize;
/** Number of bytes currently loaded in \a buf. */
size_t loaded;
+ /** Pointer to the error member of the consumer. */
+ int *output_error;
/** Whether \p STDIN_FILENO was included in the read fd set. */
int check_fd;
/** The task structure. */
*/
__must_check __malloc void *para_malloc(size_t size)
{
- assert(size);
- void *p = malloc(size);
+ void *p;
+ assert(size);
+ p = malloc(size);
if (!p) {
PARA_EMERG_LOG("malloc failed (size = %zu), aborting\n",
size);
return ret;
}
+/**
+ * Compute when to send a chunk of an audio file.
+ *
+ * \param chunk_num The number of the chunk.
+ * \param chunk_tv The duration of one chunk.
+ * \param stream_start When the first chunk was sent.
+ * \param result The time when to send chunk number \a chunk_num.
+ *
+ * This function computes stream_start + chunk_num * chunk_time.
+ */
void compute_chunk_time(long unsigned chunk_num,
struct timeval *chunk_tv, struct timeval *stream_start,
struct timeval *result)
--- /dev/null
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.6 (GNU/Linux)
+
+iD8DBQBJM93LWto1QDEAkw8RAkqoAJ4otG8p97XWBP7x2o1zy1R3M80BVQCfZy7K
+WxcDbEGOvK1Vk0zhGPWr7gU=
+=Ni2V
+-----END PGP SIGNATURE-----
if (chk_barrier("data send", &vsst->data_send_barrier,
&due, 1) < 0)
return;
- mmd->new_vss_status_flags &= ~VSS_REPOS;
if (mmd->current_chunk >= mmd->afd.afhi.chunks_total) { /* eof */
mmd->new_vss_status_flags |= VSS_NEXT;
return;
free(hn);
free(home);
mmd->sender_cmd_data.cmd_num = -1;
+ make_empty_status_items(mmd->afd.verbose_ls_output);
if (conf.autoplay_given) {
struct timeval tmp;
mmd->vss_status_flags |= VSS_PLAYING;
+++ /dev/null
-/*
- * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
- *
- * Licensed under the GPL v2. For licencing details see COPYING.
- */
-
-/** \file wav.c a filter that inserts a wave header */
-
-#include "para.h"
-
-#include "list.h"
-#include "sched.h"
-#include "filter.h"
-#include "string.h"
-
-/** size of the output buffer */
-#define WAV_OUTBUF_SIZE 81920
-/** a wav header is always 44 bytes */
-#define WAV_HEADER_LEN 44
-/** always write 16 bit header */
-#define BITS 16
-
-/**
- * write a 32 bit unsigned value to a buffer
- *
- * \param buf the buffer to write to
- * \param x the value to write
- */
-#define WRITE_U32(buf, x) (buf)[0] = (unsigned char)((x) & 0xff);\
- (buf)[1] = (unsigned char)(((x) >> 8) & 0xff);\
- (buf)[2] = (unsigned char)(((x) >> 16) & 0xff);\
- (buf)[3] = (unsigned char)(((x) >> 24) & 0xff);
-
-/**
- * write a 16 bit unsigned value to a buffer
- *
- * \param buf the buffer to write to
- * \param x the value to write
- */
-#define WRITE_U16(buf, x) (buf)[0] = (unsigned char)((x) & 0xff);
-
-static void make_wav_header(unsigned int channels, unsigned int samplerate,
- struct filter_node *fn)
-{
-
- char *headbuf = fn->buf;
- unsigned int size = 0x7fffffff;
- int bytespersec = channels * samplerate * BITS / 8;
- int align = channels * BITS / 8;
-
- PARA_DEBUG_LOG("writing wave header: %d channels, %d KHz\n", channels, samplerate);
- memset(headbuf, 0, WAV_HEADER_LEN);
- memcpy(headbuf, "RIFF", 4);
- WRITE_U32(headbuf + 4, size - 8);
- memcpy(headbuf + 8, "WAVE", 4);
- memcpy(headbuf + 12, "fmt ", 4);
- WRITE_U32(headbuf + 16, 16);
- WRITE_U16(headbuf + 20, 1); /* format */
- WRITE_U16(headbuf + 22, channels);
- WRITE_U32(headbuf + 24, samplerate);
- WRITE_U32(headbuf + 28, bytespersec);
- WRITE_U16(headbuf + 32, align);
- WRITE_U16(headbuf + 34, BITS);
- memcpy(headbuf + 36, "data", 4);
- WRITE_U32(headbuf + 40, size - 44);
-}
-
-static ssize_t wav_convert(char *inbuf, size_t len, struct filter_node *fn)
-{
- size_t copy;
- int *bof = fn->private_data;
-
- if (*bof) {
- make_wav_header(fn->fc->channels, fn->fc->samplerate, fn);
- fn->loaded = WAV_HEADER_LEN;
- *bof = 0;
-// return 0;
- }
- copy = PARA_MIN(len, fn->bufsize - fn->loaded);
- memmove(fn->buf + fn->loaded, inbuf, copy);
- fn->loaded += copy;
-// PARA_DEBUG_LOG("len = %d, copy = %d\n", len, copy);
- return copy;
-}
-
-static void wav_close(struct filter_node *fn)
-{
- free(fn->buf);
- fn->buf = NULL;
- free(fn->private_data);
- fn->private_data = NULL;
-}
-
-static void wav_open(struct filter_node *fn)
-{
- int *bof;
-
- fn->bufsize = WAV_OUTBUF_SIZE;
- fn->buf = para_malloc(fn->bufsize);
- fn->private_data = para_malloc(sizeof(int));
- bof = fn->private_data;
- *bof = 1;
- PARA_INFO_LOG("wav filter node: %p, output buffer: %p, loaded: %zd\n",
- fn, fn->buf, fn->loaded);
-}
-
-/**
- * the init function of the wav filter
- *
- * \param f struct to initialize
- */
-void wav_init(struct filter *f)
-{
- f->convert = wav_convert;
- f->close = wav_close;
- f->open = wav_open;
-}
--- /dev/null
+/*
+ * Copyright (C) 2005-2008 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file wav_filter.c A filter that inserts a wave header. */
+
+#include "para.h"
+
+#include "list.h"
+#include "sched.h"
+#include "ggo.h"
+#include "filter.h"
+#include "string.h"
+#include "portable_io.h"
+
+/** size of the output buffer */
+#define WAV_OUTBUF_SIZE 81920
+/** a wav header is always 44 bytes */
+#define WAV_HEADER_LEN 44
+/** always write 16 bit header */
+#define BITS 16
+
+static void make_wav_header(unsigned int channels, unsigned int samplerate,
+ struct filter_node *fn)
+{
+
+ char *headbuf = fn->buf;
+ unsigned int size = 0x7fffffff;
+ int bytespersec = channels * samplerate * BITS / 8;
+ int align = channels * BITS / 8;
+
+ assert(channels);
+ PARA_DEBUG_LOG("writing wave header: %d channels, %d KHz\n", channels, samplerate);
+ memset(headbuf, 0, WAV_HEADER_LEN);
+ memcpy(headbuf, "RIFF", 4);
+ write_u32(headbuf + 4, size - 8);
+ memcpy(headbuf + 8, "WAVE", 4);
+ memcpy(headbuf + 12, "fmt ", 4);
+ write_u32(headbuf + 16, 16);
+ write_u16(headbuf + 20, 1); /* format */
+ write_u16(headbuf + 22, channels);
+ write_u32(headbuf + 24, samplerate);
+ write_u32(headbuf + 28, bytespersec);
+ write_u16(headbuf + 32, align);
+ write_u16(headbuf + 34, BITS);
+ memcpy(headbuf + 36, "data", 4);
+ write_u32(headbuf + 40, size - 44);
+}
+
+static ssize_t wav_convert(char *inbuf, size_t len, struct filter_node *fn)
+{
+ size_t copy;
+ int *bof = fn->private_data;
+
+ if (*bof) {
+ if (!len)
+ return 0;
+ make_wav_header(fn->fc->channels, fn->fc->samplerate, fn);
+ fn->loaded = WAV_HEADER_LEN;
+ *bof = 0;
+// return 0;
+ }
+ copy = PARA_MIN(len, fn->bufsize - fn->loaded);
+ memmove(fn->buf + fn->loaded, inbuf, copy);
+ fn->loaded += copy;
+// PARA_DEBUG_LOG("len = %d, copy = %d\n", len, copy);
+ return copy;
+}
+
+static void wav_close(struct filter_node *fn)
+{
+ free(fn->buf);
+ fn->buf = NULL;
+ free(fn->private_data);
+ fn->private_data = NULL;
+}
+
+static void wav_open(struct filter_node *fn)
+{
+ int *bof;
+
+ fn->bufsize = WAV_OUTBUF_SIZE;
+ fn->buf = para_malloc(fn->bufsize);
+ fn->private_data = para_malloc(sizeof(int));
+ bof = fn->private_data;
+ fn->loaded = 0;
+ *bof = 1;
+ PARA_INFO_LOG("wav filter node: %p, output buffer: %p, loaded: %zd\n",
+ fn, fn->buf, fn->loaded);
+}
+
+/**
+ * the init function of the wav filter
+ *
+ * \param f struct to initialize
+ */
+void wav_filter_init(struct filter *f)
+{
+ f->convert = wav_convert;
+ f->close = wav_close;
+ f->open = wav_open;
+}
<h1>Events</h1>
<hr>
<ul>
+ <li>2008-12-01: <a href="versions/paraslash-0.3.3.tar.bz2">paraslash-0.3.3</a>
+ <a href="versions/paraslash-0.3.3.tar.bz2.asc">(sig)</a>
+ "axiomatic perspectivity"
+ </li>
<li>2008-04-11: <a href="versions/paraslash-0.3.2.tar.bz2">paraslash-0.3.2</a>
<a href="versions/paraslash-0.3.2.tar.bz2.asc">(sig)</a>
"probabilistic parity"
#include "write.cmdline.h"
#include "list.h"
#include "sched.h"
+#include "ggo.h"
#include "stdin.h"
#include "write.h"
#include "write_common.h"
unsigned channels;
/** Sample rate specified in wav header given by \a buf. */
unsigned samplerate;
+ /** The task structure used by the scheduler. */
struct task task;
};
if (!idt->start_time.tv_sec && !idt->start_time.tv_usec) {
t->error = -E_NO_DELAY;
- return;
+ goto register_check_wav;
}
if (tv_diff(now, &idt->start_time, &diff) > 0) {
t->error = -E_DELAY_TIMEOUT;
- return;
+ goto register_check_wav;
}
if (tv_diff(&s->timeout , &diff, NULL) > 0)
s->timeout = diff;
+ return;
+register_check_wav:
+ register_task(&the_check_wav_task.task);
+ s->timeout.tv_sec = 0;
+ s->timeout.tv_usec = 1;
}
INIT_STDERR_LOGGING(conf.loglevel_arg)
struct writer_node_group *g = NULL;
struct initial_delay_task *idt = &the_initial_delay_task;
- if (conf.list_writers_given) {
- char *msg = NULL;
- FOR_EACH_WRITER(i) {
- char *tmp = make_message("%s%s%s",
- i? msg : "",
- i? " " : "",
- writer_names[i]);
- free(msg);
- msg = tmp;
- }
- fprintf(stderr, "%s\n", msg);
- free(msg);
- exit(EXIT_SUCCESS);
- }
if (conf.start_time_given) {
long unsigned sec, usec;
if (sscanf(conf.start_time_arg, "%lu:%lu",
return NULL;
}
+__noreturn static void print_help_and_die(void)
+{
+ int d = conf.detailed_help_given;
+ const char **p = d? write_args_info_detailed_help
+ : write_args_info_help;
+
+ printf_or_die("%s\n\n", WRITE_CMDLINE_PARSER_PACKAGE "-"
+ WRITE_CMDLINE_PARSER_VERSION);
+ printf_or_die("%s\n\n", write_args_info_usage);
+ for (; *p; p++)
+ printf_or_die("%s\n", *p);
+ print_writer_helps(d);
+ exit(0);
+}
+
/**
* Para_write's main function.
*
struct check_wav_task *cwt = &the_check_wav_task;
struct initial_delay_task *idt = &the_initial_delay_task;
+ init_supported_writers();
write_cmdline_parser(argc, argv, &conf);
HANDLE_VERSION_FLAG("write", conf);
- init_supported_writers();
+ if (conf.help_given || conf.detailed_help_given)
+ print_help_and_die();
wng = check_args();
if (!wng)
goto out;
stdin_set_defaults(&sit);
- if (conf.bufsize_given)
- sit.bufsize = conf.bufsize_arg;
+ ret = -ERRNO_TO_PARA_ERROR(EINVAL);
+ if (conf.bufsize_arg < 0)
+ goto out;
+ if (conf.bufsize_arg >= INT_MAX / 1024)
+ goto out;
+ sit.bufsize = conf.bufsize_arg * 1024;
sit.buf = para_malloc(sit.bufsize);
wng->buf = sit.buf;
cwt->input_error = &sit.task.error;
sprintf(cwt->task.status, "check wav");
cwt->task.pre_select = check_wav_pre_select;
- register_task(&cwt->task);
idt->task.pre_select = initial_delay_pre_select;
sprintf(idt->task.status, "initial_delay");
-section "general options"
-#########################
-
-option "list_writers" L
-#~~~~~~~~~~~~~~~~~~~~~~
-"print available writers and exit"
-
- flag off
-
option "loglevel" l
#~~~~~~~~~~~~~~~~~~
"set loglevel (0-6)"
-
- int typestr="level"
- default="4"
- optional
+int typestr="level"
+default="4"
+optional
option "bufsize" b
#~~~~~~~~~~~~~~~~~
"input buffer size"
-
- int typestr="kilobytes"
- default="64"
- optional
+int typestr="kilobytes"
+default="64"
+optional
option "writer" w
#~~~~~~~~~~~~~~~~
-
-"select stream writer
-may be give multiple times. The same writer
-may be specified more than once"
-
- string typestr="name"
- default="alsa (file if alsa is unsupported)"
- optional
- multiple
+"select stream writer"
+string typestr="name"
+default="alsa (file if alsa is unsupported)"
+optional
+multiple
+details="
+ May be give multiple times. The same writer may be specified
+ more than once.
+"
option "start_time" t
#~~~~~~~~~~~~~~~~~~~~
-"start playback at given time which must be
-in a:b format where a denotes seconds and b
-denotes microseconds since the epoch"
-
- string typestr="timeval"
- optional
+"defer playback"
+string typestr="timeval"
+optional
+details="
+ Start playback at given time which must be in a:b format where
+ a denotes seconds and b denotes microseconds since the epoch.
+"
* Licensed under the GPL v2. For licencing details see COPYING.
*/
-/** \file write.h writer-related structures */
+/** \file write.h Writer-related structures. */
-/** the list of supported writers */
+/** The list of supported writers. */
enum writer_enum {WRITER_ENUM};
/**
* Describes one running instance of a writer.
*/
struct writer_node {
- /** points to the writer structure associated with this node */
- struct writer *writer; /* FIXME: Should better be only the number */
- /** writer-specific data */
+ /** Points to the writer structure associated with this node. */
+ struct writer *writer; /* FIXME: Should better be only the number. */
+ /** Writer-specific data. */
void *private_data;
- /** send that many bytes in one go */
+ /** Send that many bytes in one go. */
int chunk_bytes;
- /** pointer to the group this node belongs to */
+ /** Pointer to the group this node belongs to. */
struct writer_node_group *wng;
- /** the writer-specific configuration of this node */
+ /** The writer-specific configuration of this node. */
void *conf;
- /** how much of the wng's buffer is already written */
+ /** How much of the wng's buffer is already written. */
size_t written;
};
-/** describes one supported writer */
+/** Describes one supported writer. */
struct writer {
/**
- * the init function of the writer
+ * The init function of the writer.
*
* It must fill in all other function pointers of the given
* writer structure.
- *
*/
void (*init)(struct writer *w);
/**
- *
- *
- * the command line parser of the writer
+ * The command line parser of the writer.
*
* It should check whether the command line options given by \a options are
* valid. On success, it should return a pointer to the writer-specific
* configuration data determined by \a options. Note that this might be called
* more than once with different values of \a options.
- *
*/
- void * (*parse_config)(const char *options);
+ void *(*parse_config)(const char *options);
/**
- *
- * open one instance of this writer
+ * Open one instance of this writer.
*
* This function should perform any work necessary to write the incoming
* stream. If To this aim, it may allocate its private data structure and store
*/
int (*open)(struct writer_node *);
/**
- *
- * write a chunk of audio data
+ * Prepare the fd sets for select.
*
* This is called from the writer node group task's pre_select(). It
* may use the sched pointer to add any file descriptors or to decrease
*/
int (*pre_select)(struct sched *s, struct writer_node *wn);
/**
+ * Write audio data.
+ *
* Called from the post_select function of the wng task. It must keep
* track of the number of bytes consumed from the wng's buffer via
- * the wn->written variable (which may be modified by the wng handling
+ * the \p wn->written variable (which may be modified by the wng handling
* functions). This function must return positive on success and
* negative on errors.
*/
int (*post_select)(struct sched *s, struct writer_node *wn);
/**
- * close one instance of the writer
+ * Close one instance of the writer.
*
* This function is assumed to succeed.
*/
void (*close)(struct writer_node *);
/**
- * shutdown the writer
+ * Shutdown the writer
*
- * This is a optional function pointer used for cleaning
- * up.
+ * This is a optional function pointer used for cleaning up.
*/
void (*shutdown)(struct writer_node *);
+ struct ggo_help help;
};
/**
- * describes a set of writer nodes that all write the same stream.
+ * Describes a set of writer nodes that all write the same stream.
*/
struct writer_node_group {
- /** number of nodes belonging to this group */
+ /** Number of nodes belonging to this group. */
unsigned num_writers;
- /** array of pointers to the corresponding writer nodes */
+ /** Array of pointers to the corresponding writer nodes. */
struct writer_node *writer_nodes;
- /** the maximum of the chunk_bytes values of the writer nodes in this group */
- size_t max_chunk_bytes;
+ /** The maximum of the chunk_bytes values of the writer nodes in this group. */
+ int max_chunk_bytes;
/** Non-zero if an error or end of file was encountered by the feeding task. */
int *input_error;
- /** current output buffer */
+ /** Current output buffer. */
char *buf;
- /** number of bytes loaded in the output buffer */
+ /** Number of bytes loaded in the output buffer. */
size_t *loaded;
- /** number of audio channels of the current stream */
+ /** Number of audio channels of the current stream. */
unsigned int *channels;
- /** sample rate of the current stream */
+ /** Sample rate of the current stream. */
unsigned int *samplerate;
- /** the task associated to this group */
+ /** The task associated to this group. */
struct task task;
/** Whether the group is open, i.e. wng_open() was called. */
int open;
};
-/** loop over each writer node in a writer group */
+/** Loop over each writer node in a writer group. */
#define FOR_EACH_WRITER_NODE(i, wng) for (i = 0; i < (wng)->num_writers; i++)
-/** loop over each supported writer */
+/** Loop over each supported writer. */
#define FOR_EACH_WRITER(i) for (i = 0; i < NUM_SUPPORTED_WRITERS; i++)
-/** declare the init functions of all supported writers */
+/** Declare the init functions of all supported writers. */
DECLARE_WRITER_INITS;
-/** array containing the name of each writer */
+/** Array containing the name of each writer. */
extern const char *writer_names[];
-/** the writer structure for each supported writer */
+/** The writer structure for each supported writer. */
extern struct writer writers[NUM_SUPPORTED_WRITERS];
#include "string.h"
#include "list.h"
#include "sched.h"
+#include "ggo.h"
#include "write.h"
#include "error.h"
wng->writer_nodes[0].conf = writers[DEFAULT_WRITER].parse_config("");
return wng;
}
+
+void print_writer_helps(int detailed)
+{
+ int i;
+
+ printf_or_die("\nAvailable writers: \n\t");
+ FOR_EACH_WRITER(i)
+ printf_or_die("%s%s", i? " " : "", writer_names[i]);
+ printf_or_die("\n\n");
+ FOR_EACH_WRITER(i) {
+ struct writer *w = writers + i;
+
+ if (!w->help.short_help)
+ continue;
+ printf_or_die("Options for %s:\n", writer_names[i]);
+ ggo_print_help(&w->help, detailed);
+ }
+}
int wng_open(struct writer_node_group *g);
void wng_close(struct writer_node_group *g);
struct writer_node_group *wng_new(unsigned num_writers);
-void wng_unregister(struct writer_node_group *g);
void init_supported_writers(void);
void *check_writer_arg(const char *wa, int *writer_num);
struct writer_node_group *setup_default_wng(void);
+void print_writer_helps(int detailed);