From: Andre Noll Date: Thu, 19 Jun 2014 13:39:33 +0000 (+0200) Subject: Merge branch 't/audiod_com_version' X-Git-Tag: v0.5.3~18 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=39840199dee5c2be051222637ee36feaaff6d2e7;hp=cddb6a7b9491b32b0c1108e934a6ee7f032302bc Merge branch 't/audiod_com_version' Cooking since 2014-04-03. * t/audiod_com_version: audiod: Implement version command. audiod: Trival spelling fix for com_tasks(). --- diff --git a/Makefile.real b/Makefile.real index d6ffe0c4..2973637e 100644 --- a/Makefile.real +++ b/Makefile.real @@ -86,7 +86,6 @@ DEBUG_CPPFLAGS += -Wredundant-decls DEBUG_CPPFLAGS += -Wall -Wno-sign-compare -Wno-unknown-pragmas DEBUG_CPPFLAGS += -Wformat-security DEBUG_CPPFLAGS += -Wmissing-format-attribute -DEBUG_CPPFLAGS += -Wdeclaration-after-statement ifeq ($(uname_s),Linux) CPPFLAGS += -fdata-sections -ffunction-sections diff --git a/NEWS b/NEWS index ab53a176..1cc2f40e 100644 --- a/NEWS +++ b/NEWS @@ -1,9 +1,20 @@ NEWS ==== ---------------------------------------------- -0.5.2 (to be announced) "orthogonal interior" ---------------------------------------------- +------------------------------------------------- +0.5.3 (to be released) "symbolic synchronization" +------------------------------------------------- + + - Various alsa-related fixes, mostly for the raspberry pi. + - The test suite has been extended to include sanity checks + for the generated man pages. + - ao_writer fixes. This writer was in a quite bad shape. Many + serious bugs have been fixed. + - new audiod command: version. + +---------------------------------------- +0.5.2 (2014-04-11) "orthogonal interior" +---------------------------------------- The new sync filter, the AES_CTR128 stream cipher and the overhauled network code are the highlights of this release. It also includes a @@ -23,9 +34,13 @@ fair number of smaller fixes and improvements not mentioned here. - The man pages of para_audiod, para_filter, para_recv, and para_write contain the relevant options for receivers, filters, writers. This broke in 0.5.0. + - ogg/vorbis latency improvements. - Improved user manual. - Minor fixes to avoid clang warnings. +Downloads: ./releases/paraslash-0.5.2.tar.bz2 (tarball), +./releases/paraslash-0.5.2.tar.bz2.asc (signature) + ------------------------------------------ 0.5.1 (2013-12-20) "temporary implication" ------------------------------------------ diff --git a/alsa_mix.c b/alsa_mix.c index be38e887..c860efc8 100644 --- a/alsa_mix.c +++ b/alsa_mix.c @@ -150,7 +150,7 @@ static int alsa_mix_set_channel(struct mixer_handle *h, mixer_channel, h->card, snd_strerror(ret)); return -E_ALSA_MIX_RANGE; } - if (h->pmin < 0 || h->pmax < 0 || h->pmin >= h->pmax) { + if (h->pmin >= h->pmax) { PARA_NOTICE_LOG("alsa reported %s range %ld-%ld (%s)\n", mixer_channel, h->pmin, h->pmax, h->card); return -E_ALSA_MIX_RANGE; diff --git a/alsa_write.c b/alsa_write.c index ad59a829..3759306e 100644 --- a/alsa_write.c +++ b/alsa_write.c @@ -34,17 +34,24 @@ struct private_alsa_write_data { snd_pcm_t *handle; /** Determined and set by alsa_init(). */ int bytes_per_frame; - /** - * The sample rate given by command line option or the decoder - * of the writer node group. + /* + * If the sample rate is not given at the command line and no wav + * header was detected, the btr exec mechanism is employed to query the + * ancestor buffer tree nodes for this information. In a typical setup + * the decoder passes the sample rate back to the alsa writer. + * + * \sa \ref btr_exec_up(). */ unsigned sample_rate; - snd_pcm_format_t sample_format; - /** - * The number of channels, given by command line option or the - * decoder of the writer node group. + /* + * The sample format (8/16 bit, signed/unsigned, little/big endian) is + * determined in the same way as the \a sample_rate. */ + snd_pcm_format_t sample_format; + /* The number of channels, again determined like \a sample_rate. */ unsigned channels; + /* time until buffer underrun occurs, in milliseconds */ + unsigned buffer_time; struct timeval drain_barrier; /* File descriptor for select(). */ int poll_fd; @@ -72,9 +79,9 @@ static int alsa_init(struct private_alsa_write_data *pad, snd_pcm_uframes_t start_threshold, stop_threshold; snd_pcm_uframes_t buffer_size, period_size; snd_output_t *output_log; - unsigned buffer_time; int ret; const char *msg; + unsigned period_time; PARA_INFO_LOG("opening %s\n", conf->device_arg); msg = "unable to open pcm"; @@ -107,18 +114,21 @@ static int alsa_init(struct private_alsa_write_data *pad, &pad->sample_rate, NULL); if (ret < 0) goto fail; - msg = "unable to get buffer time"; - ret = snd_pcm_hw_params_get_buffer_time_max(hwparams, &buffer_time, - NULL); - if (ret < 0 || buffer_time == 0) - goto fail; - /* buffer at most 500 milliseconds */ - buffer_time = PARA_MIN(buffer_time, 500U * 1000U); + /* alsa wants microseconds */ + pad->buffer_time = conf->buffer_time_arg * 1000; msg = "could not set buffer time"; ret = snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams, - &buffer_time, NULL); + &pad->buffer_time, NULL); + if (ret < 0) + goto fail; + pad->buffer_time /= 1000; /* we prefer milliseconds */ + period_time = pad->buffer_time * 250; /* buffer time / 4 */ + msg = "could not set period time"; + ret = snd_pcm_hw_params_set_period_time_near(pad->handle, hwparams, + &period_time, 0); if (ret < 0) goto fail; + msg = "unable to install hw params"; ret = snd_pcm_hw_params(pad->handle, hwparams); if (ret < 0) @@ -166,15 +176,16 @@ static int alsa_init(struct private_alsa_write_data *pad, if (ret == 0) { char *buf, *p; size_t sz; - PARA_INFO_LOG("dumping alsa configuration\n"); + PARA_DEBUG_LOG("dumping alsa configuration\n"); snd_pcm_dump(pad->handle, output_log); + snd_pcm_hw_params_dump(hwparams, output_log); sz = snd_output_buffer_string(output_log, &buf); for (p = buf; p < buf + sz;) { char *q = memchr(p, '\n', buf + sz - p); if (!q) break; *q = '\0'; - PARA_INFO_LOG("%s\n", p); + PARA_DEBUG_LOG("%s\n", p); p = q + 1; } snd_output_close(output_log); @@ -207,11 +218,12 @@ static void alsa_write_pre_select(struct sched *s, struct task *t) sched_request_barrier_or_min_delay(&pad->drain_barrier, s); return; } + /* wait at most 50% of the buffer time */ + sched_request_timeout_ms(pad->buffer_time / 2, s); ret = snd_pcm_poll_descriptors(pad->handle, &pfd, 1); if (ret < 0) { PARA_ERROR_LOG("could not get alsa poll fd: %s\n", snd_strerror(-ret)); - t->error = -E_ALSA; return; } pad->poll_fd = pfd.fd; @@ -296,21 +308,13 @@ again: wn->min_iqs = pad->bytes_per_frame; goto again; } - if (pad->poll_fd < 0 || !FD_ISSET(pad->poll_fd, &s->rfds)) - return 0; frames = bytes / pad->bytes_per_frame; frames = snd_pcm_writei(pad->handle, data, frames); if (frames == 0 || frames == -EAGAIN) { - /* - * The alsa poll fd was ready for IO but we failed to write to - * the alsa handle. This means another event is pending. We - * don't care about that but we have to dispatch the event in - * order to avoid a busy loop. So we simply read from the poll - * fd. - */ char buf[100]; - if (read(pad->poll_fd, buf, 100)) - do_nothing; + if (pad->poll_fd >= 0 && FD_ISSET(pad->poll_fd, &s->rfds)) + if (read(pad->poll_fd, buf, 100)) + do_nothing; return 0; } if (frames > 0) { diff --git a/ao_write.c b/ao_write.c index 730653c1..63d18afa 100644 --- a/ao_write.c +++ b/ao_write.c @@ -28,6 +28,7 @@ struct private_aow_data { pthread_t thread; pthread_attr_t attr; + /* The mutex and the condition variable serialize access to ->btrn */ pthread_mutex_t mutex; pthread_cond_t data_available; struct btr_node *thread_btrn; @@ -39,19 +40,46 @@ static void aow_close(struct writer_node *wn) if (!pawd) return; + assert(!pawd->thread_btrn); ao_close(pawd->dev); free(pawd); wn->private_data = NULL; - ao_shutdown(); } static void aow_pre_select(struct sched *s, struct task *t) { struct writer_node *wn = container_of(t, struct writer_node, task); - int ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); + struct private_aow_data *pawd = wn->private_data; + int ret; - if (ret == 0) - return; + if (!pawd) { /* not yet started */ + assert(wn->btrn); + ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); + if (ret != 0) + goto min_delay; + return; /* no data available */ + } + if (!wn->btrn) { /* EOF */ + if (!pawd->thread_btrn) /* ready to exit */ + goto min_delay; + /* wait for the play thread to terminate */ + goto timeout; + } + pthread_mutex_lock(&pawd->mutex); + ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); + pthread_mutex_unlock(&pawd->mutex); + if (ret != 0) + goto min_delay; + /* + * Even though the node status is zero, we might have data available, + * but the output buffer is full. If we don't set a timeout here, we + * are woken up only if new data arrives, which might be too late and + * result in a buffer underrun in the playing thread. To avoid this we + * never sleep longer than the (default) buffer time. + */ +timeout: + return sched_request_timeout_ms(20, s); +min_delay: sched_min_delay(s); } @@ -129,7 +157,6 @@ static int aow_init(struct writer_node *wn, unsigned sample_rate, struct private_aow_data *pawd = para_malloc(sizeof(*pawd)); struct ao_write_args_info *conf = wn->conf; - ao_initialize(); if (conf->driver_given) { ret = -E_AO_BAD_DRIVER; id = ao_driver_id(conf->driver_arg); @@ -192,16 +219,12 @@ __noreturn static void *aow_play(void *priv) char *data; int ret; + pthread_mutex_lock(&pawd->mutex); for (;;) { - /* - * Lock mutex and wait for signal. pthread_cond_wait() will - * automatically and atomically unlock mutex while it waits. - */ - pthread_mutex_lock(&pawd->mutex); for (;;) { ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF); if (ret < 0) - goto unlock; + goto fail; if (ret > 0) { btr_merge(btrn, wn->min_iqs); bytes = btr_next_buffer(btrn, &data); @@ -210,26 +233,35 @@ __noreturn static void *aow_play(void *priv) break; /* eof and less than a single frame available */ ret = -E_WRITE_COMMON_EOF; - goto unlock; + goto fail; } - //PARA_CRIT_LOG("waiting for data\n"); - //usleep(1000); - //pthread_mutex_unlock(&pawd->mutex); - pthread_cond_wait(&pawd->data_available, &pawd->mutex); + /* + * No data available, go to sleep and wait for the main + * thread to wake us up. pthread_cond_wait() unlocks + * the mutex while it waits and locks it again upon + * return. + */ + ret = pthread_cond_wait(&pawd->data_available, + &pawd->mutex); + /* pthread_cond_wait() can never fail here */ + assert(ret == 0); } - pthread_mutex_unlock(&pawd->mutex); assert(frames > 0); bytes = frames * pawd->bytes_per_frame; - ret = -E_AO_PLAY; - if (ao_play(pawd->dev, data, bytes) == 0) /* failure */ - goto out; + pthread_mutex_unlock(&pawd->mutex); + ret = ao_play(pawd->dev, data, bytes); + pthread_mutex_lock(&pawd->mutex); + if (ret == 0) { /* failure */ + ret = -E_AO_PLAY; + goto fail; + } btr_consume(btrn, bytes); } -unlock: - pthread_mutex_unlock(&pawd->mutex); -out: +fail: + btr_remove_node(&pawd->thread_btrn); assert(ret < 0); PARA_NOTICE_LOG("%s\n", para_strerror(-ret)); + pthread_mutex_unlock(&pawd->mutex); pthread_exit(NULL); } @@ -314,21 +346,32 @@ static int aow_post_select(__a_unused struct sched *s, goto remove_thread_btrn; return 0; } + if (!wn->btrn) { + if (!pawd->thread_btrn) { + pthread_join(pawd->thread, NULL); + return -E_AO_EOF; + } + PARA_INFO_LOG("waiting for play thread to terminate\n"); + return 0; + } pthread_mutex_lock(&pawd->mutex); ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF); if (ret > 0) { btr_pushdown(wn->btrn); - pthread_cond_signal(&pawd->data_available); + if (pthread_cond_signal(&pawd->data_available) != 0) { + ret = -E_AO_PTHREAD; + PARA_ERROR_LOG("pthread_cond_signal() failed\n"); + goto remove_thread_btrn; + } } - pthread_mutex_unlock(&pawd->mutex); - if (ret >= 0) + if (ret >= 0) { + pthread_mutex_unlock(&pawd->mutex); goto out; - pthread_mutex_lock(&pawd->mutex); + } btr_remove_node(&wn->btrn); - PARA_INFO_LOG("waiting for thread to terminate\n"); pthread_cond_signal(&pawd->data_available); pthread_mutex_unlock(&pawd->mutex); - pthread_join(pawd->thread, NULL); + return 0; remove_thread_btrn: btr_remove_node(&pawd->thread_btrn); remove_btrn: @@ -410,6 +453,5 @@ void ao_write_init(struct writer *w) dh[num_lines] = NULL; w->help.detailed_help = (const char **)dh; ao_write_cmdline_parser_free(&dummy); - ao_shutdown(); } diff --git a/audiod.c b/audiod.c index d815c4aa..a8f40183 100644 --- a/audiod.c +++ b/audiod.c @@ -1153,13 +1153,10 @@ static void close_unused_slots(void) */ void __noreturn clean_exit(int status, const char *msg) { - int i; - if (socket_name) unlink(socket_name); close_stat_pipe(); - FOR_EACH_SLOT(i) - close_slot(i); + close_unused_slots(); audiod_cmdline_parser_free(&conf); close_stat_clients(); PARA_EMERG_LOG("%s\n", msg); diff --git a/buffer_tree.c b/buffer_tree.c index 5afa3ad3..44b73c94 100644 --- a/buffer_tree.c +++ b/buffer_tree.c @@ -771,6 +771,12 @@ void btr_drain(struct btr_node *btrn) btr_drop_buffer_reference(br); } +static void btr_free_node(struct btr_node *btrn) +{ + free(btrn->name); + free(btrn); +} + /** * Remove a node from a buffer tree. * @@ -798,8 +804,7 @@ void btr_remove_node(struct btr_node **btrnp) btr_drain(btrn); if (btrn->parent) list_del(&btrn->node); - free(btrn->name); - free(btrn); + btr_free_node(btrn); out: *btrnp = NULL; } @@ -862,6 +867,7 @@ void btr_splice_out_node(struct btr_node **btrnp) list_del(&ch->node); } assert(list_empty(&btrn->children)); + btr_free_node(btrn); *btrnp = NULL; } diff --git a/client.c b/client.c index c1ef5217..b39a8b01 100644 --- a/client.c +++ b/client.c @@ -114,7 +114,6 @@ static int execute_client_command(const char *cmd, char **result) schedule(&command_sched); *result = exec_task.result_buf; btr_remove_node(&exec_task.btrn); - client_disconnect(ct); ret = 1; out: btr_remove_node(&exec_task.btrn); @@ -444,7 +443,6 @@ static int client_i9e_line_handler(char *line) { int ret; - client_disconnect(ct); PARA_DEBUG_LOG("line: %s\n", line); ret = make_client_argv(line); if (ret <= 0) diff --git a/client.h b/client.h index 82dbc033..e304f092 100644 --- a/client.h +++ b/client.h @@ -52,7 +52,6 @@ struct client_task { char **features; }; -void client_disconnect(struct client_task *ct); void client_close(struct client_task *ct); int client_parse_config(int argc, char *argv[], struct client_task **ct_ptr, int *loglevel); diff --git a/client_common.c b/client_common.c index 900d3653..8212abb1 100644 --- a/client_common.c +++ b/client_common.c @@ -32,44 +32,17 @@ /** The size of the receiving buffer. */ #define CLIENT_BUFSIZE 4000 -/** - * Close the connection to para_server and deallocate per-command resources. - * - * \param ct The client task. - * - * This frees all resources of the current command but keeps the configuration - * in \p ct->conf. - * - * \sa \ref client_close(). - */ -void client_disconnect(struct client_task *ct) -{ - if (!ct) - return; - if (ct->scc.fd >= 0) - close(ct->scc.fd); - free_argv(ct->features); - ct->features = NULL; - sc_free(ct->scc.recv); - ct->scc.recv = NULL; - sc_free(ct->scc.send); - ct->scc.send = NULL; - btr_remove_node(&ct->btrn[0]); - btr_remove_node(&ct->btrn[1]); -} - /** * Close the connection to para_server and free all resources. * * \param ct Pointer to the client data. * - * \sa \ref client_open(), \ref client_disconnect(). + * \sa \ref client_open(). */ void client_close(struct client_task *ct) { if (!ct) return; - client_disconnect(ct); free(ct->user); free(ct->config_file); free(ct->key_file); @@ -476,6 +449,16 @@ out: btr_remove_node(&ct->btrn[1]); if (ret != -E_SERVER_CMD_SUCCESS && ret != -E_SERVER_CMD_FAILURE) PARA_ERROR_LOG("%s\n", para_strerror(-ret)); + if (ct->scc.fd >= 0) { + close(ct->scc.fd); + ct->scc.fd = -1; + } + free_argv(ct->features); + ct->features = NULL; + sc_free(ct->scc.recv); + ct->scc.recv = NULL; + sc_free(ct->scc.send); + ct->scc.send = NULL; return ret; } diff --git a/configure.ac b/configure.ac index 07ecffdd..30bce8ee 100644 --- a/configure.ac +++ b/configure.ac @@ -757,7 +757,15 @@ if test "$have_readline" = "yes"; then fi if test "$have_readline" = "yes"; then - : + AC_CHECK_DECL( + [rl_free_keymap], + [AC_DEFINE(RL_FREE_KEYMAP_DECLARED, 1, readline >= 6.3)], + [], + [ + #include + #include + ] + ) AC_SUBST(readline_cppflags) AC_SUBST(readline_ldflags) AC_DEFINE(HAVE_READLINE, 1, define to 1 to turn on readline support) diff --git a/crypt_common.c b/crypt_common.c index 71aabf53..fb6e1ef4 100644 --- a/crypt_common.c +++ b/crypt_common.c @@ -90,41 +90,31 @@ int base64_decode(char const *src, unsigned char *target, size_t targsize) switch (state) { case 0: - if (target) { - if (tarindex >= targsize) - return -E_BASE64; - target[tarindex] = (pos - Base64) << 2; - } + if (tarindex >= targsize) + return -E_BASE64; + target[tarindex] = (pos - Base64) << 2; state = 1; break; case 1: - if (target) { - if (tarindex + 1 >= targsize) - return -E_BASE64; - target[tarindex] |= (pos - Base64) >> 4; - target[tarindex+1] = ((pos - Base64) & 0x0f) - << 4 ; - } + if (tarindex + 1 >= targsize) + return -E_BASE64; + target[tarindex] |= (pos - Base64) >> 4; + target[tarindex + 1] = ((pos - Base64) & 0x0f) << 4; tarindex++; state = 2; break; case 2: - if (target) { - if (tarindex + 1 >= targsize) - return -E_BASE64; - target[tarindex] |= (pos - Base64) >> 2; - target[tarindex+1] = ((pos - Base64) & 0x03) - << 6; - } + if (tarindex + 1 >= targsize) + return -E_BASE64; + target[tarindex] |= (pos - Base64) >> 2; + target[tarindex + 1] = ((pos - Base64) & 0x03) << 6; tarindex++; state = 3; break; case 3: - if (target) { - if (tarindex >= targsize) - return -E_BASE64; - target[tarindex] |= (pos - Base64); - } + if (tarindex >= targsize) + return -E_BASE64; + target[tarindex] |= pos - Base64; tarindex++; state = 0; break; @@ -170,7 +160,7 @@ int base64_decode(char const *src, unsigned char *target, size_t targsize) * zeros. If we don't check them, they become a * subliminal channel. */ - if (target && target[tarindex] != 0) + if (target[tarindex] != 0) return -E_BASE64; } } else { diff --git a/error.h b/error.h index 301e2ca5..ba8409df 100644 --- a/error.h +++ b/error.h @@ -181,6 +181,7 @@ extern const char **para_errlist[]; PARA_ERROR(AO_PLAY, "ao_play() failed"), \ PARA_ERROR(AO_BAD_SAMPLE_FORMAT, "ao: unsigned sample formats not supported"), \ PARA_ERROR(AO_PTHREAD, "pthread error"), \ + PARA_ERROR(AO_EOF, "ao: end of file"), \ #define COMPRESS_FILTER_ERRORS \ diff --git a/flac_afh.c b/flac_afh.c index 21f86f46..eeb0e86c 100644 --- a/flac_afh.c +++ b/flac_afh.c @@ -80,7 +80,7 @@ static FLAC__int64 meta_tell_cb(FLAC__IOHandle handle) static int meta_eof_cb(FLAC__IOHandle handle) { struct private_flac_afh_data *pfad = handle; - return pfad->fpos == pfad->map_bytes - 1; + return pfad->fpos == pfad->map_bytes; } static int meta_close_cb(FLAC__IOHandle __a_unused handle) diff --git a/gui.c b/gui.c index 0f0a4a94..5a2e659d 100644 --- a/gui.c +++ b/gui.c @@ -560,7 +560,6 @@ static void setup_signal_handling(void) para_install_sighandler(SIGCHLD); para_install_sighandler(SIGWINCH); para_install_sighandler(SIGUSR1); - para_sigaction(SIGHUP, SIG_IGN); } /* kill every process in the process group and exit */ diff --git a/interactive.c b/interactive.c index ee953bf1..9f2b7195 100644 --- a/interactive.c +++ b/interactive.c @@ -227,6 +227,7 @@ static void wipe_bottom_line(void) fprintf(i9ep->stderr_stream, "\r"); } +#ifndef RL_FREE_KEYMAP_DECLARED /** * Free all storage associated with a keymap. * @@ -237,6 +238,7 @@ static void wipe_bottom_line(void) * \param keymap The keymap to deallocate. */ void rl_free_keymap(Keymap keymap); +#endif /** * Reset the terminal and save the in-memory command line history. diff --git a/m4/gengetopt/alsa_write.m4 b/m4/gengetopt/alsa_write.m4 index be56c984..b2c56218 100644 --- a/m4/gengetopt/alsa_write.m4 +++ b/m4/gengetopt/alsa_write.m4 @@ -8,11 +8,29 @@ include(header.m4) option "device" d #~~~~~~~~~~~~~~~~ "set PCM device" -string typestr="device" -default="default" +string typestr = "device" +default = "default" optional -details=" - On systems with dmix, a better choice than the default - value might be to use \"plug:swmix\". +details = " + Check for the presence of a /proc/asound/ directory to see if + ALSA is present in your kernel. The file /proc/asound/devices + contains all devices ALSA knows about. " + +option "buffer-time" B +#~~~~~~~~~~~~~~~~~~~~~ +"duration of the ALSA buffer" +int typestr = "milliseconds" +default = "170" +optional +details = " + This is only a hint as ALSA might pick a slightly different + time, depending on the sound hardware. The chosen value is + shown in debug output as BUFFER_TIME. + + If synchronization between multiple clients is desired, + the same buffer time should be configured for all clients. +" + + diff --git a/oggdec_filter.c b/oggdec_filter.c index 1eaa7930..3222b4aa 100644 --- a/oggdec_filter.c +++ b/oggdec_filter.c @@ -136,14 +136,13 @@ open: 0, /* no initial bytes */ ovc); /* the ov_open_callbacks */ if (oret == OV_ENOTVORBIS || oret == OV_EBADHEADER) { - /* this might be due to the input buffer being too small */ + /* maybe the input buffer is too small */ if (!btr_no_parent(btrn)) { fn->min_iqs += 1000; iqs = btr_get_input_queue_size(btrn); ret = 0; if (iqs < fn->min_iqs) goto out; - PARA_CRIT_LOG("iqs: %zu\n", iqs); btr_merge(btrn, fn->min_iqs); pod->converted = 0; goto open; @@ -236,13 +235,8 @@ static int ogg_post_select(__a_unused struct sched *s, struct task *t) break; fn->min_iqs = 0; have += ret; - if (have < OGGDEC_OUTPUT_CHUNK_SIZE) - continue; - if (btr_get_output_queue_size(btrn) > OGGDEC_MAX_OUTPUT_SIZE) + if (have >= OGGDEC_OUTPUT_CHUNK_SIZE) break; - btr_add_output(buf, have, btrn); - buf = para_malloc(OGGDEC_OUTPUT_CHUNK_SIZE); - have = 0; } pod->have_more = (ret > 0); if (have > 0) { diff --git a/opusdec_filter.c b/opusdec_filter.c index d7489859..9022fbab 100644 --- a/opusdec_filter.c +++ b/opusdec_filter.c @@ -277,7 +277,7 @@ static void opusdec_pre_select(struct sched *s, struct task *t) if (ret != 0) return sched_min_delay(s); - if (ctx->have_more) + if (!ctx->have_more) return; if (btr_get_output_queue_size(fn->btrn) <= OPUSDEC_MAX_OUTPUT_SIZE) return sched_min_delay(s); diff --git a/t/makefile.test b/t/makefile.test index a2bd4e53..6ef9ac84 100644 --- a/t/makefile.test +++ b/t/makefile.test @@ -8,6 +8,7 @@ test_options += --results-dir $(results_dir) test_options += --trash-dir $(trash_dir) test_options += --executables "$(prefixed_executables)" test_options += --objects "$(basename $(all_objs))" +test_options += --man-dir $(man_dir) ifdef V ifeq ("$(origin V)", "command line") diff --git a/t/t0005-man.sh b/t/t0005-man.sh new file mode 100755 index 00000000..7a627aad --- /dev/null +++ b/t/t0005-man.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +test_description='Parse generated man pages. + +Simple sanity checks of some man pages. + +* para_audiod: Check that list of audiod commands is present +* para_server: Check that list of server/afs commands is present +* para_play: Check that list of play commands is present + +* para_{recv,filter,write,audiod}: Check for presence of +filter/receiver/writer options as appropriate ' + +. ${0%/*}/test-lib.sh + +rfw_regex='Options for .\{100,\}Options for ' # recv/filter/writer + +grep_man() +{ + local regex="$1" exe="$2" + tr '\n' ' ' < "$o_man_dir/para_$exe.1" | grep -q "$regex" +} + +# check that options of all reveivers/filters/writers are contained +# in the man pages + +regex="$rfw_regex" +test_expect_success 'para_recv: receiver options' "grep_man '$regex' recv" +test_expect_success 'para_filter: filter options' "grep_man '$regex' filter" +test_expect_success 'para_write: writer options' "grep_man '$regex' write" +test_require_objects "audiod" +if [[ -n "$result" ]]; then + test_skip 'para_audiod' "missing object(s): $result" +else + test_expect_success 'para_audiod: recv/filter/writer options' \ + "grep_man '$regex' audiod" +fi + +# check various command lists + +test_require_objects "audiod" +if [[ -n "$result" ]]; then + test_skip 'para_audiod' "missing object(s): $result" +else + regex='LIST OF AUDIOD COMMANDS.\{200,\}' + test_expect_success 'para_audiod: command list' \ + "grep_man '$regex' audiod" +fi + +test_require_objects "server" +missing_objects="$result" +if [[ -n "$missing_objects" ]]; then + test_skip "para_server" "missing object(s): $missing_objects" +else + regex='LIST OF SERVER COMMANDS.\{100,\}LIST OF AFS COMMANDS' + test_expect_success 'para_server: server/afs commands' \ + "grep_man '$regex' server" +fi + +# para_play is always built +regex='LIST OF COMMANDS.\{100,\}' +test_expect_success 'para_play: play commands' "grep_man '$regex' play" +regex="$rfw_regex" +test_done diff --git a/t/test-lib.sh b/t/test-lib.sh index f1bb8cf6..0e702b53 100644 --- a/t/test-lib.sh +++ b/t/test-lib.sh @@ -21,22 +21,22 @@ say_color() if [[ "$o_nocolor" != "true" && -n "$1" ]]; then export TERM=$ORIGINAL_TERM case "$1" in - error) tput bold; tput setaf 1;; - skip) tput setaf 5;; + error) tput $C_BOLD; tput $C_SETAF 1;; + skip) tput $C_SETAF 5;; ok) (($o_verbose == 0)) && return - tput setaf 2;; - pass) tput bold; tput setaf 2;; - info) tput setaf 3;; + tput $C_SETAF 2;; + pass) tput $C_BOLD; tput $C_SETAF 2;; + info) tput $C_SETAF 3;; run) (($o_verbose == 0)) && return - tput setaf 6;; + tput $C_SETAF 6;; esac fi shift printf "%s\n" "$*" if [[ "$o_nocolor" != "true" && -n "$1" ]]; then - tput sgr0 + tput $C_SGR0 export TERM=dumb fi } @@ -218,9 +218,21 @@ can_use_colors() result="false" [[ "$TERM" == "dumb" ]] && return [[ -t 1 ]] || return - tput bold >/dev/null 2>&1 || return - tput setaf 1 >/dev/null 2>&1 || return - tput sgr0 >/dev/null 2>&1 || return + C_BOLD='bold' + tput $C_BOLD &>/dev/null || { + C_BOLD='md' + tput $C_BOLD &>/dev/null + } || return + C_SETAF='setaf' + tput $C_SETAF 1 &>/dev/null || { + C_SETAF='AF' + tput $C_SETAF 1 &>/dev/null + } || return + C_SGR0='sgr0' + tput $C_SGR0 >/dev/null 2>&1 || { + C_SGR0='me' + tput $C_SGR0 &>/dev/null + } || return result="true" } @@ -235,6 +247,7 @@ parse_options() -v=1|--verbose=1) o_verbose="1"; shift;; -v|--verbose|-v=2|--verbose=2) o_verbose="2"; shift;; --no-color) o_nocolor="true"; shift;; + --man-dir) export o_man_dir="$2"; shift; shift;; --results-dir) o_results_dir="$2"; shift; shift;; --trash-dir) o_trash_dir="$2"; shift; shift;; --executables-dir) export o_executables_dir="$2"; shift; shift;; @@ -265,11 +278,13 @@ fixup_dirs() [[ -z "$o_results_dir" ]] && o_results_dir="$test_dir/test-results" [[ -z "$o_executables_dir" ]] && o_executables_dir="$test_dir/.." [[ -z "$o_trash_dir" ]] && o_trash_dir="$test_dir/trashes" + [[ -z "$o_man_dir" ]] && o_man_dir="$test_dir/../build/man/man1" # we want alsolute paths because relative paths become invalid # after changing to the trash dir [[ -n "${o_results_dir##/*}" ]] && o_results_dir="$wd/$o_results_dir" [[ -n "${o_executables_dir##/*}" ]] && o_executables_dir="$wd/$o_results_dir" + [[ -n "${o_man_dir##/*}" ]] && o_man_dir="$wd/$o_man_dir" [[ -n "${o_trash_dir##/*}" ]] && o_trash_dir="$wd/$o_trash_dir" mkdir -p "$o_results_dir" diff --git a/version.c b/version.c index 8526bbcc..3a190e63 100644 --- a/version.c +++ b/version.c @@ -57,7 +57,7 @@ const char *version_text(const char *pfx) static char buf[512]; snprintf(buf, sizeof(buf) - 1, "%s\n" - "Copyright (C) 2013-2014 Andre Noll\n" + "Copyright (C) 2014 Andre Noll\n" "This is free software with ABSOLUTELY NO WARRANTY." " See COPYING for details.\n" "Report bugs to .\n" diff --git a/wma_afh.c b/wma_afh.c index 7ac1ba64..c429020c 100644 --- a/wma_afh.c +++ b/wma_afh.c @@ -132,7 +132,7 @@ static const char album_tag_header[] = { /* WM/AlbumTitle */ static void read_asf_tags(const char *buf, int buf_size, struct taginfo *ti) { const char *p, *end = buf + buf_size, *q; - uint16_t len1, len2, len3, len4, len5; + uint16_t len1, len2, len3, len4; p = search_pattern(comment_header, sizeof(comment_header), buf, buf_size); @@ -149,7 +149,7 @@ static void read_asf_tags(const char *buf, int buf_size, struct taginfo *ti) p += 2; len4 = read_u16(p); p += 2; - len5 = read_u16(p); + /* ignore length of the rating information */ p += 2; if (p + len1 >= end) goto next; @@ -158,10 +158,10 @@ static void read_asf_tags(const char *buf, int buf_size, struct taginfo *ti) if (p + len2 >= end) goto next; ti->artist = get_str16(p, len2); - p += len2 + len3 + len4; - if (p + len5 >= end) + p += len2 + len3; + if (p + len4 >= end) goto next; - ti->comment = get_str16(p, len5); + ti->comment = get_str16(p, len4); next: p = search_pattern(extended_content_header, sizeof(extended_content_header), buf, buf_size);