X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=write_common.c;h=d4d1b10b2ec597fa58ecf77dfad82ac01efa5d0c;hp=8c33b39e08ae783f2ca19ff75f4cda37fcb0a320;hb=9d849535e30c6b6a3ebb746fd3002d18927f62ea;hpb=c282c836791cedf57c128555af90af37c7c01c05 diff --git a/write_common.c b/write_common.c index 8c33b39e..d4d1b10b 100644 --- a/write_common.c +++ b/write_common.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006-2009 Andre Noll + * Copyright (C) 2006 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -7,330 +7,228 @@ /** \file write_common.c common functions of para_audiod and para_write */ #include -#include +#include +#include "write_cmd.lsg.h" #include "para.h" #include "string.h" #include "list.h" #include "sched.h" -#include "ggo.h" #include "buffer_tree.h" #include "write.h" #include "error.h" -/** the array containing the names of all supported writers */ -const char *writer_names[] ={WRITER_NAMES}; +/** Loop over all writers. */ +#define FOR_EACH_WRITER(i) for (i = 1; lls_cmd(i, write_cmd_suite); i++) -/** the array of supported writers */ -struct writer writers[NUM_SUPPORTED_WRITERS] = {WRITER_ARRAY}; -static void wng_pre_select(struct sched *s, struct task *t) +static inline bool writer_supported(int wid) { - struct writer_node_group *g = container_of(t, struct writer_node_group, task); - int i; - - FOR_EACH_WRITER_NODE(i, g) { - struct writer_node *wn = &g->writer_nodes[i]; - struct writer *w = writers + wn->writer_num; - if (!w->pre_select) - continue; - t->error = w->pre_select(s, wn); - if (t->error < 0) - return; - } - /* - * Force a minimal delay if something was written during the previous - * call to wng_post_select(). This is necessary because the filter - * chain might still have data for us which it couldn't convert during - * the previous run due to its buffer size constraints. In this case we - * do not want to wait until the next input data arrives as this could - * lead to buffer underruns. - */ - if (g->last_written == 0) - return; - s->timeout.tv_sec = 0; - s->timeout.tv_usec = 1; + return lls_user_data(WRITE_CMD(wid)); } -static void wng_post_select(struct sched *s, struct task *t) +/* simply return the first available writer */ +static int default_writer_id(void) { - struct writer_node_group *g = container_of(t, struct writer_node_group, task); int i; - size_t min_written = 0, max_written = 0; - FOR_EACH_WRITER_NODE(i, g) { - struct writer_node *wn = &g->writer_nodes[i]; - struct writer *w = writers + wn->writer_num; - t->error = w->post_select(s, wn); - if (t->error < 0) - return; - if (!i) - min_written = wn->written; - else - min_written = PARA_MIN(min_written, wn->written); - max_written = PARA_MAX(max_written, wn->written); - } - g->last_written = max_written; - //PARA_INFO_LOG("loaded: %zd, min_written: %zd bytes\n", *g->loaded, min_written); - if (min_written) { - *g->loaded -= min_written; - FOR_EACH_WRITER_NODE(i, g) - g->writer_nodes[i].written -= min_written; - } - if (!*g->loaded && *g->input_error) { - t->error = *g->input_error; - return; - } - if (*g->loaded && min_written) { -// PARA_INFO_LOG("moving %zd bytes\n", *g->loaded); - memmove(*g->bufp, *g->bufp + min_written, *g->loaded); - } + FOR_EACH_WRITER(i) + if (writer_supported(i)) + return i; + assert(0); /* the file writer should always be available */ } /** - * call the open function of each writer in the group + * Return the writer structure from a writer ID. * - * \param g the writer node group + * \param wid If non-positive, a pointer to the default writer is returned. * - * \return If at least one open function returned an error, all successful - * writer notes get closed and this error value is returned. Upon success, a - * task associated with \a g is registered to the scheduler and the function - * returns a positive value. - * */ -int wng_open(struct writer_node_group *g) + * \return Pointer to a (constant) struct writer. + */ +const struct writer *writer_get(int wid) { - int i, ret = 1; - - PARA_NOTICE_LOG("opening wng %p with %d writer(s)\n", g, g->num_writers); - FOR_EACH_WRITER_NODE(i, g) { - struct writer_node *wn = &g->writer_nodes[i]; - struct writer *w = writers + wn->writer_num; - wn->wng = g; - ret = w->open(wn); - if (ret < 0) - goto err_out; - } - sprintf(g->task.status, "%s", "writer node group"); - register_task(&g->task); - g->open = 1; - return 1; -err_out: - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); - while (i > 0) { - struct writer_node *wn = &g->writer_nodes[--i]; - struct writer *w = writers + wn->writer_num; - w->close(wn); - } - free(g->writer_nodes); - g->num_writers = 0; - g->task.error = -E_TASK_UNREGISTERED; - return ret; + if (wid < 0) + wid = default_writer_id(); + return lls_user_data(WRITE_CMD(wid)); } /** - * call the close function of each writer in the given group + * Return name of the writer identified by a writer ID. * - * \param g the writer node group to close + * \param wid If non-positive, the name of the default writer is returned. * - * This function also frees all resources of the given group. + * \return The returned buffer must not be freed by the caller. */ -void wng_close(struct writer_node_group *g) +const char *writer_name(int wid) { - int i; - - if (!g || !g->open) - return; - PARA_NOTICE_LOG("closing wng with %d writer(s)\n", g->num_writers); - FOR_EACH_WRITER_NODE(i, g) { - struct writer_node *wn = &g->writer_nodes[i]; - struct writer *w = writers + wn->writer_num; - w->close(wn); - } - free(g->writer_nodes); - free(g); + if (wid <= 0) + wid = default_writer_id(); + return lls_command_name(WRITE_CMD(wid)); } /** - * allocate and initialize a new writer_node_group struct + * Check if the given string is a valid command line for any writer. + * + * \param wa String of the form writer_name options. + * \param lprp Contains the parsed command line on success. * - * \param num_writers the number of writer nodes for the new group + * If wa is \p NULL, the (configuration-dependent) default writer is assumed. + * Otherwise, the function checks whether \a wa starts with the name of a + * supported writer. If a valid writer name was found, the rest of the command + * line is passed to the config parser of this writer. * - * \return Pointer to the new writer node group + * \return On success, the positive writer ID is returned. Otherwise the + * function prints an error message and calls exit(). */ -struct writer_node_group *wng_new(unsigned num_writers) +int check_writer_arg_or_die(const char *wa, struct lls_parse_result **lprp) { - struct writer_node_group *g = para_calloc(sizeof(struct writer_node_group)); - g->num_writers = num_writers; - g->writer_nodes = para_calloc(num_writers - * sizeof(struct writer_node)); - g->task.post_select = wng_post_select; - g->task.pre_select = wng_pre_select; - return g; + int ret, writer_num, argc; + char **argv = NULL, *errctx = NULL; + const struct lls_command *cmd; + + if (!wa || !*wa) { + writer_num = default_writer_id(); + cmd = WRITE_CMD(writer_num); + argv = para_malloc(2 * sizeof(char *)); + argc = 1; + argv[0] = para_strdup(lls_command_name(cmd)); + argv[1] = NULL; + goto parse; + } + ret = create_argv(wa, " \t\n", &argv); + if (ret < 0) + goto fail; + argc = ret; + ret = lls(lls_lookup_subcmd(argv[0], write_cmd_suite, &errctx)); + if (ret < 0) + goto free_argv; + writer_num = ret; + cmd = WRITE_CMD(writer_num); + if (!writer_supported(writer_num)) { + ret = -ERRNO_TO_PARA_ERROR(EINVAL); + errctx = make_message("%s writer is not supported", + lls_command_name(cmd)); + goto free_argv; + } +parse: + ret = lls(lls_parse(argc, argv, cmd, lprp, &errctx)); + if (ret >= 0) + ret = writer_num; +free_argv: + free_argv(argv); + if (ret >= 0) + return ret; +fail: + if (errctx) + PARA_ERROR_LOG("%s\n", errctx); + free(errctx); + PARA_EMERG_LOG("%s\n", para_strerror(-ret)); + exit(EXIT_FAILURE); } /** - * Call the init function of each supported paraslash writer. + * Open a writer node and register the corresponding task. + * + * \param wn The writer node to open. + * \param parent The parent btr node (the source for the writer node). + * \param s The scheduler instance to register the task to. */ -void writer_init(void) +void register_writer_node(struct writer_node *wn, struct btr_node *parent, + struct sched *s) { - int i; - - FOR_EACH_WRITER(i) - writers[i].init(&writers[i]); + const struct writer *w = writer_get(wn->wid); + + wn->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = writer_name(wn->wid), .parent = parent, + .handler = w->execute, .context = wn)); + wn->task = task_register(&(struct task_info) { + .name = writer_name(wn->wid), + .pre_select = w->pre_select, + .post_select = w->post_select, + .context = wn, + }, s); } + /** - * check if given string is a valid command line for any writer - * - * \param \wa string of the form writer_name:options - * \param writer_num contains the number of the writer upon success - * - * This function checks whether \a wa starts with the name of a supported - * paraslash writer, optionally followed by a colon and any options for that - * writer. If a valid writer name was found and further are present, the - * remaining part of \a wa is passed to that writer's config parser. + * Print the help text of all writers to stdout. * - * \return On success, a pointer to the gengetopt args info struct is returned - * and \a writer_num contains the number of the writer. Otherwise this function - * returns \p NULL. + * \param detailed Whether to print the short or the detailed help. */ -void *check_writer_arg(const char *wa, int *writer_num) +void print_writer_helps(bool detailed) { int i; - *writer_num = -E_WRITE_COMMON_SYNTAX; - PARA_INFO_LOG("checking %s\n", wa); + printf("\nAvailable writers: "); FOR_EACH_WRITER(i) { - const char *name = writer_names[i]; - size_t len = strlen(name); - char c; - if (strlen(wa) < len) + if (!writer_supported(i)) continue; - if (strncmp(name, wa, len)) + printf("%s%s", i? " " : "", writer_name(i)); + } + printf("\n"); + FOR_EACH_WRITER(i) { + const struct lls_command *cmd = WRITE_CMD(i); + char *help; + if (!writer_supported(i)) continue; - c = wa[len]; - if (c && c != ' ') + help = detailed? lls_long_help(cmd) : lls_short_help(cmd); + if (!help) continue; - if (c && !writers[i].parse_config) - return NULL; - *writer_num = i; - return writers[i].parse_config(c? wa + len + 1 : ""); + printf("%s\n", help); + free(help); } - PARA_ERROR_LOG("writer not found\n"); - return NULL; } -/** - * setup a writer node group with only one writer, the default writer - * - * The writer which is set up depends on the OS. It defaults to alsa for Linux, - * osx_write for OS X, file writer if neither of these is supported. - * - * \return pointer to the allocated writer node group - */ -struct writer_node_group *setup_default_wng(void) +static void get_btr_value(struct btr_node *btrn, const char *cmd, + int32_t *result) { - struct writer_node_group *wng = wng_new(1); - wng->writer_nodes[0].writer_num = DEFAULT_WRITER; - PARA_INFO_LOG("using default writer: %s %p\n", - writer_names[DEFAULT_WRITER], writers[DEFAULT_WRITER].parse_config); - wng->writer_nodes[0].conf = writers[DEFAULT_WRITER].parse_config(""); - return wng; -} - -void register_writer_node(struct writer_node *wn, struct btr_node *parent) -{ - struct writer *w = writers + wn->writer_num; - char *name = make_message("%s writer", writer_names[wn->writer_num]); - int ret; - - wn->btrn = btr_new_node(name, parent, w->execute, wn); - strcpy(wn->task.status, name); - free(name); - ret = w->open(wn); - wn->task.post_select = w->post_select_btr; - wn->task.pre_select = w->pre_select_btr; - register_task(&wn->task); + char *buf = NULL; + int ret = btr_exec_up(btrn, cmd, &buf); + + if (ret < 0) { + /* + * This really should not happen. It means one of our parent + * nodes died unexpectedly. Proceed with fingers crossed. + */ + PARA_CRIT_LOG("cmd %s: %s\n", cmd, para_strerror(-ret)); + *result = 0; + return; + } + ret = para_atoi32(buf, result); + assert(ret >= 0); + free(buf); } /** - * Setup a writer node with the default writer. - * - * If arg is \p NULL, the OS-dependent default writer is used with an empty - * configuration string. It defaults to alsa for Linux, osx for OS X, oss for - * *BSD and the file writer if neither of these is supported. - * - * Once the writer configuration has been retrieved, a writer node is created, - * its buffer tree node is added to the buffer tree as a child of the given - * parent. + * Ask parent btr nodes for the sample rate of the current stream. * - * Finally, the new writer node's taks structure is initialized and registered - * to the paraslash scheduler. + * \param btrn Where to start the search. + * \param result Filled in by this function. * - * \return A pointer to the allocated writer node group. + * This function is assumed to succeed and terminates on errors. */ -int setup_writer_node(const char *arg, struct btr_node *parent, - struct writer_node *wn) +void get_btr_sample_rate(struct btr_node *btrn, int32_t *result) { - if (arg) - wn->conf = check_writer_arg(arg, &wn->writer_num); - else { - wn->writer_num = DEFAULT_WRITER; - wn->conf = writers[DEFAULT_WRITER].parse_config(""); - } - if (!wn->conf) - return -E_WRITE_COMMON_SYNTAX; - register_writer_node(wn, parent); - return 1; + get_btr_value(btrn, "sample_rate", result); } - /** - * Print the help text of all writers to stdout. + * Ask parent btr nodes for the channel count of the current stream. * - * \param detailed Whether to print the detailed help text. + * \param btrn See \ref get_btr_sample_rate. + * \param result See \ref get_btr_sample_rate. */ -void print_writer_helps(int detailed) +void get_btr_channels(struct btr_node *btrn, int32_t *result) { - 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); - } -} - -static int get_btr_value(struct btr_node *btrn, const char *key, int32_t *result) -{ - char *buf = NULL; - int ret = btr_exec_up(btrn, key, &buf); - - if (ret < 0) - return ret; - ret = para_atoi32(buf, result); - free(buf); - return ret; + get_btr_value(btrn, "channels", result); } -/* - * Ask parent btr nodes for the samplerate of the current stream. - */ -int get_btr_samplerate(struct btr_node *btrn, int32_t *result) -{ - return get_btr_value(btrn, "samplerate", result); -} - -/* - * Ask parent btr nodes for the channel count of the current stream. +/** + * Ask parent btr nodes for the number of bits per sample and the byte sex. + * + * \param btrn See \ref get_btr_sample_rate. + * \param result Contains the sample format as an enum sample_format type. */ -int get_btr_channels(struct btr_node *btrn, int32_t *result) +void get_btr_sample_format(struct btr_node *btrn, int32_t *result) { - return get_btr_value(btrn, "channels", result); + get_btr_value(btrn, "sample_format", result); }