]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 't/const'
authorAndre Noll <maan@systemlinux.org>
Sun, 15 May 2011 07:38:04 +0000 (09:38 +0200)
committerAndre Noll <maan@systemlinux.org>
Sun, 15 May 2011 07:38:04 +0000 (09:38 +0200)
FEATURES
Makefile.in
NEWS
ao_write.c [new file with mode: 0644]
audiod.c
configure.ac
error.h
fd.c
ggo/.gitignore
ggo/ao_write.m4 [new file with mode: 0644]
web/manual.m4

index 5b5f4699d5123abc5919aaf45369bd804dfefde5..2839d79cc0393f4ee30afa9b86ed7f2a3c3d126f 100644 (file)
--- a/FEATURES
+++ b/FEATURES
@@ -6,7 +6,9 @@ Features
        * Runs on Linux, Mac OS, FreeBSD, NetBSD, Solaris and probably other
          Unixes
        * Mp3, ogg/vorbis, ogg/speex, aac (m4a) and wma support
-       * Local or remote http, dccp, and udp network audio streaming
+       * Native Alsa, OSS, CoreAudio output support
+       * Support for ESD, Pulseaudio, AIX, Solaris, IRIX through libao
+       * Local or remote http, dccp and udp network audio streaming
        * IPv6 support
        * Forward error correction allows receivers to recover from packet losses
        * Volume normalizer
index ca79f8b9948235970e31da5c9ecf2a96a53b9672..cfc6909a7e1737466b0042ee455aee53bed64fc8 100644 (file)
@@ -196,6 +196,9 @@ $(object_dir)/aac_afh.o: aac_afh.c | $(object_dir)
 $(object_dir)/gui%.o: gui%.c | $(object_dir)
        @[ -z "$(Q)" ] || echo 'CC $<'
        $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @curses_cppflags@ $<
+$(object_dir)/ao_write.o: ao_write.c | $(object_dir)
+       @[ -z "$(Q)" ] || echo 'CC $<'
+       $(Q) $(CC) -c -o $@ $(CPPFLAGS) $(DEBUG_CPPFLAGS) @ao_cppflags@ $<
 
 $(object_dir)/%.cmdline.o: $(cmdline_dir)/%.cmdline.c $(cmdline_dir)/%.cmdline.h | $(object_dir)
        @[ -z "$(Q)" ] || echo 'CC $<'
diff --git a/NEWS b/NEWS
index 3295738632ef423ca4b36f68f3bcdc533d873e38..4430343ec73e641f401263aee9e3dbefb2aa5993 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -2,6 +2,8 @@
 0.4.7 (to be announced) "infinite rollback"
 -------------------------------------------
 
+       - Support for ESD, Pulseaudio, AIX, Solaris, IRIX and other
+         platforms through the libao audio library.
        - configure: improved options for ogg/vorbis/speex.
        - The git version reported by --version always matches HEAD.
        - The autogen script detects the number of processors and
diff --git a/ao_write.c b/ao_write.c
new file mode 100644 (file)
index 0000000..bb60e19
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * Copyright (C) 2011 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file ao_write.c Paraslash's libao output plugin. */
+
+#include <pthread.h>
+#include <ao/ao.h>
+#include <regex.h>
+#include <stdbool.h>
+
+#include "para.h"
+#include "fd.h"
+#include "string.h"
+#include "list.h"
+#include "sched.h"
+#include "ggo.h"
+#include "buffer_tree.h"
+#include "write.h"
+#include "write_common.h"
+#include "ao_write.cmdline.h"
+#include "error.h"
+
+struct private_aow_data {
+       ao_device *dev;
+       int bytes_per_frame;
+
+       pthread_t thread;
+       pthread_attr_t attr;
+       pthread_mutex_t mutex;
+       pthread_cond_t data_available;
+       struct btr_node *thread_btrn;
+};
+
+static void aow_close(struct writer_node *wn)
+{
+       struct private_aow_data *pawd = wn->private_data;
+
+       if (!pawd)
+               return;
+       ao_close(pawd->dev);
+       free(pawd);
+       wn->private_data = NULL;
+       ao_shutdown();
+}
+
+static void aow_pre_select(struct sched *s, struct task *t)
+{
+       struct writer_node *wn = container_of(t, struct writer_node, task);
+       int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
+
+       if (ret == 0)
+               return;
+       sched_min_delay(s);
+}
+
+static int aow_set_sample_format(unsigned sample_rate, unsigned channels,
+               int sample_format, ao_sample_format *result)
+{
+       memset(result, 0, sizeof(*result));
+       switch (sample_format) {
+               case SF_U8:
+               case SF_U16_LE:
+               case SF_U16_BE:
+                       return -E_AO_BAD_SAMPLE_FORMAT;
+               case SF_S8:
+                       /* no need to set byte_format */
+                       result->bits = 8;
+                       break;
+               case SF_S16_LE:
+                       result->bits = 16;
+                       result->byte_format = AO_FMT_LITTLE;
+                       break;
+               case SF_S16_BE:
+                       result->bits = 16;
+                       result->byte_format = AO_FMT_BIG;
+                       break;
+               default:
+                       PARA_EMERG_LOG("bug: invalid sample format\n");
+                       exit(EXIT_FAILURE);
+       }
+       result->channels = channels;
+       result->rate = sample_rate;
+       return 1;
+}
+
+static int aow_open_device(int id, ao_sample_format *asf, ao_option *options,
+               ao_device **result)
+{
+       const char *msg;
+       ao_device *dev = ao_open_live(id, asf, options);
+
+       if (dev) {
+               *result = dev;
+               return 1;
+       }
+       switch (errno) {
+               case AO_ENODRIVER:
+                       msg = "No driver corresponds to driver_id";
+                       break;
+               case AO_ENOTLIVE:
+                       msg = "This driver is not a live output device";
+                       break;
+               case AO_EBADOPTION:
+                       msg = "A valid option key has an invalid value";
+                       break;
+               case AO_EOPENDEVICE:
+                       msg = "Cannot open the device";
+                       break;
+               case AO_EFAIL:
+                       msg = "General libao error";
+                       break;
+               default:
+                       msg = "Unknown ao error";
+                       break;
+       }
+       PARA_ERROR_LOG("%s\n", msg);
+       return -E_AO_OPEN_LIVE;
+}
+
+static int aow_init(struct writer_node *wn, unsigned sample_rate,
+               unsigned channels, int sample_format)
+{
+       int id, ret, i;
+       ao_option *aoo = NULL;
+       ao_sample_format asf;
+       ao_info *info;
+       struct private_aow_data *pawd = para_malloc(sizeof(*pawd));
+       struct ao_write_args_info *conf = wn->conf;
+
+       ao_initialize();
+       if (conf->driver_given) {
+               ret = -E_AO_BAD_DRIVER;
+               id = ao_driver_id(conf->driver_arg);
+       } else {
+               ret = -E_AO_DEFAULT_DRIVER;
+               id = ao_default_driver_id();
+       }
+       if (id < 0)
+               goto fail;
+       info = ao_driver_info(id);
+       assert(info && info->short_name);
+       if (info->type == AO_TYPE_FILE) {
+               ret = -E_AO_FILE_NOT_SUPP;
+               goto fail;
+       }
+       PARA_INFO_LOG("using %s driver\n", info->short_name);
+       for (i = 0; i < conf->ao_option_given; i++) {
+               char *o = para_strdup(conf->ao_option_arg[i]), *value;
+
+               ret = -E_AO_BAD_OPTION;
+               value = strchr(o, ':');
+               if (!value) {
+                       free(o);
+                       goto fail;
+               }
+               *value = '\0';
+               value++;
+               PARA_INFO_LOG("appending option: key=%s, value=%s\n", o, value);
+               ret = ao_append_option(&aoo, o, value);
+               free(o);
+               if (ret == 0) {
+                       ret = -E_AO_APPEND_OPTION;
+                       goto fail;
+               }
+       }
+       ret = aow_set_sample_format(sample_rate, channels, sample_format, &asf);
+       if (ret < 0)
+               goto fail;
+       if (sample_format == SF_S8 || sample_format == SF_U8)
+               pawd->bytes_per_frame = channels;
+       else
+               pawd->bytes_per_frame = channels * 2;
+       ret = aow_open_device(id, &asf, aoo, &pawd->dev);
+       if (ret < 0)
+               goto fail;
+       PARA_INFO_LOG("successfully opened %s\n", info->short_name);
+       wn->private_data = pawd;
+       return 1;
+fail:
+       free(pawd);
+       return ret;
+}
+
+__noreturn static void *aow_play(void *priv)
+{
+       struct writer_node *wn = priv;
+       struct private_aow_data *pawd = wn->private_data;
+       struct btr_node *btrn = pawd->thread_btrn;
+       size_t frames, bytes;
+       char *data;
+       int ret;
+
+       for (;;) {
+               /*
+                * Lock mutex and wait for signal. pthread_cond_wait() will
+                * automatically and atomically unlock mutex while it waits.
+                */
+               pthread_mutex_lock(&pawd->mutex);
+               for (;;) {
+                       ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
+                       if (ret < 0)
+                               goto unlock;
+                       if (ret > 0) {
+                               btr_merge(btrn, wn->min_iqs);
+                               bytes = btr_next_buffer(btrn, &data);
+                               frames = bytes / pawd->bytes_per_frame;
+                               if (frames > 0)
+                                       break;
+                               /* eof and less than a single frame available */
+                               ret = -E_WRITE_COMMON_EOF;
+                               goto unlock;
+                       }
+                       //PARA_CRIT_LOG("waiting for data\n");
+                       //usleep(1000);
+                       //pthread_mutex_unlock(&pawd->mutex);
+                       pthread_cond_wait(&pawd->data_available, &pawd->mutex);
+               }
+               pthread_mutex_unlock(&pawd->mutex);
+               assert(frames > 0);
+               bytes = frames * pawd->bytes_per_frame;
+               ret = -E_AO_PLAY;
+               if (ao_play(pawd->dev, data, bytes) == 0) /* failure */
+                       goto out;
+               btr_consume(btrn, bytes);
+       }
+unlock:
+       pthread_mutex_unlock(&pawd->mutex);
+out:
+       assert(ret < 0);
+       PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
+       pthread_exit(NULL);
+}
+
+static int aow_create_thread(struct writer_node *wn)
+{
+       struct private_aow_data *pawd = wn->private_data;
+       int ret;
+       const char *msg;
+
+       /* initialize with default attributes */
+       msg = "could not init mutex";
+       ret = pthread_mutex_init(&pawd->mutex, NULL);
+       if (ret < 0)
+               goto fail;
+
+       msg = "could not initialize condition variable";
+       ret = pthread_cond_init(&pawd->data_available, NULL);
+       if (ret < 0)
+               goto fail;
+
+       msg = "could not initialize thread attributes";
+       ret = pthread_attr_init(&pawd->attr);
+       if (ret < 0)
+               goto fail;
+
+       /* schedule this thread under the real-time policy SCHED_FIFO */
+       msg = "could not set sched policy";
+       ret = pthread_attr_setschedpolicy(&pawd->attr, SCHED_FIFO);
+       if (ret < 0)
+               goto fail;
+
+       msg = "could not set detach state to joinable";
+       ret = pthread_attr_setdetachstate(&pawd->attr, PTHREAD_CREATE_JOINABLE);
+       if (ret < 0)
+               goto fail;
+
+       msg = "could not create thread";
+       ret = pthread_create(&pawd->thread, &pawd->attr, aow_play, wn);
+       if (ret < 0)
+               goto fail;
+       return 1;
+fail:
+       PARA_ERROR_LOG("%s (%s)\n", msg, strerror(ret));
+       return -E_AO_PTHREAD;
+}
+
+static void aow_post_select(__a_unused struct sched *s,
+               struct task *t)
+{
+       struct writer_node *wn = container_of(t, struct writer_node, task);
+       struct btr_node *btrn = wn->btrn;
+       struct private_aow_data *pawd = wn->private_data;
+       int ret;
+
+       if (!pawd) {
+               int32_t rate, ch, format;
+               struct btr_node_description bnd;
+
+               ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
+               if (ret < 0)
+                       goto remove_btrn;
+               if (ret == 0)
+                       return;
+               get_btr_sample_rate(btrn, &rate);
+               get_btr_channels(btrn, &ch);
+               get_btr_sample_format(btrn, &format);
+               ret = aow_init(wn, rate, ch, format);
+               if (ret < 0)
+                       goto remove_btrn;
+               pawd = wn->private_data;
+
+               /* set up thread btr node */
+               bnd.name = "ao_thread_btrn";
+               bnd.parent = btrn;
+               bnd.child = NULL;
+               bnd.handler = NULL;
+               bnd.context = pawd;
+               pawd->thread_btrn = btr_new_node(&bnd);
+               wn->private_data = pawd;
+
+               ret = aow_create_thread(wn);
+               if (ret < 0)
+                       goto remove_thread_btrn;
+               return;
+       }
+       pthread_mutex_lock(&pawd->mutex);
+       ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
+       if (ret > 0) {
+               btr_pushdown(btrn);
+               pthread_cond_signal(&pawd->data_available);
+       }
+       pthread_mutex_unlock(&pawd->mutex);
+       if (ret >= 0)
+               goto out;
+       pthread_mutex_lock(&pawd->mutex);
+       btr_remove_node(btrn);
+       btrn = NULL;
+       PARA_INFO_LOG("waiting for thread to terminate\n");
+       pthread_cond_signal(&pawd->data_available);
+       pthread_mutex_unlock(&pawd->mutex);
+       pthread_join(pawd->thread, NULL);
+remove_thread_btrn:
+       btr_remove_node(pawd->thread_btrn);
+       btr_free_node(pawd->thread_btrn);
+remove_btrn:
+       if (btrn)
+               btr_remove_node(btrn);
+out:
+       t->error = ret;
+}
+
+__malloc static void *aow_parse_config_or_die(const char *options)
+{
+       struct ao_write_args_info *conf = para_calloc(sizeof(*conf));
+
+       /* exits on errors */
+       ao_cmdline_parser_string(options, conf, "ao_write");
+       return conf;
+}
+
+static void aow_free_config(void *conf)
+{
+       ao_cmdline_parser_free(conf);
+}
+
+/**
+ * The init function of the ao writer.
+ *
+ * \param w Pointer to the writer to initialize.
+ *
+ * \sa struct writer.
+ */
+void ao_write_init(struct writer *w)
+{
+       struct ao_write_args_info dummy;
+       int i, j, num_drivers, num_lines;
+       ao_info **driver_list;
+       char **dh; /* detailed help */
+
+       ao_cmdline_parser_init(&dummy);
+       w->close = aow_close;
+       w->pre_select = aow_pre_select;
+       w->post_select = aow_post_select;
+       w->parse_config_or_die = aow_parse_config_or_die;
+       w->free_config = aow_free_config;
+       w->shutdown = NULL;
+       w->help = (struct ggo_help) {
+               .short_help = ao_write_args_info_help,
+       };
+       /* create detailed help containing all supported drivers/options */
+       for (i = 0; ao_write_args_info_detailed_help[i]; i++)
+               ; /* nothing */
+       num_lines = i;
+       dh = para_malloc((num_lines + 3) * sizeof(char *));
+       for (i = 0; i < num_lines; i++)
+               dh[i] = para_strdup(ao_write_args_info_detailed_help[i]);
+       dh[num_lines++] = para_strdup("libao drivers available on this host:");
+       dh[num_lines++] = para_strdup("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
+
+       ao_initialize();
+       driver_list = ao_driver_info_list(&num_drivers);
+
+       for (i = 0; i < num_drivers; i++) {
+               ao_info *info = driver_list[i];
+               char *keys = NULL, *tmp = NULL;
+
+               if (info->type == AO_TYPE_FILE)
+                       continue;
+               for (j = 0; j < info->option_count; j++) {
+                       tmp = make_message("%s%s%s", keys? keys : "",
+                               keys? ", " : "",
+                               info->options[j]);
+                       free(keys);
+                       keys = tmp;
+               }
+               dh = para_realloc(dh, (num_lines + 6) * sizeof(char *));
+               dh[num_lines++] = make_message("%s: %s", info->short_name, info->name);
+               dh[num_lines++] = make_message("priority: %d", info->priority);
+               dh[num_lines++] = make_message("keys: %s", keys? keys : "[none]");
+               dh[num_lines++] = make_message("comment: %s", info->comment?
+                       info->comment : "[none]");
+               dh[num_lines++] = para_strdup(NULL);
+               free(keys);
+       }
+       dh[num_lines] = NULL;
+       w->help.detailed_help = (const char **)dh;
+       ao_cmdline_parser_free(&dummy);
+       ao_shutdown();
+}
+
index 9f664e178309c39e70a64d7ea55f6a29f0c5f6cf..6a4c9dbead6c61bbaaedfb484369749404dbe7b5 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -429,22 +429,15 @@ static void kill_btrn(struct btr_node *btrn, struct task *t, int error)
 
 static void kill_all_decoders(int error)
 {
-       int i, j;
+       int i;
 
        FOR_EACH_SLOT(i) {
-               struct slot_info *s = &slot[i];
-               struct audio_format_info *a;
+               struct slot_info *s = slot + i;
                if (s->format < 0)
                        continue;
-               a = afi + s->format;
-               if (s->wns)
-                       for (j = 0; j < a->num_writers; j++)
-                               kill_btrn(s->wns[j].btrn, &s->wns[j].task, error);
-               if (s->fns)
-                       for (j = 0; j < a->num_writers; j++)
-                               kill_btrn(s->fns[j].btrn, &s->wns[j].task, error);
-               if (s->receiver_node)
-                       kill_btrn(s->receiver_node->btrn, &s->receiver_node->task,
+               if (!s->receiver_node)
+                       continue;
+               kill_btrn(s->receiver_node->btrn, &s->receiver_node->task,
                                error);
        }
 }
index 32381c6b1ceec6dfd2586a1f73c0cdd4804ce855..8904281235c49caaef2211d008f399c80de19e26 100644 (file)
@@ -727,6 +727,67 @@ fi
 CPPFLAGS="$OLD_CPPFLAGS"
 LDFLAGS="$OLD_LDFLAGS"
 LIBS="$OLD_LIBS"
+########################################################################### libao
+OLD_CPPFLAGS="$CPPFLAGS"
+OLD_LD_FLAGS="$LDFLAGS"
+OLD_LIBS="$LIBS"
+
+have_ao="yes"
+AC_ARG_WITH(ao_headers, [AC_HELP_STRING(--with-ao-headers=dir,
+       [look for ao/ao.h also in dir])])
+if test -n "$with_ao_headers"; then
+       ao_cppflags="-I$with_ao_headers"
+       CPPFLAGS="$CPPFLAGS $ao_cppflags"
+fi
+AC_ARG_WITH(ao_libs, [AC_HELP_STRING(--with-ao-libs=dir,
+       [look for libao also in dir])])
+if test -n "$with_ao_libs"; then
+       ao_libs="-L$with_ao_libs"
+       LDFLAGS="$LDFLAGS $ao_libs"
+fi
+msg="no libao support for para_audiod/para_write"
+AC_CHECK_HEADERS([ao/ao.h], [
+       ], [
+       have_ao="no"
+       AC_MSG_WARN([ao.h not found, $msg])
+])
+if test "$have_ao" = "yes"; then
+       AC_CHECK_LIB([ao], [ao_initialize], [], [
+               have_ao="no"
+               AC_MSG_WARN([ao lib not found or not working, $msg])
+       ])
+fi
+if test "$have_ao" = "yes"; then
+       AC_CHECK_HEADERS([pthread.h], [
+               ], [
+               have_ao="no"
+               AC_MSG_WARN([pthread.h not found, $msg])
+       ])
+fi
+if test "$have_ao" = "yes"; then
+       AC_CHECK_LIB([pthread], [pthread_create], [], [
+               have_ao="no"
+               AC_MSG_WARN([pthread lib not found or not working, $msg])
+       ])
+fi
+if test "$have_ao" = "yes"; then
+       all_errlist_objs="$all_errlist_objs ao_write"
+       audiod_errlist_objs="$audiod_errlist_objs ao_write"
+       audiod_cmdline_objs="$audiod_cmdline_objs add_cmdline(ao_write)"
+       audiod_ldflags="$audiod_ldflags -lao -lpthread"
+
+       write_errlist_objs="$write_errlist_objs ao_write"
+       write_cmdline_objs="$write_cmdline_objs add_cmdline(ao_write)"
+       write_ldflags="$write_ldflags $ao_libs -lao -lpthread"
+       writers="$writers ao"
+       AC_SUBST(ao_cppflags)
+fi
+
+CPPFLAGS="$OLD_CPPFLAGS"
+LDFLAGS="$OLD_LDFLAGS"
+LIBS="$OLD_LIBS"
+
+
 
 AC_SUBST(install_sh, [$INSTALL])
 AC_CONFIG_FILES([Makefile])
diff --git a/error.h b/error.h
index 2e32c24044bd074cc9e311903adde6d85ac5a4c0..a24af894ef1ae49bd505ac82f093fde1e66dc6f1 100644 (file)
--- a/error.h
+++ b/error.h
@@ -104,6 +104,18 @@ extern const char **para_errlist[];
        PARA_ERROR(BAD_SAMPLERATE, "sample rate not supported"), \
 
 
+#define AO_WRITE_ERRORS \
+       PARA_ERROR(AO_DEFAULT_DRIVER, "ao: no usable output device"), \
+       PARA_ERROR(AO_BAD_DRIVER, "ao: invalid driver"), \
+       PARA_ERROR(AO_BAD_OPTION, "ao option is not of type key:value"), \
+       PARA_ERROR(AO_APPEND_OPTION, "ao append option: memory allocation failure"), \
+       PARA_ERROR(AO_OPEN_LIVE, "ao: could not open audio device"), \
+       PARA_ERROR(AO_FILE_NOT_SUPP, "ao: file io drivers not supported"), \
+       PARA_ERROR(AO_PLAY, "ao_play() failed"), \
+       PARA_ERROR(AO_BAD_SAMPLE_FORMAT, "ao: unsigned sample formats not supported"), \
+       PARA_ERROR(AO_PTHREAD, "pthread error"), \
+
+
 #define COMPRESS_FILTER_ERRORS \
        PARA_ERROR(COMPRESS_SYNTAX, "syntax error in compress filter config"), \
        PARA_ERROR(COMPRESS_EOF, "compress: end of file"), \
diff --git a/fd.c b/fd.c
index 6f9266f47a56cd8dee3e99b8651d12a89bb4069b..ea16bdda993aff814fa41684af53506e46242a4f 100644 (file)
--- a/fd.c
+++ b/fd.c
@@ -563,6 +563,9 @@ out:
 int para_munmap(void *start, size_t length)
 {
        int err;
+
+       if (!start)
+               return 0;
        if (munmap(start, length) >= 0)
                return 1;
        err = errno;
index a0824cf8cc7b82f109a4fd787d5cfdc4691b42f6..8b70bd4ff3cbedacd8a90dc62f8a19e9f5c28b19 100644 (file)
@@ -8,3 +8,4 @@ gui.ggo
 recv.ggo
 server.ggo
 write.ggo
+ao_write.ggo
diff --git a/ggo/ao_write.m4 b/ggo/ao_write.m4
new file mode 100644 (file)
index 0000000..baccc57
--- /dev/null
@@ -0,0 +1,26 @@
+include(header.m4)
+<qu>
+
+option "driver" d
+#~~~~~~~~~~~~~~~~
+"Select a output driver by name"
+string typestr = "name"
+optional
+details = "
+       If this is not given, the driver with the highest priority
+       (see below) will be used.
+"
+
+option "ao-option" o
+#~~~~~~~~~~~~~~~~~~~
+"Pass a key-value pair to the libao driver"
+string typestr = "key:value"
+optional
+multiple
+details = "
+       For each time this option is given, the supplied key-value
+       pair is appended to the list of options for the driver. Invalid
+       keys are silently ignored.
+"
+
+</qu>
index 30fe922f63bd6fab3ffd86ca4f2653dc0423bf1c..ea5ebeb7ac2cfb701fd0b4b676f525b7fbc5c74f 100644 (file)
@@ -251,6 +251,10 @@ Optional:
        Linux, you'll need to have ALSA's development package
        libasound2-dev installed.
 
+       - XREFERENCE(http://downloads.xiph.org/releases/ao/,
+       libao). Needed to build the ao writer (ESD, PulseAudio,...).
+       Debian package: libao-dev.
+
 Installation
 ~~~~~~~~~~~~
 
@@ -1533,7 +1537,7 @@ From these observations it is clear that there are three different
 FEC parameters: The slice size, the number of data slices k, and the
 total number of slices n. It is crucial to choose the slice size
 such that no fragmentation of network packets takes place because
-FEC only guards against losses and reodering but fails if slices are
+FEC only guards against losses and reordering but fails if slices are
 received partially.
 
 FEC decoding in paralash is performed through the fecdec filter which
@@ -1678,6 +1682,11 @@ write the PCM data to a file on the file system rather than playing
 it through a sound device. It is supported on all platforms and is
 always compiled in.
 
+*AO*. _Libao_ is a cross-platform audio library which supports a wide
+variety of platforms including PulseAudio (gnome), ESD (Enlightened
+Sound Daemon), AIX, Solaris and IRIX.  The ao writer plays audio
+through an output plugin of libao.
+
 Examples
 ~~~~~~~~
 
@@ -2032,7 +2041,7 @@ detection of duplicate or reordered packets. Being a connectionless
 protocol, only minimal internal state about the connection is
 maintained, which means that there is no protection against packet
 loss or network congestion. Error checking and correction (if at all)
-are performed in the application.'
+are performed in the application.
 
 *DCCP*. The _Datagram Congestion Control Protocol_ combines the
 connection-oriented state maintenance known from TCP with the