Fix stream grabbing.
authorAndre Noll <maan@systemlinux.org>
Sun, 10 Jan 2010 03:30:45 +0000 (04:30 +0100)
committerAndre Noll <maan@systemlinux.org>
Sun, 10 Jan 2010 03:30:45 +0000 (04:30 +0100)
The new code uses a buffer tree node which is inserted into an existing
buffer tree if possible, or put to the inactive list.

The grab command now takes the name of an existing parent node. The new
grab client node will be inserted as a child of this parent node.

audiod.c
audiod.cmd
audiod.h
buffer_tree.c
buffer_tree.h
grab_client.c
grab_client.h

index 3cc0921..79dd615 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -541,6 +541,29 @@ static int receiver_running(int format)
        return ret;
 }
 
+struct btr_node *audiod_get_btr_root(void)
+{
+       int i, newest_slot = -1;
+       struct timeval newest_rstime = {0, 0};
+
+       FOR_EACH_SLOT(i) {
+               struct slot_info *s = &slot[i];
+               struct timeval rstime;
+               if (!s->receiver_node)
+                       continue;
+               if (s->receiver_node->task.error < 0)
+                       continue;
+               btr_get_node_start(s->receiver_node->btrn, &rstime);
+               if (newest_slot >= 0 && tv_diff(&rstime, &newest_rstime, NULL) < 0)
+                       continue;
+               newest_rstime = rstime;
+               newest_slot = i;
+       }
+       if (newest_slot == -1)
+               return NULL;
+       return slot[newest_slot].receiver_node->btrn;
+}
+
 /* returns slot num on success. */
 static int open_current_receiver(struct sched *s)
 {
@@ -1052,6 +1075,7 @@ static void start_stop_decoders(struct sched *s)
        if (a->num_filters)
                open_filters(sl);
        open_writers(sl);
+       activate_grab_clients();
        btr_log_tree(sl->receiver_node->btrn, LL_NOTICE);
        s->timeout.tv_sec = 0;
        s->timeout.tv_usec = 1;
index 94f3ca0..a33b28f 100644 (file)
@@ -14,15 +14,13 @@ H: on -> standby -> off -> on
 N: grab
 D: grab the audio stream
 L:
-U: -- grab -[n=<num>] [-m[{s|p|a}]] [-i] [-o] [-f=<format>]
+U: -- grab [-m[{s|p|a}]] [-p=<parent>] [-o]
 H:
 H: grab ('splice') the audio stream at any position in the filter
 H: chain and send that data back to the client.
 H:
 H: Options:
 H:
-H: -n  Point of the filter chain to grab. Filters count from zero.
-H:
 H: -m  Change grab mode. Defaults to sloppy grab if not given.
 H:
 H:             -ms: sloppy grab
@@ -37,13 +35,9 @@ H:   ready for writing (i.e. would block). Sloppy mode ignores
 H:     the write, pedantic mode aborts and aggressive mode tries
 H:     to write anyway.
 H:
-H: -i  Grab the filter input instead of its output.
+H: -p  Grab output of this node of the buffer tree.
 H:
 H: -o  One-shot mode: Stop grabbing if audio file changes.
-H:
-H: -f  Only grab streams of this format (mp3, ogg, aac). The default is to
-H:     grab any stream.
-H:
 ---
 N: help
 D: display command list or help for given command
index 90ca22c..79c0f35 100644 (file)
--- a/audiod.h
+++ b/audiod.h
@@ -75,6 +75,7 @@ void __noreturn clean_exit(int status, const char *msg);
 int handle_connect(int accept_fd);
 void audiod_status_dump(void);
 char *get_time_string(int slot_num);
+struct btr_node *audiod_get_btr_root(void);
 
 void stat_client_write_item(int item_num);
 void clear_and_dump_items(void);
index a7a3220..8aa66b6 100644 (file)
@@ -745,6 +745,25 @@ void btr_log_tree(struct btr_node *btrn, int loglevel)
        return log_tree_recursively(btrn, loglevel, 0);
 }
 
+/*
+ * \return \a root if \a name is \p NULL.
+ */
+struct btr_node *btr_search_node(const char *name, struct btr_node *root)
+{
+       struct btr_node *ch;
+
+       if (!name)
+               return root;
+       if (!strcmp(root->name, name))
+               return root;
+       FOR_EACH_CHILD(ch, root) {
+               struct btr_node *result = btr_search_node(name, ch);
+               if (result)
+                       return result;
+       }
+       return NULL;
+}
+
 /** 640K ought to be enough for everybody ;) */
 #define BTRN_MAX_PENDING (640 * 1024)
 
index b9cfc1e..9f96ef9 100644 (file)
@@ -45,3 +45,4 @@ bool btr_inplace_ok(struct btr_node *btrn);
 int btr_node_status(struct btr_node *btrn, size_t min_iqs,
                enum btr_node_type type);
 void btr_get_node_start(struct btr_node *btrn, struct timeval *tv);
+struct btr_node *btr_search_node(const char *name, struct btr_node *root);
index 853dfba..bce9c96 100644 (file)
 #include "string.h"
 #include "fd.h"
 
-/** Grab clients that are not yet attached to a filter node. */
+/** Grab clients that are not yet attached any btr node. */
 static struct list_head inactive_grab_client_list;
 
-static int max_num_filters(void)
-{
-       int i, ret = 0;
-
-       for (i = 0; audio_formats[i]; i++) {
-               PARA_INFO_LOG("%s filter chain length: %d\n", audio_formats[i],
-                       num_filters(i));
-               ret = PARA_MAX(ret, num_filters(i));
-       }
-       PARA_INFO_LOG("maximal filter chain length: %d\n", ret);
-       return ret;
-}
+/** Grab clients that are attached to a btr node. */
+static struct list_head active_grab_client_list;
 
-static struct filter_node *find_filter_node(int format, int filternum)
+static int gc_write(struct grab_client *gc, char *buf, size_t len)
 {
-       int i;
+       int ret = write_ok(gc->fd);
 
-       FOR_EACH_SLOT(i) {
-               struct slot_info *s = &slot[i];
-               if (s->format < 0 || !s->fc)
-                       continue;
-               if (format >= 0 && s->format != format)
-                       continue;
-               if (num_filters(i) <= filternum)
-                       continue;
-               /* success */
-               return  s->fc->filter_nodes + filternum;
+       if (ret < 0)
+               goto err;
+       if (ret == 0) { /* fd not ready */
+               if (gc->mode == GM_PEDANTIC)
+                       goto err;
+               if (gc->mode == GM_SLOPPY)
+                       return len;
        }
-       return NULL;
-}
-
-static int gc_write(char *buf, size_t len, struct filter_callback *fcb)
-{
-       struct grab_client *gc = container_of(fcb, struct grab_client, fcb);
-       size_t written = 0;
-
-       while (written < len) {
-               int ret = write_ok(gc->fd);
-               if (ret < 0)
+       ret = write_nonblock(gc->fd, buf, len, 0);
+       if (ret < 0)
+               goto err;
+       if (ret > 0)
+               return ret;
+       if (ret == 0) {
+               if (gc->mode == GM_PEDANTIC)
                        goto err;
-               if (ret == 0) { /* fd not ready */
-                       if (gc->mode == GM_PEDANTIC)
-                               goto err;
-                       if (gc->mode == GM_SLOPPY)
-                               return 1;
-               }
-               ret = write(gc->fd, buf + written, len - written);
-               if (ret < 0) {
-                       if (errno != EAGAIN && errno != EINTR)
-                               goto err;
-                       if (gc->mode == GM_PEDANTIC)
-                               goto err;
-                       if (gc->mode == GM_SLOPPY)
-                               return 1;
-               } else
-                       written += ret;
+               if (gc->mode == GM_SLOPPY)
+                       return len;
        }
-       return 1;
+       return 0;
 err:
-       gc->error = -E_GC_WRITE;
        return -E_GC_WRITE;
 }
 
-static void add_inactive_gc(struct grab_client *gc)
-{
-       PARA_INFO_LOG("adding grab client %p (fd %d) to inactive list\n",
-               gc, gc->fd);
-       para_list_add(&gc->node, &inactive_grab_client_list);
-}
-
-static void gc_close(struct filter_callback *fcb)
+static void gc_pre_select(struct sched *s, struct task *t)
 {
-       struct grab_client *gc = container_of(fcb, struct grab_client, fcb);
+       struct grab_client *gc = container_of(t, struct grab_client, task);
+       int ret = btr_node_status(gc->btrn, 0, BTR_NT_LEAF);
 
-       if ((gc->flags & GF_ONE_SHOT) || gc->error < 0) {
-               PARA_INFO_LOG("closing fd %d (grab client %p)\n", gc->fd, gc);
-               close(gc->fd);
-               free(gc);
+       if (ret == 0)
+               return;
+       if (ret < 0) {
+               s->timeout.tv_sec = 0;
+               s->timeout.tv_usec = 0;
                return;
        }
-       add_inactive_gc(gc);
+       para_fd_set(gc->fd, &s->wfds, &s->max_fileno);
 }
 
+/*
+ * We need this forward declaration as post_select() needs
+ * activate_grab_client and vice versa.
+ */
+static void gc_post_select(struct sched *s, struct task *t);
+
 /**
- * Move a grab client from the inactive list to a filter node.
+ * Move a grab client to the active list and start it.
  *
  * \param gc The grab client to activate.
- * \param fn The filter node \a gc gets attached to.
  *
- * \sa filter_node::callbacks, inactive_grab_client_list.
  */
-void activate_grab_client(struct grab_client *gc, struct filter_node *fn)
+static void activate_grab_client(struct grab_client *gc)
 {
-       PARA_INFO_LOG("activating %p (fd %d, filter node: %p)\n", gc, gc->fd, fn);
-       list_del(&gc->node);
-       para_list_add(&gc->fcb.node, &fn->callbacks);
+       struct btr_node *root = audiod_get_btr_root(), *parent;
+
+       if (!root)
+               return;
+       parent = btr_search_node(gc->parent, root);
+       if (!parent)
+               return;
+       PARA_INFO_LOG("activating %p (fd %d)\n", gc, gc->fd);
+       list_move(&gc->node, &active_grab_client_list);
+       gc->btrn = btr_new_node("grab", parent, NULL, NULL);
+       if (!gc->task.pre_select) {
+               gc->task.pre_select = gc_pre_select;
+               gc->task.post_select = gc_post_select;
+               sprintf(gc->task.status, "grab");
+               register_task(&gc->task);
+       }
 }
 
 /**
  * Activate inactive grab clients if possible.
  *
- * \param audio_format_num The number of the audio format of the new audio file.
- * \param fc The filter chain containing the activated filters.
- *
  * This is called from audiod.c when the current audio file changes. It loops
  * over all inactive grab clients and checks each grab client's configuration
  * to determine if the client in question wishes to grab the new stream.  If
- * yes, this grab client is moved from the inactive grab client list to an
- * appropriate filter_node.
- *
- * \sa filter_chain_info::filters, inactive_grab_client_list,
- * activate_grab_client.
+ * yes, this grab client is moved from the inactive to the active grab client list.
  */
-void activate_inactive_grab_clients(int audio_format_num,
-               struct filter_chain *fc)
+void activate_grab_clients(void)
 {
        struct grab_client *gc, *tmp;
-       struct filter_node *fn;
 
        list_for_each_entry_safe(gc, tmp, &inactive_grab_client_list, node) {
-               if (gc->audio_format_num >= 0 && gc->audio_format_num !=
-                               audio_format_num)
-                       continue;
-               if (gc->filter_num >= num_filters(audio_format_num))
+               if (gc->task.error == -E_TASK_UNREGISTERED) {
+                       list_del(&gc->node);
+                       free(gc);
                        continue;
-               fn = fc->filter_nodes + gc->filter_num;
-               activate_grab_client(gc, fn);
+               }
+               activate_grab_client(gc);
        }
 }
 
+static void add_inactive_gc(struct grab_client *gc)
+{
+       PARA_INFO_LOG("adding grab client %p (fd %d) to inactive list\n",
+               gc, gc->fd);
+       para_list_add(&gc->node, &inactive_grab_client_list);
+}
+
+static int gc_close(struct grab_client *gc, int err)
+{
+       btr_remove_node(gc->btrn);
+       btr_free_node(gc->btrn);
+       gc->btrn = NULL;
+       PARA_INFO_LOG("closing gc: %s\n", para_strerror(-err));
+       list_move(&gc->node, &inactive_grab_client_list);
+       if (err == -E_GC_WRITE || (gc->flags & GF_ONE_SHOT)) {
+               close(gc->fd);
+               free(gc->parent);
+               return 1;
+       }
+       activate_grab_client(gc);
+       return 0;
+}
+
+static void gc_post_select(__a_unused struct sched *s, struct task *t)
+{
+       struct grab_client *gc = container_of(t, struct grab_client, task);
+       struct btr_node *btrn = gc->btrn;
+       int ret;
+       size_t sz;
+       char *buf;
+
+       t->error = 0;
+       ret = btr_node_status(btrn, 0, BTR_NT_LEAF);
+       if (ret == 0)
+               return;
+       if (ret < 0)
+               goto err;
+       sz = btr_next_buffer(btrn, &buf);
+       assert(sz != 0);
+       ret = gc_write(gc, buf, sz);
+       if (ret < 0)
+               goto err;
+       if (ret > 0)
+               btr_consume(btrn, ret);
+       return;
+err:
+       t->error = gc_close(gc, ret)? ret : 0;
+}
+
 static int check_gc_args(int argc, char **argv, struct grab_client *gc)
 {
-       int i, ret;
+       int i;
 
-       gc->audio_format_num = -1; /* default: grab any audio format */
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
                if (arg[0] != '-')
@@ -174,16 +192,6 @@ static int check_gc_args(int argc, char **argv, struct grab_client *gc)
                        i++;
                        break;
                }
-               if (!strncmp(arg, "-n=", 3)) {
-                       ret = para_atoi32(arg + 3, &gc->filter_num);
-                       if (ret < 0)
-                               return ret;
-                       if (gc->filter_num < 0)
-                               return -E_BAD_GC_FILTER_NUM;
-                       if (gc->filter_num >= max_num_filters())
-                               return -E_BAD_GC_FILTER_NUM;
-                       continue;
-               }
                if (!strncmp(arg, "-m", 2)) {
                        if (*(arg + 3))
                                return -E_GC_SYNTAX;
@@ -201,19 +209,12 @@ static int check_gc_args(int argc, char **argv, struct grab_client *gc)
                                return -E_GC_SYNTAX;
                        }
                }
-               if (!strcmp(arg, "-i")) {
-                       gc->flags |= GF_INPUT_GRAB;
-                       continue;
-               }
                if (!strcmp(arg, "-o")) {
                        gc->flags |= GF_ONE_SHOT;
                        continue;
                }
-               if (!strncmp(arg, "-f=", 3)) {
-                       ret = get_audio_format_num(arg + 3);
-                       if (ret < 0)
-                               return ret;
-                       gc->audio_format_num = ret;
+               if (!strncmp(arg, "-p=", 3)) {
+                       gc->parent = para_strdup(arg + 3);
                        continue;
                }
                return -E_GC_SYNTAX;
@@ -243,22 +244,13 @@ int grab_client_new(int fd, int argc, char **argv)
 {
        int ret;
        struct grab_client *gc = para_calloc(sizeof(struct grab_client));
-       struct filter_node *fn;
 
        ret = check_gc_args(argc, argv, gc);
        if (ret < 0)
                goto err_out;
-       if (gc->flags & GF_INPUT_GRAB)
-               gc->fcb.input_cb = gc_write;
-       else
-               gc->fcb.output_cb = gc_write;
        gc->fd = fd;
-       gc->fcb.close = gc_close;
-       fn = find_filter_node(gc->audio_format_num, gc->filter_num);
-       if (fn)
-               para_list_add(&gc->fcb.node, &fn->callbacks);
-       else
-               add_inactive_gc(gc);
+       add_inactive_gc(gc);
+       activate_grab_client(gc);
        return 1;
 err_out:
        free(gc);
@@ -275,4 +267,5 @@ void init_grabbing(void)
 {
        PARA_INFO_LOG("grab init\n");
        INIT_LIST_HEAD(&inactive_grab_client_list);
+       INIT_LIST_HEAD(&active_grab_client_list);
 }
index 04e0a75..59ba697 100644 (file)
@@ -22,38 +22,28 @@ enum grab_mode {
 
 /** Flags specified as arguments to the grab command. */
 enum grab_flags {
-       /** Grab the filter input instead of its output. */
-       GF_INPUT_GRAB = 1,
        /** Stop grabbing if audio file changes. */
-       GF_ONE_SHOT = 2,
+       GF_ONE_SHOT = 1,
 };
 
-/**
- * Describes one active grab client.
- *
- * \sa filter_callback, filter_node::callbacks.
- */
+/** Describes one active grab client. */
 struct grab_client {
+       /* The value of the -p option. */
+       char *parent;
        /** The file descriptor to send the grabbed stream to. */
        int fd;
-       /** Non-zero if the write() to \a fd failed. */
-       int error;
        /** See \ref grab_mode. */
        enum grab_mode mode;
-       /** Point of filter chain to grab. */
-       int32_t filter_num;
-       /** The number of the desired audio format. */
-       int audio_format_num;
        /** Flags given at the command line. */
        enum grab_flags flags;
-       /** The callback data which gets attached to a suitable filter_node. */
-       struct filter_callback fcb;
+       /** The point of the grab client's node in the buffer tree. */
+       struct btr_node *btrn;
+       /* The task of this grab client. */
+       struct task task;
        /** All grab clients belong either to a filter node or to the inactive list. */
        struct list_head node;
 };
 
 int grab_client_new(int fd, int argc, char **argv);
-void activate_inactive_grab_clients(int audio_format_num,
-               struct filter_chain *fc);
-void activate_grab_client(struct grab_client *gc, struct filter_node *fn);
+void activate_grab_clients(void);
 void init_grabbing(void);