X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=grab_client.c;h=ef3f34a76216e3b677e82e435ef5d5f947667891;hp=bce9c964b9730604cd315603673ca98f9c5e454d;hb=1cefe6a503c74d609db4e99e689d46575a5e40fd;hpb=1d7a26dca86a639db694663738ccc01acaa88aba diff --git a/grab_client.c b/grab_client.c index bce9c964..ef3f34a7 100644 --- a/grab_client.c +++ b/grab_client.c @@ -1,20 +1,13 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2012 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ -/** - * \file grab_client.c Functions for grabbing the stream at any position - * in a filter chain. - * - * \sa filter_chain filter_chain_info filter. - */ +/** \file grab_client.c Functions for grabbing the audio stream. */ #include #include -#include -#include #include "para.h" #include "list.h" @@ -28,11 +21,48 @@ #include "string.h" #include "fd.h" -/** Grab clients that are not yet attached any btr node. */ -static struct list_head inactive_grab_client_list; +/** + * How to handle blocking writes for the grab client fds. + */ +enum grab_mode { + /** Ignore the data and do not write. */ + GM_SLOPPY, + /** Write anyway (default). */ + GM_AGGRESSIVE, + /** Close fd if write would block. */ + GM_PEDANTIC, +}; + +/** Flags specified as arguments to the grab command. */ +enum grab_flags { + /** Stop grabbing if audio file changes. */ + GF_ONE_SHOT = 1, +}; + +/** Describes one active grab client. */ +struct grab_client { + /* The value of the -p option. */ + char *parent; + /* The value of the -n option. */ + char *name; + /** The file descriptor to send the grabbed stream to. */ + int fd; + /** See \ref grab_mode. */ + enum grab_mode mode; + /** Flags given at the command line. */ + enum grab_flags flags; + /** 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; + /** Belongs to either the active or the inactive list. */ + struct list_head node; +}; -/** Grab clients that are attached to a btr node. */ -static struct list_head active_grab_client_list; +/* Grab clients that are attached to a btr node. */ +static INITIALIZED_LIST_HEAD(active_grab_client_list); +/* Grab clients that are not currently attached any btr node. */ +static INITIALIZED_LIST_HEAD(inactive_grab_client_list); static int gc_write(struct grab_client *gc, char *buf, size_t len) { @@ -46,7 +76,7 @@ static int gc_write(struct grab_client *gc, char *buf, size_t len) if (gc->mode == GM_SLOPPY) return len; } - ret = write_nonblock(gc->fd, buf, len, 0); + ret = write_nonblock(gc->fd, buf, len); if (ret < 0) goto err; if (ret > 0) @@ -69,11 +99,8 @@ static void gc_pre_select(struct sched *s, struct task *t) if (ret == 0) return; - if (ret < 0) { - s->timeout.tv_sec = 0; - s->timeout.tv_usec = 0; - return; - } + if (ret < 0) + sched_min_delay(s); para_fd_set(gc->fd, &s->wfds, &s->max_fileno); } @@ -87,26 +114,27 @@ static void gc_post_select(struct sched *s, struct task *t); * Move a grab client to the active list and start it. * * \param gc The grab client to activate. - * */ -static void activate_grab_client(struct grab_client *gc) +static void gc_activate(struct grab_client *gc, struct sched *s) { struct btr_node *root = audiod_get_btr_root(), *parent; + char *name = gc->name? gc->name : "grab"; if (!root) return; parent = btr_search_node(gc->parent, root); if (!parent) return; - PARA_INFO_LOG("activating %p (fd %d)\n", gc, gc->fd); + PARA_INFO_LOG("activating fd %d\n", 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); - } + gc->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = name, .parent = parent)); + gc->task.pre_select = gc_pre_select; + gc->task.post_select = gc_post_select; + snprintf(gc->task.status, sizeof(gc->task.status) - 1, "%s", name); + gc->task.status[sizeof(gc->task.status) - 1] = '\0'; + gc->task.error = 0; + register_task(s, &gc->task); } /** @@ -116,28 +144,24 @@ static void activate_grab_client(struct grab_client *gc) * 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 to the active grab client list. + * + * This function also garbage collects all grab clients whose tasks have been + * unscheduled. */ -void activate_grab_clients(void) +void activate_grab_clients(struct sched *s) { struct grab_client *gc, *tmp; list_for_each_entry_safe(gc, tmp, &inactive_grab_client_list, node) { - if (gc->task.error == -E_TASK_UNREGISTERED) { + if (gc->fd < 0) { list_del(&gc->node); free(gc); continue; } - activate_grab_client(gc); + gc_activate(gc, s); } } -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); @@ -146,11 +170,17 @@ static int gc_close(struct grab_client *gc, int err) 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)) { + /* + * We must not free the gc structure here as it contains ->task + * which is still used because this function is called from + * post_select(). + */ close(gc->fd); + gc->fd = -1; free(gc->parent); + free(gc->name); return 1; } - activate_grab_client(gc); return 0; } @@ -177,10 +207,11 @@ static void gc_post_select(__a_unused struct sched *s, struct task *t) btr_consume(btrn, ret); return; err: - t->error = gc_close(gc, ret)? ret : 0; + gc_close(gc, ret); + t->error = ret; } -static int check_gc_args(int argc, char **argv, struct grab_client *gc) +static int gc_check_args(int argc, char **argv, struct grab_client *gc) { int i; @@ -217,6 +248,10 @@ static int check_gc_args(int argc, char **argv, struct grab_client *gc) gc->parent = para_strdup(arg + 3); continue; } + if (!strncmp(arg, "-n=", 3)) { + gc->name = para_strdup(arg + 3); + continue; + } return -E_GC_SYNTAX; } if (i != argc) @@ -230,42 +265,30 @@ static int check_gc_args(int argc, char **argv, struct grab_client *gc) * \param fd The file descriptor of the client. * \param argc Argument count. * \param argv Argument vector. + * \param s The scheduler to register the grab client task to. * * If the command line options given by \a argc and \a argv are valid. * allocate a struct grab_client and initialize it with this valid - * configuration. Moreover, add the new grab client to the inactive list. + * configuration. * - * \return Standard. + * If the new grab client can be added to an existing buffer tree, activate it. + * Otherwise, add it to the inactive list for later activation. * - * \sa grab_client, inactive_grab_client_list, activate_grab_client, - * filter_node::callbacks. + * \return Standard. */ -int grab_client_new(int fd, int argc, char **argv) +int grab_client_new(int fd, int argc, char **argv, struct sched *s) { int ret; struct grab_client *gc = para_calloc(sizeof(struct grab_client)); - ret = check_gc_args(argc, argv, gc); + ret = gc_check_args(argc, argv, gc); if (ret < 0) goto err_out; gc->fd = fd; - add_inactive_gc(gc); - activate_grab_client(gc); + para_list_add(&gc->node, &inactive_grab_client_list); + gc_activate(gc, s); return 1; err_out: free(gc); return ret; } - -/** - * Initialize the grabbing subsystem. - * - * This has to be called once during startup before any other function from - * grab_client.c may be used. It initializes \a inactive_grab_client_list. - */ -void init_grabbing(void) -{ - PARA_INFO_LOG("grab init\n"); - INIT_LIST_HEAD(&inactive_grab_client_list); - INIT_LIST_HEAD(&active_grab_client_list); -}