X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=grab_client.c;h=9109c2f8f6c5f1978eddd356ff7f8e979d73dd8a;hp=d1ef43f95bba34cc43881ec779976f5c3d797e50;hb=bda95f9508b456dcea89d300f6d4104e30ab9f3e;hpb=d5d96c6c65b47a34136bc04420bffd38e947bfea diff --git a/grab_client.c b/grab_client.c index d1ef43f9..9109c2f8 100644 --- a/grab_client.c +++ b/grab_client.c @@ -1,24 +1,20 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006-2011 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" #include "sched.h" #include "ggo.h" +#include "buffer_tree.h" #include "filter.h" #include "grab_client.h" #include "audiod.h" @@ -26,144 +22,200 @@ #include "string.h" #include "fd.h" -/** Grab clients that are not yet attached to a filter node. */ -static struct list_head inactive_grab_client_list; - -static int max_num_filters(void) -{ - int i, ret = 0; +/** + * 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, +}; - 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; -} +/** Flags specified as arguments to the grab command. */ +enum grab_flags { + /** Stop grabbing if audio file changes. */ + GF_ONE_SHOT = 1, +}; -static struct filter_node *find_filter_node(int format, int filternum) -{ - int i; +/** 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; +}; - 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; - } - return NULL; -} +/* 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(char *buf, size_t len, struct filter_callback *fcb) +static int gc_write(struct grab_client *gc, char *buf, size_t len) { - struct grab_client *gc = container_of(fcb, struct grab_client, fcb); - size_t written = 0; + int ret = write_ok(gc->fd); - while (written < len) { - int ret = write_ok(gc->fd); - if (ret < 0) + if (ret < 0) + goto err; + if (ret == 0) { /* fd not ready */ + 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; + ret = write_nonblock(gc->fd, buf, len); + if (ret < 0) + goto err; + if (ret > 0) + return ret; + if (ret == 0) { + if (gc->mode == GM_PEDANTIC) + goto err; + if (gc->mode == GM_SLOPPY) + return len; + } + 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; - } - add_inactive_gc(gc); + if (ret < 0) + sched_min_delay(s); + 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 gc_activate(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; + char *name = gc->name? gc->name : "grab"; + + if (!root) + return; + parent = btr_search_node(gc->parent, root); + if (!parent) + return; + PARA_INFO_LOG("activating fd %d\n", gc->fd); + list_move(&gc->node, &active_grab_client_list); + 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(&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. + * yes, this grab client is moved from the inactive to the active grab client list. * - * \sa filter_chain_info::filters, inactive_grab_client_list, - * activate_grab_client. + * This function also garbage collects all grab clients whose tasks have been + * unscheduled. */ -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->fd < 0) { + list_del(&gc->node); + free(gc); continue; - fn = fc->filter_nodes + gc->filter_num; - activate_grab_client(gc, fn); + } + gc_activate(gc); + } +} + +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)) { + /* + * 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; } + 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: + 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, 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] != '-') @@ -172,16 +224,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; @@ -199,19 +241,16 @@ 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; + } + if (!strncmp(arg, "-n=", 3)) { + gc->name = para_strdup(arg + 3); continue; } return -E_GC_SYNTAX; @@ -230,47 +269,26 @@ static int check_gc_args(int argc, char **argv, struct grab_client *gc) * * 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 ret; struct grab_client *gc = para_calloc(sizeof(struct grab_client)); - struct filter_node *fn; - ret = check_gc_args(argc, argv, gc); + ret = gc_check_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); + para_list_add(&gc->node, &inactive_grab_client_list); + gc_activate(gc); 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); -}