X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=audiod.c;h=fc4ac6f5017b2eb5c4876d0c29a0bd53ba512b8f;hp=d3f45ec95f32b046910903af1f57351a5cb1998d;hb=9627e8157c8c916555b993d7254b0b638815ebc1;hpb=63b57e3e60923cea4e3e1808e3133789db2dc83a diff --git a/audiod.c b/audiod.c index d3f45ec9..fc4ac6f5 100644 --- a/audiod.c +++ b/audiod.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2008 Andre Noll + * Copyright (C) 2005-2009 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -7,12 +7,14 @@ /** \file audiod.c the paraslash's audio daemon */ #include #include +#include #include "para.h" #include "error.h" #include "audiod.cmdline.h" #include "list.h" #include "sched.h" +#include "ggo.h" #include "recv.h" #include "filter.h" #include "grab_client.cmdline.h" @@ -65,6 +67,9 @@ struct audio_format_info { struct slot_info slot[MAX_STREAM_SLOTS]; +/** The array of status items sent by para_server. */ +char *stat_item_values[NUM_STAT_ITEMS] = {NULL}; + /** * the current mode of operation of which can be changed by the on/off/cycle * commands. It is either, AUDIOD_OFF, AUDIOD_ON or AUDIOD_STANDBY. @@ -90,7 +95,7 @@ static struct status_task status_task_struct; * * \sa struct status_task */ -struct status_task *stat_task = &status_task_struct; +static struct status_task *stat_task = &status_task_struct; static struct timeval initial_delay_barrier; /** @@ -127,13 +132,62 @@ int get_audio_format_num(char *name) return -E_UNSUPPORTED_AUDIO_FORMAT; } +char *get_time_string(struct timeval *newest_stime) +{ + struct timeval diff, adj_stream_start, tmp; + int total = 0, use_server_time = 1, + length_seconds = stat_task->length_seconds; + + if (!stat_task->playing) { + if (length_seconds) + return NULL; + return make_message("%s:\n", status_item_list[SI_PLAY_TIME]); + } + if (audiod_status == AUDIOD_OFF) + goto out; + if (stat_task->sa_time_diff_sign > 0) + tv_diff(&stat_task->server_stream_start, &stat_task->sa_time_diff, + &adj_stream_start); + else + tv_add(&stat_task->server_stream_start, &stat_task->sa_time_diff, + &adj_stream_start); + tmp = adj_stream_start; + if (newest_stime && audiod_status == AUDIOD_ON) { + tv_diff(newest_stime, &adj_stream_start, &diff); + if (tv2ms(&diff) < 5000) { + tmp = *newest_stime; + use_server_time = 0; + } + } + tv_diff(now, &tmp, &diff); + total = diff.tv_sec + stat_task->offset_seconds; + if (total > length_seconds) + total = length_seconds; + if (total < 0) + total = 0; +out: + return make_message( + "%s: %s%d:%02d [%d:%02d] (%d%%/%d:%02d)\n", + status_item_list[SI_PLAY_TIME], + use_server_time? "~" : "", + total / 60, + total % 60, + (length_seconds - total) / 60, + (length_seconds - total) % 60, + length_seconds? (total * 100 + length_seconds / 2) / + length_seconds : 0, + length_seconds / 60, + length_seconds % 60 + ); +} + /** * the log function of para_audiod * * \param ll loglevel * \param fmt the format string */ -void para_log(int ll, const char* fmt,...) +__printf_2_3 void para_log(int ll, const char* fmt,...) { va_list argp; FILE *outfd; @@ -160,16 +214,39 @@ void para_log(int ll, const char* fmt,...) va_end(argp); } -static char *configfile_exists(void) +static void parse_config_or_die(void) { - char *home = para_homedir(); - char *config_file = make_message("%s/.paraslash/audiod.conf", - home); - free(home); - if (file_exists(config_file)) - return config_file; + char *config_file; + + if (conf.config_file_given) + config_file = para_strdup(conf.config_file_arg); + else { + char *home = para_homedir(); + config_file = make_message("%s/.paraslash/audiod.conf", home); + free(home); + } + if (conf.config_file_given && !file_exists(config_file)) { + PARA_EMERG_LOG("can not read config file %s\n", config_file); + goto err; + } + if (config_file) { + struct audiod_cmdline_parser_params params = { + .override = 0, + .initialize = 0, + .check_required = 1, + .check_ambiguity = 0, + .print_errors = 1 + }; + if (audiod_cmdline_parser_config_file(config_file, &conf, ¶ms)) { + PARA_EMERG_LOG("parse error in config file\n"); + goto err; + } + } free(config_file); - return NULL; + return; +err: + free(config_file); + exit(EXIT_FAILURE); } static void setup_signal_handling(void) @@ -274,7 +351,7 @@ static void open_filters(int slot_num) return; PARA_INFO_LOG("opening %s filters\n", audio_formats[s->format]); s->fc = para_calloc(sizeof(struct filter_chain)); - s->fc->filter_nodes = para_malloc(nf * sizeof(struct filter_chain)); + s->fc->filter_nodes = para_malloc(nf * sizeof(struct filter_node)); s->fc->inbuf = s->receiver_node->buf; s->fc->in_loaded = &s->receiver_node->loaded; s->fc->input_error = &s->receiver_node->task.error; @@ -294,7 +371,7 @@ static void open_filters(int slot_num) INIT_LIST_HEAD(&fn->callbacks); f->open(fn); PARA_NOTICE_LOG("%s filter %d/%d (%s) started in slot %d\n", - audio_formats[s->format], i + 1, nf, f->name, slot_num); + audio_formats[s->format], i, nf, f->name, slot_num); s->fc->outbuf = fn->buf; s->fc->out_loaded = &fn->loaded; } @@ -338,35 +415,13 @@ static void open_writers(int slot_num) activate_inactive_grab_clients(slot_num, s->format, s->fc); } -#if 0 -static void rn_event_handler(struct task *t) -{ - struct receiver_node *rn = t->private_data; - int i; - - PARA_NOTICE_LOG("%s\n", para_strerror(-t->ret)); - unregister_task(t); - rn->error = t->ret; - /* set restart barrier */ - FOR_EACH_SLOT(i) { - struct timeval restart_delay = {0, 10 * 1000}; - if (slot[i].receiver_node != rn) - continue; - if (rn->error != -E_RECV_EOF) - /* don't reconnect immediately on errors */ - restart_delay.tv_sec = 5; - tv_add(now, &restart_delay, &afi[slot[i].format].restart_barrier); - } -} -#endif - static int open_receiver(int format) { struct audio_format_info *a = &afi[format]; struct slot_info *s; int ret, slot_num; struct receiver_node *rn; - const struct timeval restart_delay = {1, 0}; + const struct timeval restart_delay = {2, 0}; ret = get_empty_slot(); if (ret < 0) @@ -390,9 +445,10 @@ static int open_receiver(int format) rn->task.post_select = a->receiver->post_select; sprintf(rn->task.status, "%s receiver node", rn->receiver->name); register_task(&rn->task); - return 1; + ret = 1; err: - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); + if (ret < 0) + PARA_ERROR_LOG("%s\n", para_strerror(-ret)); tv_add(now, &restart_delay, &afi[format].restart_barrier); return ret; } @@ -403,8 +459,7 @@ static int receiver_running(int format) FOR_EACH_SLOT(i) { struct slot_info *s = &slot[i]; - if (s->format == format && s->receiver_node - && s->receiver_node->task.error >= 0) + if (s->format == format && s->receiver_node) return 1; } return 0; @@ -488,8 +543,8 @@ static int check_stat_line(char *line, __a_unused void *data) tmp = make_message("%s\n", line); stat_client_write(tmp, itemnum); free(tmp); - free(stat_task->stat_item_values[itemnum]); - stat_task->stat_item_values[itemnum] = para_strdup(line); + free(stat_item_values[itemnum]); + stat_item_values[itemnum] = para_strdup(line); ilen = strlen(status_item_list[itemnum]); switch (itemnum) { case SI_STATUS: @@ -525,8 +580,6 @@ static int check_stat_line(char *line, __a_unused void *data) struct timeval tv = {sec, usec}; compute_time_diff(&tv); } - if (stat_task->clock_diff_count) - stat_task->clock_diff_count--; break; case SI_FORMAT: stat_task->current_audio_format_num = get_audio_format_num( @@ -535,96 +588,6 @@ static int check_stat_line(char *line, __a_unused void *data) return 1; } -static void try_to_close_slot(int slot_num) -{ - struct slot_info *s = &slot[slot_num]; - - if (s->format < 0) - return; - if (s->receiver_node && s->receiver_node->task.error >= 0) - return; - if (s->fc && s->fc->task.error >= 0) - return; - if (s->wng && s->wng->task.error >= 0) - return; - PARA_INFO_LOG("closing slot %d\n", slot_num); - wng_close(s->wng); - close_filters(s->fc); - free(s->fc); - close_receiver(slot_num); - clear_slot(slot_num); -} - -/* - * Check if any receivers/filters/writers need to be started and do so if - * necessary. Since the pre_select function didn't have a chance yet to put - * file descriptors into the fd sets given by s, make the upcoming select() - * return immediately to avoid a long timeout in case we started something. - */ -static void audiod_pre_select(struct sched *s, __a_unused struct task *t) -{ - int i; - struct timeval min_delay = {0, 1}; - - if (audiod_status != AUDIOD_ON || !stat_task->playing) - return kill_all_decoders(-E_NOT_PLAYING); - if (open_current_receiver(s)) - s->timeout = min_delay; - FOR_EACH_SLOT(i) { - struct slot_info *sl = &slot[i]; - struct audio_format_info *a; - struct timeval diff; - - if (sl->format < 0) - continue; - a = &afi[sl->format]; - if (!sl->receiver_node) - continue; - if ((!a->num_filters || sl->fc) && sl->wng) - continue; /* everything already started */ - if (!a->num_filters) { - if (sl->receiver_node->loaded && !sl->wng) { - open_writers(i); - s->timeout = min_delay; - } - continue; - } - if (sl->receiver_node->loaded && !sl->fc) { - open_filters(i); - s->timeout = min_delay; - continue; - } - if (sl->wng || !sl->fc || !*sl->fc->out_loaded) - continue; - if (tv_diff(now, &initial_delay_barrier, &diff) > 0) { - open_writers(i); - s->timeout = min_delay; - continue; - } - PARA_INFO_LOG("initial delay: %lu ms left\n", tv2ms(&diff)); - if (tv_diff(&s->timeout, &diff, NULL) > 0) { - s->timeout = diff; - } - } -} - -static void audiod_post_select(__a_unused struct sched *s, - __a_unused struct task *t) -{ - int i; - - FOR_EACH_SLOT(i) - try_to_close_slot(i); -} - -static void init_audiod_task(struct task *t) -{ - t->pre_select = audiod_pre_select; - t->post_select = audiod_post_select; - t->error = 0; - sprintf(t->status, "audiod task"); -} - static int parse_stream_command(const char *txt, char **cmd) { char *p = strchr(txt, ':'); @@ -652,7 +615,7 @@ static int add_filter(int format, char *cmdline) return filter_num; a->filter_nums[nf] = filter_num; a->num_filters++; - PARA_INFO_LOG("%s filter %d: %s\n", audio_formats[format], nf + 1, + PARA_INFO_LOG("%s filter %d: %s\n", audio_formats[format], nf, filters[filter_num].name); return filter_num; } @@ -664,7 +627,7 @@ static int init_writers(void) struct audio_format_info *a; init_supported_writers(); - nw = PARA_MAX(1, conf.writer_given); + nw = PARA_MAX(1U, conf.writer_given); PARA_INFO_LOG("maximal number of writers: %d\n", nw); FOR_EACH_AUDIO_FORMAT(i) { a = &afi[i]; @@ -781,7 +744,7 @@ static int init_filters(void) int i, ret, nf; filter_init(filters); - nf = PARA_MAX(1, conf.filter_given); + nf = PARA_MAX(1U, conf.filter_given); PARA_INFO_LOG("maximal number of filters: %d\n", nf); FOR_EACH_AUDIO_FORMAT(i) { afi[i].filter_conf = para_malloc(nf * sizeof(void *)); @@ -869,13 +832,12 @@ static void signal_pre_select(struct sched *s, struct task *t) static void signal_post_select(struct sched *s, struct task *t) { struct signal_task *st = container_of(t, struct signal_task, task); - int signum; if (!FD_ISSET(st->fd, &s->rfds)) return; - signum = para_next_signal(); - switch (signum) { + st->signum = para_next_signal(); + switch (st->signum) { case SIGINT: case SIGTERM: case SIGHUP: @@ -928,23 +890,14 @@ static void close_stat_pipe(void) client_close(stat_task->ct); stat_task->ct = NULL; FOR_EACH_STATUS_ITEM(i) { - free(stat_task->stat_item_values[i]); - stat_task->stat_item_values[i] = NULL; + free(stat_item_values[i]); + stat_item_values[i] = NULL; } dump_empty_status(); stat_task->length_seconds = 0; stat_task->offset_seconds = 0; audiod_status_dump(); stat_task->playing = 0; - stat_task->stat_item_values[SI_BASENAME] = make_message( - "%s: no connection to para_server\n", - status_item_list[SI_BASENAME]); - stat_client_write(stat_task->stat_item_values[SI_BASENAME], - SI_BASENAME); - if (stat_task->clock_diff_count) { - stat_task->clock_diff_barrier.tv_sec = now->tv_sec + 1; - stat_task->clock_diff_barrier.tv_usec = now->tv_usec; - } } /** @@ -969,76 +922,153 @@ void __noreturn clean_exit(int status, const char *msg) } /* avoid busy loop if server is down */ -static void set_stat_task_restart_barrier(void) +static void set_stat_task_restart_barrier(unsigned seconds) { - struct timeval delay = {5, 0}; + struct timeval delay = {seconds, 0}; tv_add(now, &delay, &stat_task->restart_barrier); } -/* restart the client task if necessary */ -static void status_pre_select(struct sched *s, struct task *t) +static void try_to_close_slot(int slot_num) { - struct status_task *st = container_of(t, struct status_task, task); - int ret; + struct slot_info *s = &slot[slot_num]; - if (st->ct || audiod_status == AUDIOD_OFF) /* no need to restart */ + if (s->format < 0) return; - if (!st->clock_diff_count && tv_diff(now, &st->restart_barrier, NULL) - < 0) + if (s->receiver_node && s->receiver_node->task.error != -E_TASK_UNREGISTERED) return; - if (st->clock_diff_count) { /* get status only one time */ - char *argv[] = {"audiod", "stat", "1", NULL}; - int argc = 3; - if (tv_diff(now, &st->clock_diff_barrier, NULL) < 0) - return; - PARA_INFO_LOG("clock diff count: %d\n", st->clock_diff_count); - ret = client_open(argc, argv, &st->ct); + if (s->fc && s->fc->task.error != -E_TASK_UNREGISTERED) + return; + if (s->wng && s->wng->task.error != -E_TASK_UNREGISTERED) + return; + PARA_INFO_LOG("closing slot %d\n", slot_num); + wng_close(s->wng); + close_filters(s->fc); + free(s->fc); + close_receiver(slot_num); + clear_slot(slot_num); +} - } else { - char *argv[] = {"audiod", "stat", NULL}; - int argc = 2; - ret = client_open(argc, argv, &st->ct); +/* + * Check if any receivers/filters/writers need to be started and do so if + * necessary. + */ +static void start_stop_decoders(struct sched *s) +{ + int i; + + FOR_EACH_SLOT(i) + try_to_close_slot(i); + if (audiod_status != AUDIOD_ON || !stat_task->playing) + return kill_all_decoders(-E_NOT_PLAYING); + open_current_receiver(s); + FOR_EACH_SLOT(i) { + struct slot_info *sl = &slot[i]; + struct audio_format_info *a; + struct timeval diff; + + if (sl->format < 0) + continue; + a = &afi[sl->format]; + if (!sl->receiver_node) + continue; + if ((!a->num_filters || sl->fc) && sl->wng) + continue; /* everything already started */ + if (!a->num_filters) { + if (sl->receiver_node->loaded && !sl->wng) { + open_writers(i); + } + continue; + } + if (sl->receiver_node->loaded && !sl->fc) { + open_filters(i); + continue; + } + if (sl->wng || !sl->fc || !*sl->fc->out_loaded) + continue; + if (tv_diff(now, &initial_delay_barrier, &diff) > 0) { + open_writers(i); + continue; + } + PARA_INFO_LOG("initial delay: %lu ms left\n", tv2ms(&diff)); + if (tv_diff(&s->timeout, &diff, NULL) > 0) { + s->timeout = diff; + } } - set_stat_task_restart_barrier(); - if (ret < 0) - return; - s->timeout.tv_sec = 0; - s->timeout.tv_usec = 1; } -static void status_post_select(__a_unused struct sched *s, struct task *t) + +/* restart the client task if necessary */ +static void status_pre_select(struct sched *s, struct task *t) { struct status_task *st = container_of(t, struct status_task, task); - unsigned bytes_left; - if (!st->ct || st->ct->status != CL_RECEIVING) - return; - if (audiod_status == AUDIOD_OFF || st->ct->task.error < 0) { - if (st->ct->task.error >= 0) - unregister_task(&st->ct->task); - if (audiod_status == AUDIOD_OFF) - st->clock_diff_count = conf.clock_diff_count_arg; + if (audiod_status == AUDIOD_OFF) { + if (!st->ct) + goto out; + if (st->ct->task.error >= 0) { + st->ct->task.error = -E_AUDIOD_OFF; + goto out; + } + if (st->ct->task.error != -E_TASK_UNREGISTERED) + goto out; close_stat_pipe(); - return; + st->clock_diff_count = conf.clock_diff_count_arg; + goto out; } - bytes_left = for_each_line(st->ct->buf, st->ct->loaded, - &check_stat_line, NULL); - if (st->ct->loaded != bytes_left) { - st->last_status_read = *now; - st->ct->loaded = bytes_left; - } else { - struct timeval diff; - tv_diff(now, &st->last_status_read, &diff); - if (diff.tv_sec > 61) + if (st->ct) { + unsigned bytes_left; + if (st->ct->task.error < 0) { + if (st->ct->task.error != -E_TASK_UNREGISTERED) + goto out; close_stat_pipe(); + goto out; + } + if (st->ct->status != CL_RECEIVING) + goto out; + bytes_left = for_each_line(st->ct->buf, st->ct->loaded, + &check_stat_line, NULL); + if (st->ct->loaded != bytes_left) { + st->last_status_read = *now; + st->ct->loaded = bytes_left; + } else { + struct timeval diff; + tv_diff(now, &st->last_status_read, &diff); + if (diff.tv_sec > 61) + st->ct->task.error = -E_STATUS_TIMEOUT; + } + goto out; } + if (tv_diff(now, &st->restart_barrier, NULL) < 0) + goto out; + if (st->clock_diff_count) { /* get status only one time */ + char *argv[] = {"audiod", "stat", "1", NULL}; + int argc = 3; + PARA_INFO_LOG("clock diff count: %d\n", st->clock_diff_count); + st->clock_diff_count--; + client_open(argc, argv, &st->ct); + set_stat_task_restart_barrier(2); + + } else { + char *argv[] = {"audiod", "stat", NULL}; + int argc = 2; + client_open(argc, argv, &st->ct); + set_stat_task_restart_barrier(5); + } + free(stat_item_values[SI_BASENAME]); + stat_item_values[SI_BASENAME] = make_message( + "%s: no connection to para_server\n", + status_item_list[SI_BASENAME]); + stat_client_write(stat_item_values[SI_BASENAME], + SI_BASENAME); + st->last_status_read = *now; +out: + start_stop_decoders(s); } static void init_status_task(struct status_task *st) { memset(st, 0, sizeof(struct status_task)); st->task.pre_select = status_pre_select; - st->task.post_select = status_post_select; st->sa_time_diff_sign = 1; st->clock_diff_count = conf.clock_diff_count_arg; st->current_audio_format_num = -1; @@ -1062,6 +1092,24 @@ static void set_initial_status(void) PARA_WARNING_LOG("invalid mode\n"); } +__noreturn static void print_help_and_die(void) +{ + int d = conf.detailed_help_given; + const char **p = d? audiod_args_info_detailed_help + : audiod_args_info_help; + + printf_or_die("%s\n\n", AUDIOD_CMDLINE_PARSER_PACKAGE "-" + AUDIOD_CMDLINE_PARSER_VERSION); + printf_or_die("%s\n\n", audiod_args_info_usage); + for (; *p; p++) + printf_or_die("%s\n", *p); + print_receiver_helps(d); + print_filter_helps(d); + print_writer_helps(d); + exit(0); +} + + /** * the main function of para_audiod * @@ -1074,39 +1122,32 @@ static void set_initial_status(void) * */ int main(int argc, char *argv[]) { - char *config_file; int ret, i; - struct sched s; + static struct sched s; struct command_task command_task_struct, *cmd_task = &command_task_struct; - struct task audiod_task_struct, *audiod_task = &audiod_task_struct; + struct audiod_cmdline_parser_params params = { + .override = 0, + .initialize = 1, + .check_required = 0, + .check_ambiguity = 0, + .print_errors = 1 + }; valid_fd_012(); - audiod_cmdline_parser(argc, argv, &conf); + audiod_cmdline_parser_ext(argc, argv, &conf, ¶ms); HANDLE_VERSION_FLAG("audiod", conf); - para_drop_privileges(conf.user_arg, conf.group_arg); - config_file = configfile_exists(); - if (config_file) { - struct audiod_cmdline_parser_params params = { - .override = 0, - .initialize = 0, - .check_required = 0, - .check_ambiguity = 0 - - }; - if (audiod_cmdline_parser_config_file(config_file, &conf, ¶ms)) { - PARA_EMERG_LOG("parse error in config file\n"); - exit(EXIT_FAILURE); - } - free(config_file); - } + drop_privileges_or_die(conf.user_arg, conf.group_arg); + parse_config_or_die(); if (conf.logfile_given) logfile = open_log(conf.logfile_arg); - log_welcome("para_audiod", conf.loglevel_arg); i = init_stream_io(); + if (conf.help_given || conf.detailed_help_given) + print_help_and_die(); if (i < 0) { PARA_EMERG_LOG("init stream io error: %s\n", para_strerror(-i)); exit(EXIT_FAILURE); } + log_welcome("para_audiod", conf.loglevel_arg); server_uptime(UPTIME_SET); set_initial_status(); FOR_EACH_SLOT(i) @@ -1117,7 +1158,6 @@ int main(int argc, char *argv[]) init_status_task(stat_task); init_command_task(cmd_task); - init_audiod_task(audiod_task); if (conf.daemon_given) daemon_init(); @@ -1125,7 +1165,6 @@ int main(int argc, char *argv[]) register_task(&sig_task->task); register_task(&cmd_task->task); register_task(&stat_task->task); - register_task(audiod_task); s.default_timeout.tv_sec = 0; s.default_timeout.tv_usec = 99 * 1000; ret = schedule(&s);