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
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 \
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));
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);
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];
* 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)
{
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)
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;
}
/**
#include "recv.h"
#include "write.h"
#include "fd.h"
+#include "interactive.h"
/** Array of error strings. */
DEFINE_PARA_ERRLIST;
int err;
if (!pt->wn.task)
- return 0;
+ return 1;
err = task_status(pt->wn.task);
if (err >= 0)
return 0;
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));
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)
{
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
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;
- assert(p);
+ 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);
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";
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;
);
}
-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') {
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;
}
free(str);
tv_add(now, &delay, &pt->next_update);
}
- ret = 1;
-out:
- return ret;
+ return 1;
}
/**
}, &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));
free(get_confdir());
"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
- [curses](ftp://ftp.gnu.org/pub/gnu/ncurses). Needed for
para_gui. Debian package: `libncurses-dev`.
-- [GNU
-Readline](https://www.gnu.org/software/readline/). If
-this library (`libreadline-dev`) is installed, para_client, para_audioc
-and para_play support interactive sessions.
+- [GNU Readline](https://www.gnu.org/software/readline/). 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
------------