From: Andre Noll Date: Sun, 5 Dec 2010 17:41:36 +0000 (+0100) Subject: Merge branch 't/writer_cleanups' X-Git-Tag: v0.4.5~4 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=877c179023ba45d481e9d89ac82fb0856617587b;hp=f4b28c16b93699723d42c46facec2e7b4d2d71df Merge branch 't/writer_cleanups' --- diff --git a/alsa_write.c b/alsa_write.c index bcabe53c..8676a451 100644 --- a/alsa_write.c +++ b/alsa_write.c @@ -35,7 +35,7 @@ struct private_alsa_write_data { /** The alsa handle */ snd_pcm_t *handle; - /** Determined and set by alsa_open(). */ + /** Determined and set by alsa_init(). */ int bytes_per_frame; /** The approximate maximum buffer duration in us. */ unsigned buffer_time; @@ -142,28 +142,18 @@ static int alsa_init(struct private_alsa_write_data *pad, return 1; } -/* Open an instance of the alsa writer. */ -static int alsa_open(struct writer_node *wn) -{ - wn->private_data = para_calloc(sizeof(struct private_alsa_write_data)); - return 1; -} - static void alsa_write_pre_select(struct sched *s, struct task *t) { struct writer_node *wn = container_of(t, struct writer_node, task); struct private_alsa_write_data *pad = wn->private_data; struct timeval tv; snd_pcm_sframes_t avail, underrun; - int ret; + int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); - if (!pad->handle) - return; - ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); - if (ret < 0) - sched_request_timeout_ms(20, s); - if (ret <= 0) + if (ret == 0) return; + if (ret < 0 || !pad) + return sched_min_delay(s); /* * Data is available to be written to the alsa handle. Compute number * of milliseconds until next buffer underrun would occur. @@ -191,16 +181,16 @@ static void alsa_close(struct writer_node *wn) struct private_alsa_write_data *pad = wn->private_data; PARA_INFO_LOG("closing writer node %p\n", wn); - if (pad->handle) { - /* - * It's OK to have a blocking operation here because we already - * made sure that the PCM output buffer is (nearly) empty. - */ - snd_pcm_nonblock(pad->handle, 0); - snd_pcm_drain(pad->handle); - snd_pcm_close(pad->handle); - snd_config_update_free_global(); - } + if (!pad) + return; + /* + * It's OK to have a blocking operation here because we already made + * sure that the PCM output buffer is (nearly) empty. + */ + snd_pcm_nonblock(pad->handle, 0); + snd_pcm_drain(pad->handle); + snd_pcm_close(pad->handle); + snd_config_update_free_global(); free(pad); } @@ -222,10 +212,10 @@ again: return; btr_merge(btrn, wn->min_iqs); bytes = btr_next_buffer(btrn, &data); - if (ret < 0 || bytes < pad->bytes_per_frame) { /* eof */ + if (ret < 0 || bytes < wn->min_iqs) { /* eof */ assert(btr_no_parent(btrn)); ret = -E_ALSA_EOF; - if (!pad->handle) + if (!pad) goto err; /* wait until pending frames are played */ if (pad->drain_barrier.tv_sec == 0) { @@ -238,9 +228,11 @@ again: goto err; return; } - if (!pad->handle) { + if (!pad) { int32_t val; + pad = para_calloc(sizeof(*pad)); + wn->private_data = pad; if (bytes == 0) /* no data available */ return; get_btr_sample_rate(btrn, &val); @@ -279,21 +271,14 @@ err: t->error = ret; } -__malloc static void *alsa_parse_config(const char *options) +__malloc static void *alsa_parse_config_or_die(const char *options) { - int ret; - struct alsa_write_args_info *conf - = para_calloc(sizeof(struct alsa_write_args_info)); + struct alsa_write_args_info *conf = para_calloc(sizeof(*conf)); PARA_INFO_LOG("options: %s, %zd\n", options, strcspn(options, " \t")); - ret = alsa_cmdline_parser_string(options, conf, "alsa_write"); - if (ret) - goto err_out; - PARA_INFO_LOG("help given: %d\n", conf->help_given); + /* exits on errors */ + alsa_cmdline_parser_string(options, conf, "alsa_write"); return conf; -err_out: - free(conf); - return NULL; } static void alsa_free_config(void *conf) @@ -313,11 +298,10 @@ void alsa_write_init(struct writer *w) struct alsa_write_args_info dummy; alsa_cmdline_parser_init(&dummy); - w->open = alsa_open; w->close = alsa_close; w->pre_select = alsa_write_pre_select; w->post_select = alsa_write_post_select; - w->parse_config = alsa_parse_config; + w->parse_config_or_die = alsa_parse_config_or_die; w->shutdown = NULL; /* nothing to do */ w->free_config = alsa_free_config; w->help = (struct ggo_help) { diff --git a/file_write.c b/file_write.c index 0cee535a..7762d794 100644 --- a/file_write.c +++ b/file_write.c @@ -50,19 +50,17 @@ __must_check __malloc static char *random_filename(void) return result; } -static int file_write_open(struct writer_node *wn) +static int prepare_output_file(struct writer_node *wn) { - struct private_file_write_data *pfwd = para_calloc( - sizeof(struct private_file_write_data)); struct file_write_args_info *conf = wn->conf; char *filename; int ret; + struct private_file_write_data *pfwd = para_calloc(sizeof(*pfwd)); if (conf->filename_given) filename = conf->filename_arg; else filename = random_filename(); - wn->private_data = pfwd; ret = para_open(filename, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); if (!conf->filename_given) free(filename); @@ -70,8 +68,11 @@ static int file_write_open(struct writer_node *wn) goto out; pfwd->fd = ret; ret = mark_fd_blocking(pfwd->fd); - if (ret >= 0) - return 1; + if (ret < 0) + goto out_close; + wn->private_data = pfwd; + return 1; +out_close: close(pfwd->fd); out: free(pfwd); @@ -82,20 +83,21 @@ static void file_write_pre_select(struct sched *s, struct task *t) { struct writer_node *wn = container_of(t, struct writer_node, task); struct private_file_write_data *pfwd = wn->private_data; - int ret; + int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); - t->error = 0; - ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); - if (ret > 0) - para_fd_set(pfwd->fd, &s->wfds, &s->max_fileno); - else if (ret < 0) - sched_min_delay(s); + if (ret == 0) + return; + if (ret < 0 || !pfwd) + return sched_min_delay(s); + para_fd_set(pfwd->fd, &s->wfds, &s->max_fileno); } static void file_write_close(struct writer_node *wn) { struct private_file_write_data *pfwd = wn->private_data; + if (!pfwd) + return; close(pfwd->fd); free(pfwd); } @@ -114,6 +116,10 @@ static void file_write_post_select(__a_unused struct sched *s, ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF); if (ret <= 0) goto out; + if (!pfwd) { + ret = prepare_output_file(wn); + goto out; + } if (!FD_ISSET(pfwd->fd, &s->wfds)) return; bytes = btr_next_buffer(btrn, &buf); @@ -129,17 +135,13 @@ out: t->error = ret; } -__malloc static void *file_write_parse_config(const char *options) +__malloc static void *file_write_parse_config_or_die(const char *options) { - struct file_write_args_info *conf - = para_calloc(sizeof(struct file_write_args_info)); - int ret = file_cmdline_parser_string(options, conf, "file_write"); - - PARA_INFO_LOG("conf->filename_given: %d\n", conf->filename_given); - if (!ret) - return conf; - free(conf); - return NULL; + struct file_write_args_info *conf = para_calloc(sizeof(*conf)); + + /* exits on errors */ + file_cmdline_parser_string(options, conf, "file_write"); + return conf; } static void file_write_free_config(void *conf) @@ -153,10 +155,9 @@ void file_write_init(struct writer *w) struct file_write_args_info dummy; file_cmdline_parser_init(&dummy); - w->open = file_write_open; w->pre_select = file_write_pre_select; w->post_select = file_write_post_select; - w->parse_config = file_write_parse_config; + w->parse_config_or_die = file_write_parse_config_or_die; w->free_config = file_write_free_config; w->close = file_write_close; w->shutdown = NULL; /* nothing to do */ diff --git a/oss_write.c b/oss_write.c index b82b3968..090f9905 100644 --- a/oss_write.c +++ b/oss_write.c @@ -52,19 +52,20 @@ static void oss_pre_select(struct sched *s, struct task *t) struct private_oss_write_data *powd = wn->private_data; int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); - t->error = 0; - if (ret < 0) - sched_min_delay(s); - else if (ret > 0) - para_fd_set(powd->fd, &s->wfds, &s->max_fileno); + if (ret == 0) + return; + if (ret < 0 || !powd) + return sched_min_delay(s); + para_fd_set(powd->fd, &s->wfds, &s->max_fileno); } static void oss_close(struct writer_node *wn) { struct private_oss_write_data *powd = wn->private_data; - if (powd->fd >= 0) - close(powd->fd); + if (!powd) + return; + close(powd->fd); free(powd); } @@ -85,12 +86,13 @@ static int oss_init(struct writer_node *wn, unsigned sample_rate, int ret, format; unsigned ch, rate; struct oss_write_args_info *conf = wn->conf; - struct private_oss_write_data *powd = wn->private_data; + struct private_oss_write_data *powd = para_calloc(sizeof(*powd)); + wn->private_data = powd; PARA_INFO_LOG("opening %s\n", conf->device_arg); ret = para_open(conf->device_arg, O_WRONLY, 0); if (ret < 0) - return ret; + goto err_free; powd->fd = ret; ret = mark_fd_nonblocking(powd->fd); if (ret < 0) @@ -150,7 +152,8 @@ static int oss_init(struct writer_node *wn, unsigned sample_rate, return 1; err: close(powd->fd); - powd->fd = -1; +err_free: + free(powd); return ret; } @@ -168,7 +171,7 @@ static void oss_post_select(__a_unused struct sched *s, goto out; if (ret == 0) return; - if (powd->fd < 0) { + if (!powd) { int32_t rate, ch, format; get_btr_sample_rate(btrn, &rate); get_btr_channels(btrn, &ch); @@ -199,28 +202,13 @@ out: btr_remove_node(btrn); } -static int oss_open(struct writer_node *wn) -{ - struct private_oss_write_data *powd; - - powd = para_calloc(sizeof(*powd)); - wn->private_data = powd; - powd->fd = -1; - return 1; -} - -__malloc static void *oss_parse_config(const char *options) +__malloc static void *oss_parse_config_or_die(const char *options) { - int ret; struct oss_write_args_info *conf = para_calloc(sizeof(*conf)); - ret = oss_cmdline_parser_string(options, conf, "oss_write"); - if (ret) - goto err_out; + /* exits on errors */ + oss_cmdline_parser_string(options, conf, "oss_write"); return conf; -err_out: - free(conf); - return NULL; } static void oss_free_config(void *conf) @@ -240,11 +228,10 @@ void oss_write_init(struct writer *w) struct oss_write_args_info dummy; oss_cmdline_parser_init(&dummy); - w->open = oss_open; w->close = oss_close; w->pre_select = oss_pre_select; w->post_select = oss_post_select; - w->parse_config = oss_parse_config; + w->parse_config_or_die = oss_parse_config_or_die; w->free_config = oss_free_config; w->shutdown = NULL; w->help = (struct ggo_help) { diff --git a/osx_write.c b/osx_write.c index cfd02e74..ebfa4b3c 100644 --- a/osx_write.c +++ b/osx_write.c @@ -187,18 +187,9 @@ static OSStatus osx_callback(void * inClientData, #define ENDIAN_FLAGS 0 #endif -static int osx_write_open(struct writer_node *wn) -{ - struct private_osx_write_data *powd = para_calloc(sizeof(*powd)); - - wn->private_data = powd; - init_buffers(wn); - return 0; -} - static int core_audio_init(struct writer_node *wn) { - struct private_osx_write_data *powd = wn->private_data; + struct private_osx_write_data *powd = para_calloc(sizeof(*powd)); ComponentDescription desc; Component comp; AURenderCallbackStruct inputCallback = {osx_callback, powd}; @@ -207,6 +198,8 @@ static int core_audio_init(struct writer_node *wn) struct btr_node *btrn = wn->btrn; int32_t val; + wn->private_data = powd; + init_buffers(wn); /* where did that default audio output go? */ desc.componentType = kAudioUnitType_Output; desc.componentSubType = kAudioUnitSubType_DefaultOutput; @@ -276,22 +269,18 @@ e2: e1: CloseComponent(powd->audio_unit); e0: + free(powd); + wn->private_data = NULL; return ret; } -__malloc static void *osx_write_parse_config(const char *options) +__malloc static void *osx_write_parse_config_or_die(const char *options) { - struct osx_write_args_info *conf - = para_calloc(sizeof(struct osx_write_args_info)); - PARA_INFO_LOG("options: %s\n", options); - int ret = osx_cmdline_parser_string(options, conf, "osx_write"); - if (ret) - goto err_out; - return conf; -err_out: - free(conf); - return NULL; + struct osx_write_args_info *conf = para_calloc(sizeof(*conf)); + /* exits on errors */ + osx_cmdline_parser_string(options, conf, "osx_write"); + return conf; } static void osx_free_config(void *conf) @@ -303,6 +292,8 @@ static void osx_write_close(struct writer_node *wn) { struct private_osx_write_data *powd = wn->private_data; + if (!powd) + return; PARA_INFO_LOG("closing writer node %p\n", wn); AudioOutputUnitStop(powd->audio_unit); AudioUnitUninitialize(powd->audio_unit); @@ -320,14 +311,15 @@ static void osx_write_post_select(__a_unused struct sched *s, struct task *t) size_t bytes; int ret = 0; - while (powd->to->remaining <= 0) { + while (!powd || powd->to->remaining <= 0) { ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); if (ret <= 0) break; - if (powd->sample_rate == 0) { + if (!powd) { ret = core_audio_init(wn); if (ret < 0) break; + powd = wn->private_data; } btr_merge(btrn, 8192); bytes = btr_next_buffer(btrn, &data); @@ -342,7 +334,7 @@ static void osx_write_post_select(__a_unused struct sched *s, struct task *t) } powd->to = powd->to->next; } - if (ret < 0 && powd->from->remaining <= 0) { + if (ret < 0 && (!powd || powd->from->remaining <= 0)) { btr_remove_node(btrn); t->error = ret; } @@ -353,17 +345,19 @@ static void osx_write_pre_select(struct sched *s, struct task *t) struct writer_node *wn = container_of(t, struct writer_node, task); struct private_osx_write_data *powd = wn->private_data; struct timeval tmp = {.tv_sec = 1, .tv_usec = 0}, delay = tmp; - unsigned long divisor; - size_t numbytes = powd->to->remaining * sizeof(short); + unsigned long factor; + size_t numbytes; int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); - if (ret < 0) - sched_min_delay(s); - if (ret <= 0 || numbytes < wn->min_iqs) + if (ret == 0) return; - divisor = powd->sample_rate * wn->min_iqs / numbytes; - if (divisor) - tv_divide(divisor, &tmp, &delay); + if (ret < 0 || !powd) + return sched_min_delay(s); + assert(powd->sample_rate > 0); + assert(wn->min_iqs > 0); + numbytes = powd->to->remaining * sizeof(short); + factor = numbytes / powd->sample_rate / wn->min_iqs; + tv_scale(factor, &tmp, &delay); sched_request_timeout(&delay, s); } @@ -373,11 +367,10 @@ void osx_write_init(struct writer *w) struct osx_write_args_info dummy; osx_cmdline_parser_init(&dummy); - w->open = osx_write_open; w->close = osx_write_close; w->pre_select = osx_write_pre_select; w->post_select = osx_write_post_select; - w->parse_config = osx_write_parse_config; + w->parse_config_or_die = osx_write_parse_config_or_die; w->free_config = osx_free_config; w->shutdown = NULL; /* nothing to do */ w->help = (struct ggo_help) { diff --git a/write.h b/write.h index 1361fcf1..d5834b4c 100644 --- a/write.h +++ b/write.h @@ -36,26 +36,20 @@ struct writer { /** * The command line parser of the writer. * - * It should check whether the command line options given by \a options are - * valid. On success, it should return a pointer to the writer-specific - * configuration data determined by \a options. Note that this might be called - * more than once with different values of \a options. \sa \ref free_config(). + * It should check whether the command line options given by \a options + * are valid and return a pointer to the writer-specific configuration + * data determined by \a options. This function must either succeed or + * call exit(). Note that parse_config_or_die() might be called more + * than once with different values of \a options. \sa \ref + * free_config(). */ - void *(*parse_config)(const char *options); + void *(*parse_config_or_die)(const char *options); /** * Dellocate all configuration resources. * - * This should free whatever was allocated by \ref parse_config(). + * This should free whatever was allocated by \ref parse_config_or_die(). */ void (*free_config)(void *config); - /** - * Open one instance of this writer. - * - * This function should perform any work necessary to write the incoming - * stream. To this aim, it may allocate its private data structure and store - * a pointer to that structure via the given writer_node parameter. - */ - int (*open)(struct writer_node *); /** * Prepare the fd sets for select. * diff --git a/write_common.c b/write_common.c index 7131a940..04db8ff0 100644 --- a/write_common.c +++ b/write_common.c @@ -66,27 +66,32 @@ void *check_writer_arg(const char *wa, int *writer_num) c = wa[len]; if (c && c != ' ') continue; - if (c && !writers[i].parse_config) - return NULL; *writer_num = i; - return writers[i].parse_config(c? wa + len + 1 : ""); + return writers[i].parse_config_or_die(c? wa + len + 1 : ""); } PARA_ERROR_LOG("writer not found\n"); return NULL; } +/** + * 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). + * + * The configuration of the writer node stored in \p wn->conf must be + * initialized before this function may be called. + */ 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(&(struct btr_node_description) EMBRACE(.name = name, .parent = parent, .handler = w->execute, .context = wn)); strcpy(wn->task.status, name); free(name); - ret = w->open(wn); wn->task.post_select = w->post_select; wn->task.pre_select = w->pre_select; register_task(&wn->task); @@ -115,7 +120,7 @@ int setup_writer_node(const char *arg, struct btr_node *parent, wn->conf = check_writer_arg(arg, &wn->writer_num); else { wn->writer_num = DEFAULT_WRITER; - wn->conf = writers[DEFAULT_WRITER].parse_config(""); + wn->conf = writers[DEFAULT_WRITER].parse_config_or_die(""); } if (!wn->conf) return -E_WRITE_COMMON_SYNTAX; @@ -123,7 +128,6 @@ int setup_writer_node(const char *arg, struct btr_node *parent, return 1; } - /** * Print the help text of all writers to stdout. *