From: Andre Noll Date: Sun, 17 Mar 2024 11:38:21 +0000 (+0100) Subject: Merge topic branch t/play into pu X-Git-Url: http://git.tuebingen.mpg.de/?a=commitdiff_plain;h=f192fc3c0f824b951c4cb275d7b54b2651de7e8e;hp=8e875411f29b017313482637cbc5b74a69f4ddde;p=paraslash.git Merge topic branch t/play into pu This small series contains a few minor tweaks for para_play. The most obvious change is that para_play is no longer built on systems which lack libreadline. * refs/heads/t/play: play: Shut down alsa on input EOF. play: Simplify and improve get_key_map_seq(). play: Remove pointless goto in play_post_monitor(). Return from filter_setup() so callers can reset the terminal. Let para_play depend on libreadline. # Conflicts: # configure.ac --- diff --git a/Makefile.real b/Makefile.real index 21d5fc03..fec328a6 100644 --- a/Makefile.real +++ b/Makefile.real @@ -37,7 +37,7 @@ test_dir := t yy_src_dir := yy yy_build_dir := $(build_dir)/yy -executables := recv filter audioc write afh play +executables := recv filter audioc write afh ifneq ($(CRYPTOLIB),) ifeq ($(HAVE_OSL),yes) executables += server upgrade_db @@ -225,48 +225,48 @@ ifeq ($(HAVE_FAAD),yes) audio_format_handlers += aac endif -play_objs := $(addsuffix .o, \ - play fd sched buffer_tree time string net afh_recv afh_common \ - wma_afh wma_common mp3_afh recv_common udp_recv http_recv dccp_recv \ - filter_common fec bitstream imdct wav_filter compress_filter \ - amp_filter prebuffer_filter fecdec_filter wmadec_filter write_common \ - file_write version sync_filter lsu \ -) -ifeq ($(NEED_OGG_OBJECTS),yes) - play_objs += ogg_afh_common.o -endif -ifeq ($(NEED_VORBIS_OBJECTS),yes) - play_objs += oggdec_filter.o ogg_afh.o -endif -ifeq ($(NEED_SPEEX_OBJECTS),yes) - play_objs += spxdec_filter.o spx_afh.o spx_common.o -endif -ifeq ($(NEED_OPUS_OBJECTS),yes) - play_objs += opusdec_filter.o opus_afh.o opus_common.o -endif -ifeq ($(NEED_FLAC_OBJECTS),yes) - play_objs += flacdec_filter.o flac_afh.o -endif -ifeq ($(HAVE_FAAD),yes) - play_objs += aac_afh.o aacdec_filter.o mp4.o -endif -ifeq ($(HAVE_MAD),yes) - play_objs += mp3dec_filter.o -endif -ifeq ($(HAVE_OSS),yes) - play_objs += oss_write.o -endif -ifeq ($(HAVE_ALSA),yes) - play_objs += alsa_write.o -endif -ifeq ($(NEED_AO_OBJECTS),yes) - play_objs += ao_write.o -endif ifeq ($(HAVE_READLINE),yes) - play_objs += interactive.o -endif -ifeq ($(HAVE_SAMPLERATE),yes) - play_objs += resample_filter.o check_wav.o + executables += play + play_objs := $(addsuffix .o, \ + play fd sched buffer_tree time string net afh_recv afh_common \ + wma_afh wma_common mp3_afh recv_common udp_recv http_recv dccp_recv \ + filter_common fec bitstream imdct wav_filter compress_filter \ + amp_filter prebuffer_filter fecdec_filter wmadec_filter write_common \ + file_write version sync_filter lsu interactive \ + ) + ifeq ($(NEED_OGG_OBJECTS),yes) + play_objs += ogg_afh_common.o + endif + ifeq ($(NEED_VORBIS_OBJECTS),yes) + play_objs += oggdec_filter.o ogg_afh.o + endif + ifeq ($(NEED_SPEEX_OBJECTS),yes) + play_objs += spxdec_filter.o spx_afh.o spx_common.o + endif + ifeq ($(NEED_OPUS_OBJECTS),yes) + play_objs += opusdec_filter.o opus_afh.o opus_common.o + endif + ifeq ($(NEED_FLAC_OBJECTS),yes) + play_objs += flacdec_filter.o flac_afh.o + endif + ifeq ($(HAVE_FAAD),yes) + play_objs += aac_afh.o aacdec_filter.o mp4.o + endif + ifeq ($(HAVE_MAD),yes) + play_objs += mp3dec_filter.o + endif + ifeq ($(HAVE_OSS),yes) + play_objs += oss_write.o + endif + ifeq ($(HAVE_ALSA),yes) + play_objs += alsa_write.o + endif + ifeq ($(NEED_AO_OBJECTS),yes) + play_objs += ao_write.o + endif + ifeq ($(HAVE_SAMPLERATE),yes) + play_objs += resample_filter.o check_wav.o + endif endif write_objs := $(addsuffix .o, write write_common file_write time fd \ diff --git a/audiod.c b/audiod.c index 0fae454c..3e86af53 100644 --- a/audiod.c +++ b/audiod.c @@ -829,11 +829,14 @@ static int parse_stream_command(const char *txt, const char **cmd) static int add_filter(int format, const char *cmdline) { struct audio_format_info *a = &afi[format]; - int filter_num, nf = a->num_filters; + int ret, filter_num, nf = a->num_filters; void *cfg; struct lls_parse_result *flpr; - filter_num = filter_setup(cmdline, &cfg, &flpr); + ret = filter_setup(cmdline, &cfg, &flpr); + if (ret < 0) + return ret; + filter_num = ret; a->filter_lpr = arr_realloc(a->filter_lpr, nf + 1, sizeof(flpr)); a->filter_conf = arr_realloc(a->filter_conf, nf + 1, sizeof(void *)); a->filter_nums = arr_realloc(a->filter_nums, nf + 1, sizeof(unsigned)); diff --git a/filter.c b/filter.c index 722cb16f..50447ec0 100644 --- a/filter.c +++ b/filter.c @@ -128,7 +128,10 @@ int main(int argc, char *argv[]) struct task_info ti; fn = fns[i] = zalloc(sizeof(*fn)); - fn->filter_num = filter_setup(fa, &fn->conf, &filter_lpr); + ret = filter_setup(fa, &fn->conf, &filter_lpr); + if (ret < 0) + goto teardown; + fn->filter_num = ret; name = filter_name(fn->filter_num); fn->lpr = filter_lpr; PARA_DEBUG_LOG("filter #%d: %s\n", i, name); @@ -153,6 +156,7 @@ int main(int argc, char *argv[]) btr_log_tree(sit->btrn, LL_INFO); ret = schedule(&s); sched_shutdown(&s); +teardown: for (i--; i >= 0; i--) { struct filter_node *fn = fns[i]; diff --git a/filter_common.c b/filter_common.c index f48e4570..7966b806 100644 --- a/filter_common.c +++ b/filter_common.c @@ -67,11 +67,11 @@ const char *filter_name(int filter_num) * filter, optionally followed by options for this filter. If yes, call the * command line parser of that filter and its ->setup method. * - * \return This function either succeeds or does not return. On success, the - * number of the filter is returned and conf is initialized to point to the - * filter configuration as returned by the filter's ->setup() method, if any. - * Moreover, *lprp is initialized to contain the parsed command line options. - * On errors, the function calls exit(EXIT_FAILURE). + * \return On success, the number of the filter is returned and conf is + * initialized to point to the filter configuration as returned by the filter's + * ->setup() method, if any. Moreover, *lprp is initialized to contain the + * parsed command line options. On errors a negative paraslash error code is + * returned. */ int filter_setup(const char *fa, void **conf, struct lls_parse_result **lprp) { @@ -80,9 +80,10 @@ int filter_setup(const char *fa, void **conf, struct lls_parse_result **lprp) const struct lls_command *cmd; const struct filter *f; + *lprp = NULL; ret = create_argv(fa, " \t\n", &argv); if (ret < 0) - goto fail; + return ret; argc = ret; ret = lls(lls_lookup_subcmd(argv[0], filter_cmd_suite, &errctx)); if (ret < 0) @@ -105,12 +106,10 @@ 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); + return ret; } /** diff --git a/play.c b/play.c index bd183b6b..c9a9f7d7 100644 --- a/play.c +++ b/play.c @@ -24,6 +24,7 @@ #include "recv.h" #include "write.h" #include "fd.h" +#include "interactive.h" /** Array of error strings. */ DEFINE_PARA_ERRLIST; @@ -222,7 +223,7 @@ static int get_playback_error(void) int err; if (!pt->wn.task) - return 0; + return 1; err = task_status(pt->wn.task); if (err >= 0) return 0; @@ -253,7 +254,7 @@ static int eof_cleanup(void) decoder = filter_get(pt->fn.filter_num); task_reap(&pt->fn.task); - if (decoder->close) + if (decoder && decoder->close) decoder->close(&pt->fn); btr_remove_node(&pt->fn.btrn); lls_free_parse_result(pt->fn.lpr, FILTER_CMD(pt->fn.filter_num)); @@ -470,8 +471,6 @@ static void kill_stream(void) task_notify(pt->wn.task, E_EOF); } -#ifdef HAVE_READLINE - /* only called from com_prev(), nec. only if we have readline */ static int previous_valid_file(void) { @@ -488,8 +487,6 @@ static int previous_valid_file(void) return -E_NO_VALID_FILES; } -#include "interactive.h" - /* * Define the default (internal) key mappings and helper functions to get the * key sequence or the command from a key id, which is what we obtain from @@ -573,15 +570,17 @@ static inline char *get_internal_key_map_seq(int key) return para_strdup(default_keyseqs[get_internal_key_map_idx(key)]); } -static char *get_user_key_map_seq(int key) +static char *get_key_map_seq(int key) { - const char *kma = get_user_key_map_arg(key); - const char *p = strchr(kma + 1, ':'); + const char *kma, *p; char *result; int len; - if (!p) - return NULL; + if (is_internal_key(key)) + return get_internal_key_map_seq(key); + kma = get_user_key_map_arg(key); + p = strchr(kma + 1, ':'); + assert(p); /* We checked earlier that kma contains a colon */ len = p - kma; result = alloc(len + 1); memcpy(result, kma, len); @@ -589,12 +588,6 @@ static char *get_user_key_map_seq(int key) return result; } -static char *get_key_map_seq(int key) -{ - return is_internal_key(key)? - get_internal_key_map_seq(key) : get_user_key_map_seq(key); -} - static char *get_key_map_seq_safe(int key) { const char hex[] = "0123456789abcdef"; @@ -1098,64 +1091,6 @@ static void session_update_time_string(char *str, unsigned len) i9e_print_status_bar(str, len); } -/* - * If we are about to die we must call i9e_close() to reset the terminal. - * However, i9e_close() must be called in *this* context, i.e. from - * play_task.post_monitor() rather than from i9e_post_monitor(), because - * otherwise i9e would access freed memory upon return. So the play task must - * stay alive until the i9e task terminates. - * - * We achieve this by sending a fake SIGTERM signal via i9e_signal_dispatch() - * and reschedule. In the next iteration, i9e->post_monitor returns an error and - * terminates. Subsequent calls to i9e_get_error() then return negative and we - * are allowed to call i9e_close() and terminate as well. - */ -static int session_post_monitor(__a_unused struct sched *s) -{ - int ret; - - if (pt->background) - detach_stdout(); - else - attach_stdout(__FUNCTION__); - ret = i9e_get_error(); - if (ret < 0) { - kill_stream(); - i9e_close(); - para_log = stderr_log; - free(ici.history_file); - return ret; - } - if (get_playback_state() == 'X') - i9e_signal_dispatch(SIGTERM); - return 0; -} - -#else /* HAVE_READLINE */ - -static int session_post_monitor(struct sched *s) -{ - char c; - - if (!sched_read_ok(STDIN_FILENO, s)) - return 0; - if (read(STDIN_FILENO, &c, 1)) - do_nothing; - kill_stream(); - return 1; -} - -static void session_open(void) -{ -} - -static void session_update_time_string(char *str, __a_unused unsigned len) -{ - printf("\r%s ", str); - fflush(stdout); -} -#endif /* HAVE_READLINE */ - static void play_pre_monitor(struct sched *s, __a_unused void *context) { char state; @@ -1194,18 +1129,24 @@ static unsigned get_time_string(char **result) ); } -static int play_post_monitor(struct sched *s, __a_unused void *context) +static int play_post_monitor(__a_unused struct sched *s, __a_unused void *context) { - int ret; + int ret, i9e_error; + if (pt->background) + detach_stdout(); + else + attach_stdout(__FUNCTION__); + i9e_error = i9e_get_error(); ret = eof_cleanup(); - if (ret < 0) { - pt->rq = CRT_TERM_RQ; - return 0; - } - ret = session_post_monitor(s); - if (ret < 0) - goto out; + if (pt->rq == CRT_TERM_RQ || i9e_error < 0) /* com_quit() or CTRL+D */ + kill_stream(); /* terminate receiver/filter/writer */ + if ((ret < 0 || pt->rq == CRT_TERM_RQ) && i9e_error >= 0) + i9e_signal_dispatch(SIGTERM); /* terminate the i9e task */ + if (ret < 0) /* unexpected error from the writer node */ + return ret; + if (ret != 0 && i9e_error < 0) /* eof, and i9e has died */ + return i9e_error; if (!pt->wn.btrn && !pt->fn.btrn) { char state = get_playback_state(); if (state == 'P' || state == 'R' || state == 'F') { @@ -1214,8 +1155,7 @@ static int play_post_monitor(struct sched *s, __a_unused void *context) if (ret < 0) { PARA_ERROR_LOG("%s\n", para_strerror(-ret)); pt->rq = CRT_TERM_RQ; - ret = 1; - goto out; + return 1; } pt->next_update = *now; } @@ -1229,9 +1169,7 @@ static int play_post_monitor(struct sched *s, __a_unused void *context) free(str); tv_add(now, &delay, &pt->next_update); } - ret = 1; -out: - return ret; + return 1; } /** @@ -1278,6 +1216,10 @@ int main(int argc, char *argv[]) }, &sched); ret = schedule(&sched); sched_shutdown(&sched); + i9e_close(); + wipe_receiver_node(); + para_log = stderr_log; + free(ici.history_file); if (ret < 0) PARA_ERROR_LOG("%s\n", para_strerror(-ret)); return ret < 0? EXIT_FAILURE : EXIT_SUCCESS; diff --git a/t/t0005-man.sh b/t/t0005-man.sh index ee1949c0..307be124 100755 --- a/t/t0005-man.sh +++ b/t/t0005-man.sh @@ -58,7 +58,12 @@ else "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" +test_require_objects 'play' +missing_objects="$result" +if [[ -n "$missing_objects" ]]; then + test_skip 'para_play' "missing object(s): $missing_objects" +else + regex='LIST OF COMMANDS.\{100,\}' + test_expect_success 'para_play: play commands' "grep_man '$regex' play" +fi test_done diff --git a/web/manual.md b/web/manual.md index b5329ea0..a2942d71 100644 --- a/web/manual.md +++ b/web/manual.md @@ -385,9 +385,10 @@ the ao writer (ESD, PulseAudio,...). Debian package: `libao-dev`. para_gui. Debian package: `libncurses-dev`. - [GNU -Readline](http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html). If -this library (`libreadline-dev`) is installed, para_client, para_audioc -and para_play support interactive sessions. +Readline](http://cnswww.cns.cwru.edu/php/chet/readline/rltop.html). Only if +this library (`libreadline-dev`) is installed, para_play is built, Without it, +para_client(1) and para_audioc(1) still work, but lack support for interactive +sessions. Installation ------------