X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=audiod.c;h=40a35c6a601cf5839b044e44be35992b6e8036d3;hp=e0a455d223f9822bdb4c366915661514f64b7543;hb=c387b216b5fd3c944053e1bba024815719e9a0d9;hpb=63c4f3404cfb3aea943949d81b03bca14dbea559 diff --git a/audiod.c b/audiod.c index e0a455d2..40a35c6a 100644 --- a/audiod.c +++ b/audiod.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2009 Andre Noll + * Copyright (C) 2005-2011 Andre Noll * * Licensed under the GPL v2. For licencing details see COPYING. */ @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include "para.h" #include "error.h" @@ -19,6 +19,7 @@ #include "sched.h" #include "ggo.h" #include "recv.h" +#include "buffer_tree.h" #include "filter.h" #include "grab_client.h" #include "client.cmdline.h" @@ -90,6 +91,7 @@ struct status_task { struct timeval restart_barrier; /** Last time we received status data from para_server. */ struct timeval last_status_read; + size_t min_iqs; /** The offset value announced by para_server. */ int offset_seconds; /** The length of the current audio file as announced by para_server. */ @@ -108,6 +110,8 @@ struct status_task { struct timeval clock_diff_barrier; /** Number of the audio format as announced by para_server. */ int current_audio_format_num; + /* The status task btrn is the child of the client task. */ + struct btr_node *btrn; }; /** The array of status items sent by para_server. */ @@ -138,7 +142,6 @@ static struct status_task status_task_struct; * \sa struct status_task */ static struct status_task *stat_task = &status_task_struct; -static struct timeval initial_delay_barrier; /** * the task for handling audiod commands @@ -156,13 +159,14 @@ struct command_task { #define FOR_EACH_AUDIO_FORMAT(af) for (af = 0; af < NUM_AUDIO_FORMATS; af++) /** - * get the audio format number - * \param name the name of the audio format + * Get the audio format number. + * + * \param name The name of the audio format. * * \return The audio format number on success, -E_UNSUPPORTED_AUDIO_FORMAT if * \a name is not a supported audio format. */ -int get_audio_format_num(const char *name) +static int get_audio_format_num(const char *name) { int i; @@ -174,13 +178,37 @@ int get_audio_format_num(const char *name) return -E_UNSUPPORTED_AUDIO_FORMAT; } +/** + * Compute the play time based on information of the given slot. + * + * \param slot_num The slot number (negative means: no slot). + * + * This computes a string of the form "0:07 [3:33] (3%/3:40)" using information + * from the status items received from para_server and the start time of the + * (first) writer of the given slot. + * + * It has to to take into account that probably the stream was not started at + * the beginning of the file, that the clock between the server and the client + * host may differ and that playback of the stream was delayed, e.g. because + * the prebuffer filter is used in the filter configuration of the given slot. + * + * If no writer is active in the given slot, or \a slot_num is negative + * (indicating that para_audiod runs in standby mode), an approximation based + * only on the status items is computed and the returned string is prefixed + * with "~". + * + * \return A string that must be freed by the caller. + */ char *get_time_string(int slot_num) { int ret, seconds = 0, length; struct timeval *tmp, sum, sss, /* server stream start */ + rstime, /* receiver start time */ + wstime, /* writer start time */ wtime, /* now - writer start */ rskip; /* receiver start - sss */ struct slot_info *s = slot_num < 0? NULL : &slot[slot_num]; + char *msg; if (audiod_status == AUDIOD_OFF) goto empty; @@ -191,27 +219,41 @@ char *get_time_string(int slot_num) } if (audiod_status == AUDIOD_ON && !s) goto empty; - /* valid status items and playing */ - if (s) { /* writer active in this slot */ - length = s->seconds_total; - tmp = &s->server_stream_start; - } else { /* standby mode, rely on status items */ - length = stat_task->length_seconds; - tmp = &stat_task->server_stream_start; + /* + * Valid status items and playing, set length and tmp to the stream + * start. We use the slot info and fall back to the info from current + * status items if no slot info is available. + */ + length = stat_task->length_seconds; + tmp = &stat_task->server_stream_start; + if (s && s->wns) { /* writer active in this slot */ + btr_get_node_start(s->wns[0].btrn, &wstime); + if (wstime.tv_sec != 0) { /* writer wrote something */ + if (s->server_stream_start.tv_sec == 0) { + /* copy status info to slot */ + s->server_stream_start = stat_task->server_stream_start; + s->offset_seconds = stat_task->offset_seconds; + s->seconds_total = stat_task->length_seconds; + } + length = s->seconds_total; + tmp = &s->server_stream_start; + } } if (stat_task->sa_time_diff_sign > 0) tv_diff(tmp, &stat_task->sa_time_diff, &sss); else tv_add(tmp, &stat_task->sa_time_diff, &sss); - if (!s) { + if (!s || !s->wns) { struct timeval diff; tv_diff(now, &sss, &diff); seconds = diff.tv_sec + stat_task->offset_seconds; goto out; } - tv_diff(now, &s->wstime, &wtime); + tv_diff(now, &wstime, &wtime); + //PARA_CRIT_LOG("offset %d\n", s->offset_seconds); seconds = s->offset_seconds; - ret = tv_diff(&s->rstime, &sss, &rskip); + btr_get_node_start(s->receiver_node->btrn, &rstime); + ret = tv_diff(&rstime, &sss, &rskip); if (ret > 0) { /* audiod was started in the middle of the stream */ tv_add(&wtime, &rskip, &sum); seconds += sum.tv_sec; @@ -220,7 +262,7 @@ char *get_time_string(int slot_num) out: seconds = PARA_MIN(seconds, length); seconds = PARA_MAX(seconds, 0); - return make_message( + msg = make_message( "%s%d:%02d [%d:%02d] (%d%%/%d:%02d)", s? "" : "~", seconds / 60, @@ -231,6 +273,8 @@ out: length / 60, length % 60 ); + PARA_DEBUG_LOG("slot %d: %s\n", slot_num, msg); + return msg; empty: return para_strdup(NULL); } @@ -310,29 +354,98 @@ static void close_receiver(int slot_num) PARA_NOTICE_LOG("closing %s receiver in slot %d\n", audio_formats[s->format], slot_num); a->receiver->close(s->receiver_node); + btr_free_node(s->receiver_node->btrn); free(s->receiver_node); s->receiver_node = NULL; + tv_add(now, &(struct timeval)EMBRACE(0, 200 * 1000), + &a->restart_barrier); } -static void kill_all_decoders(int error) +static void writer_cleanup(struct writer_node *wn) +{ + struct writer *w; + + if (!wn) + return; + w = writers + wn->writer_num; + PARA_INFO_LOG("closing %s\n", writer_names[wn->writer_num]); + w->close(wn); + btr_free_node(wn->btrn); +} + +static void close_writers(struct slot_info *s) { + struct audio_format_info *a; int i; + if (s->format < 0) + return; + assert(s->wns); + a = afi + s->format; + if (a->num_writers == 0) + writer_cleanup(s->wns); + else { + for (i = 0; i < a->num_writers; i++) + writer_cleanup(s->wns + i); + } + free(s->wns); + s->wns = NULL; +} + +static void close_filters(struct slot_info *s) +{ + int i; + struct audio_format_info *a = afi + s->format; + if (a->num_filters == 0) + return; + for (i = 0; i < a->num_filters; i++) { + struct filter_node *fn = s->fns + i; + struct filter *f; + + if (!fn) + continue; + f = filters + fn->filter_num; + if (f->close) + f->close(fn); + btr_free_node(fn->btrn); + } + free(s->fns); + s->fns = NULL; +} + +/* + * Whenever a task commits suicide by returning from post_select with t->error + * < 0, it also removes its btr node. We do exactly that to kill a running + * task. Note that the scheduler checks t->error also _before_ each pre/post + * select call, so the victim will never be scheduled again. + */ +static void kill_btrn(struct btr_node *btrn, struct task *t, int error) +{ + if (t->error < 0) + return; + t->error = error; + btr_remove_node(btrn); +} + +static void kill_all_decoders(int error) +{ + int i, j; + FOR_EACH_SLOT(i) { struct slot_info *s = &slot[i]; - if (s->wng && s->wng->task.error >= 0) { - PARA_INFO_LOG("deactivating wng in slot %d\n", - i); - s->wng->task.error = error; - } - if (s->fc && s->fc->task.error >= 0) { - PARA_INFO_LOG("deactivatimg filter chain in slot %d\n", i); - s->fc->task.error = error; - } - if (s->receiver_node && s->receiver_node->task.error >= 0) { - PARA_INFO_LOG("deactivating receiver_node in slot %d\n", i); - s->receiver_node->task.error = error; - } + struct audio_format_info *a; + if (s->format < 0) + continue; + a = afi + s->format; + if (s->wns) + for (j = 0; j < a->num_writers; j++) + kill_btrn(s->wns[j].btrn, &s->wns[j].task, error); + if (s->fns) + for (j = 0; j < a->num_writers; j++) + kill_btrn(s->fns[j].btrn, &s->wns[j].task, error); + if (s->receiver_node) + kill_btrn(s->receiver_node->btrn, &s->receiver_node->task, + error); } } @@ -347,7 +460,7 @@ static int get_empty_slot(void) clear_slot(i); return i; } - if (s->wng || s->receiver_node || s->fc) + if (s->wns || s->receiver_node || s->fns) continue; clear_slot(i); return i; @@ -355,186 +468,173 @@ static int get_empty_slot(void) return -E_NO_MORE_SLOTS; } -/** - * get the number of filters - * - * \param audio_format_num the number identifying the audio format - * - * \return the number of filters for the given audio format - * - * \sa struct filter; - */ -int num_filters(int audio_format_num) -{ - return afi[audio_format_num].num_filters; -} - -static void open_filters(int slot_num) +static void open_filters(struct slot_info *s) { - struct slot_info *s = &slot[slot_num]; - struct audio_format_info *a = &afi[s->format]; + struct audio_format_info *a = afi + s->format; struct filter_node *fn; int nf = a->num_filters; + struct btr_node *parent; int i; - s->fc = NULL; - if (!nf) + if (nf == 0) 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_node)); - s->fc->inbufp = &s->receiver_node->buf; - s->fc->in_loaded = &s->receiver_node->loaded; - s->fc->input_error = &s->receiver_node->task.error; - s->fc->task.pre_select = NULL; - s->fc->task.post_select = filter_post_select; - s->fc->task.error = 0; - s->fc->num_filters = nf; - - s->receiver_node->output_error = &s->fc->task.error; - sprintf(s->fc->task.status, "filter chain"); - FOR_EACH_FILTER_NODE(fn, s->fc, i) { + assert(s->fns == NULL); + s->fns = para_calloc(nf * sizeof(struct filter_node)); + parent = s->receiver_node->btrn; + for (i = 0; i < nf; i++) { struct filter *f = filters + a->filter_nums[i]; + fn = s->fns + i; fn->filter_num = a->filter_nums[i]; fn->conf = a->filter_conf[i]; - fn->fc = s->fc; - fn->loaded = 0; - INIT_LIST_HEAD(&fn->callbacks); + fn->task.pre_select = f->pre_select; + fn->task.post_select = f->post_select; + + fn->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = f->name, .parent = parent, + .handler = f->execute, .context = fn)); + f->open(fn); + register_task(&fn->task); + parent = fn->btrn; PARA_NOTICE_LOG("%s filter %d/%d (%s) started in slot %d\n", - audio_formats[s->format], i, nf, f->name, slot_num); - s->fc->outbufp = &fn->buf; - s->fc->out_loaded = &fn->loaded; + audio_formats[s->format], i, nf, f->name, (int)(s - slot)); + sprintf(fn->task.status, "%s (slot %d)", f->name, (int)(s - slot)); } - register_task(&s->fc->task); } -static void open_writers(int slot_num) +static void open_writers(struct slot_info *s) { - int ret, i; - struct slot_info *s = &slot[slot_num]; - struct audio_format_info *a = &afi[s->format]; - - PARA_INFO_LOG("opening %s writers\n", audio_formats[s->format]); - if (!a->num_writers) - s->wng = setup_default_wng(); - else - s->wng = wng_new(a->num_writers); - if (s->fc) { - s->wng->bufp = s->fc->outbufp; - s->wng->loaded = s->fc->out_loaded; - s->wng->input_error = &s->fc->task.error; - s->wng->channels = &s->fc->channels; - s->wng->samplerate = &s->fc->samplerate; - s->fc->output_error = &s->wng->task.error; - PARA_INFO_LOG("samplerate: %d\n", *s->wng->samplerate); - } else { - s->wng->bufp = &s->receiver_node->buf; - s->wng->loaded = &s->receiver_node->loaded; - s->wng->input_error = &s->receiver_node->task.error; - } - for (i = 0; i < a->num_writers; i++) { - s->wng->writer_nodes[i].conf = a->writer_conf[i]; - s->wng->writer_nodes[i].writer_num = a->writer_nums[i]; + int i; + struct audio_format_info *a = afi + s->format; + struct writer_node *wn; + struct btr_node *parent = s->fns[a->num_filters - 1].btrn; + + assert(s->wns == NULL); + s->wns = para_calloc(PARA_MAX(1U, a->num_writers) + * sizeof(struct writer_node)); + if (a->num_writers == 0) + setup_writer_node(NULL, parent, s->wns); + else { + PARA_INFO_LOG("opening %s writers\n", audio_formats[s->format]); + for (i = 0; i < a->num_writers; i++) { + wn = s->wns + i; + wn->conf = a->writer_conf[i]; + wn->writer_num = a->writer_nums[i]; + register_writer_node(wn, parent); + } } - ret = wng_open(s->wng); - if (ret < 0) - return; - s->wstime = *now; - s->server_stream_start = stat_task->server_stream_start.tv_sec? - stat_task->server_stream_start : *now; - s->offset_seconds = stat_task->offset_seconds; - s->seconds_total = stat_task->length_seconds; - activate_inactive_grab_clients(s->format, s->fc); } +/* returns slot num on success */ static int open_receiver(int format) { struct audio_format_info *a = &afi[format]; struct slot_info *s; int ret, slot_num; + struct receiver *r = a->receiver; struct receiver_node *rn; - const struct timeval restart_delay = {2, 0}; + tv_add(now, &(struct timeval)EMBRACE(2, 0), &a->restart_barrier); ret = get_empty_slot(); if (ret < 0) - goto err; + return ret; slot_num = ret; - s = &slot[slot_num]; - s->format = format; - s->receiver_node = para_calloc(sizeof(struct receiver_node)); - rn = s->receiver_node; - rn->receiver = a->receiver; + rn = para_calloc(sizeof(*rn)); + rn->receiver = r; rn->conf = a->receiver_conf; - ret = a->receiver->open(s->receiver_node); + rn->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = r->name, .context = rn)); + ret = r->open(rn); if (ret < 0) { - free(s->receiver_node); - s->receiver_node = NULL; - goto err; + btr_free_node(rn->btrn); + free(rn); + return ret; } + s = &slot[slot_num]; + s->format = format; + s->receiver_node = rn; PARA_NOTICE_LOG("started %s: %s receiver in slot %d\n", - audio_formats[s->format], a->receiver->name, slot_num); - rn->task.pre_select = a->receiver->pre_select; - rn->task.post_select = a->receiver->post_select; - s->rstime = *now; - sprintf(rn->task.status, "%s receiver node", rn->receiver->name); + audio_formats[format], r->name, slot_num); + rn->task.pre_select = r->pre_select; + rn->task.post_select = r->post_select; + sprintf(rn->task.status, "%s receiver node", r->name); register_task(&rn->task); - ret = 1; -err: - if (ret < 0) - PARA_ERROR_LOG("%s\n", para_strerror(-ret)); - tv_add(now, &restart_delay, &afi[format].restart_barrier); - return ret; + return slot_num; } -/* return: 0: Not running, 1: Running, -1: Running but eof (or error) */ -static int receiver_running(int format) +static bool receiver_running(void) { - int i, ret = 0; + int i; + long unsigned ss1 = stat_task->server_stream_start.tv_sec; FOR_EACH_SLOT(i) { struct slot_info *s = &slot[i]; - if (s->format != format) - continue; + long unsigned ss2 = s->server_stream_start.tv_sec; + if (!s->receiver_node) continue; if (s->receiver_node->task.error >= 0) - return 1; - ret = -1; + return true; + if (ss1 == ss2) + return true; } - return ret; + return false; } -static void open_current_receiver(struct sched *s) +/** + * Return the root node of the current buffer tree. + * + * This is only used for stream grabbing. + * + * \return \p NULL if no slot is currently active. If more than one buffer tree + * exists, the node corresponding to the most recently started receiver is + * returned. + */ +struct btr_node *audiod_get_btr_root(void) { - struct timeval diff; - int ret, cafn = stat_task->current_audio_format_num; + int i, newest_slot = -1; + struct timeval newest_rstime = {0, 0}; - if (cafn < 0 || !stat_task->ct) - return; - /* Do nothing if the 'N' flag is set or the 'P' flag is unset */ - if (stat_task->vss_status != VSS_STATUS_FLAG_PLAYING) - return; - ret = receiver_running(cafn); - if (ret > 0) /* already running and not eof */ - return; - if (ret < 0) { /* eof */ - /* - * para_server uses a zero start time during the announcement - * period, i.e. before it sends the first chunk. Wait until - * this period begins to avoid restarting the receiver that - * belongs to the file just completed. - */ - if (stat_task->server_stream_start.tv_sec) - return; - } - if (tv_diff(now, &afi[cafn].restart_barrier, &diff) < 0) { - /* avoid busy loop */ - s->timeout = diff; - return; + FOR_EACH_SLOT(i) { + struct slot_info *s = &slot[i]; + struct timeval rstime; + if (!s->receiver_node) + continue; + if (s->receiver_node->task.error < 0) + continue; + btr_get_node_start(s->receiver_node->btrn, &rstime); + if (newest_slot >= 0 && tv_diff(&rstime, &newest_rstime, NULL) < 0) + continue; + newest_rstime = rstime; + newest_slot = i; } - /* start a new receiver */ - open_receiver(cafn); + if (newest_slot == -1) + return NULL; + return slot[newest_slot].receiver_node->btrn; +} + +/* whether a new instance of a decoder should be started. */ +static bool must_start_decoder(void) +{ + int cafn = stat_task->current_audio_format_num; + unsigned vs = stat_task->vss_status; + + if (audiod_status != AUDIOD_ON) + return false; + if (cafn < 0) + return false; + if (!stat_task->ct) + return false; + if (vs & VSS_STATUS_FLAG_NEXT) + return false; + if (!(vs & VSS_STATUS_FLAG_PLAYING)) + return false; + if (receiver_running()) + return false; + if (tv_diff(now, &afi[cafn].restart_barrier, NULL) < 0) + return false; + return true; } static unsigned compute_time_diff(const struct timeval *status_time) @@ -569,9 +669,9 @@ static unsigned compute_time_diff(const struct timeval *status_time) &tmp); stat_task->sa_time_diff = tmp; PARA_INFO_LOG("time diff (cur/avg): %s%lums/%s%lums\n", - sign > 0? "+" : "-", + sign < 0? "-" : "+", tv2ms(&diff), - sa_time_diff_sign ? "+" : "-", + sa_time_diff_sign < 0? "-" : "+", tv2ms(&stat_task->sa_time_diff) ); out: @@ -604,20 +704,8 @@ static int update_item(int itemnum, char *buf) break; case SI_STREAM_START: if (sscanf(buf, "%lu.%lu", &sec, &usec) == 2) { - struct timeval a_start, delay; - delay.tv_sec = conf.stream_delay_arg / 1000; - delay.tv_usec = (conf.stream_delay_arg % 1000) * 1000; stat_task->server_stream_start.tv_sec = sec; stat_task->server_stream_start.tv_usec = usec; - if (compute_time_diff(NULL) > 2) { - if (stat_task->sa_time_diff_sign < 0) - tv_add(&stat_task->server_stream_start, - &stat_task->sa_time_diff, &a_start); - else - tv_diff(&stat_task->server_stream_start, - &stat_task->sa_time_diff, &a_start); - tv_add(&a_start, &delay, &initial_delay_barrier); - } } break; case SI_CURRENT_TIME: @@ -720,6 +808,14 @@ static int parse_receiver_args(void) ret = get_audio_format_num(arg); if (ret < 0) goto out; + /* + * If multiple receivers are given for this audio format, the + * last one wins and we have to free the previous receiver + * config here. Since we are iterating backwards, the winning + * receiver arg is in fact the first one given. + */ + if (afi[ret].receiver_conf) + afi[ret].receiver->free_config(afi[ret].receiver_conf); afi[ret].receiver_conf = check_receiver_arg(recv_arg, &receiver_num); if (!afi[ret].receiver_conf) { ret = -E_RECV_SYNTAX; @@ -727,9 +823,9 @@ static int parse_receiver_args(void) } afi[ret].receiver = &receivers[receiver_num]; } - /* use the first available receiver with no arguments - * for those audio formats for which no receiver - * was specified + /* + * Use the first available receiver with no arguments for those audio + * formats for which no receiver was specified. */ cmd = para_strdup(receivers[0].name); FOR_EACH_AUDIO_FORMAT(i) { @@ -758,6 +854,18 @@ static int init_default_filters(void) if (a->num_filters) continue; /* no default -- nothing to to */ + /* + * If udp is used to receive this audiod format, add fecdec as + * the first filter. + */ + if (strcmp(afi[i].receiver->name, "udp") == 0 || + strcmp(afi[i].receiver->name, "dccp") == 0) { + tmp = para_strdup("fecdec"); + add_filter(i, tmp); + free(tmp); + if (ret < 0) + goto out; + } /* add "dec" to audio format name */ tmp = make_message("%sdec", audio_formats[i]); for (j = 0; filters[j].name; j++) @@ -783,7 +891,7 @@ static int parse_filter_args(void) { int i, ret, nf; - nf = PARA_MAX(1U, conf.filter_given); + nf = PARA_MAX(2U, 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 *)); @@ -868,19 +976,16 @@ static void signal_pre_select(struct sched *s, struct task *t) para_fd_set(st->fd, &s->rfds, &s->max_fileno); } -static void signal_post_select(struct sched *s, struct task *t) +static void signal_post_select(struct sched *s, __a_unused struct task *t) { - struct signal_task *st = container_of(t, struct signal_task, task); - - if (!FD_ISSET(st->fd, &s->rfds)) - return; + int signum; - st->signum = para_next_signal(); - switch (st->signum) { + signum = para_next_signal(&s->rfds); + switch (signum) { case SIGINT: case SIGTERM: case SIGHUP: - PARA_EMERG_LOG("terminating on signal %d\n", st->signum); + PARA_EMERG_LOG("terminating on signal %d\n", signum); clean_exit(EXIT_FAILURE, "caught deadly signal"); } } @@ -903,19 +1008,18 @@ static void command_post_select(struct sched *s, struct task *t) int ret; struct command_task *ct = container_of(t, struct command_task, task); static struct timeval last_status_dump; - struct timeval tmp; + struct timeval tmp, delay = {0, 500 * 1000}; - tv_add(&last_status_dump, &(struct timeval){0, 500 * 1000}, &tmp); + tv_add(&last_status_dump, &delay, &tmp); if (tv_diff(&tmp, now, NULL) < 0) { audiod_status_dump(); last_status_dump = *now; } - if (!FD_ISSET(ct->fd, &s->rfds)) - return; - ret = handle_connect(ct->fd); + ret = handle_connect(ct->fd, &s->rfds); if (ret < 0) PARA_ERROR_LOG("%s\n", para_strerror(-ret)); + audiod_status_dump(); } static void init_command_task(struct command_task *ct) @@ -931,12 +1035,14 @@ static void close_stat_pipe(void) { if (!stat_task->ct) return; + btr_free_node(stat_task->ct->btrn); client_close(stat_task->ct); stat_task->ct = NULL; clear_and_dump_items(); stat_task->length_seconds = 0; stat_task->offset_seconds = 0; stat_task->vss_status = 0; + stat_task->current_audio_format_num = -1; audiod_status_dump(); } @@ -971,19 +1077,27 @@ static void set_stat_task_restart_barrier(unsigned seconds) static void try_to_close_slot(int slot_num) { struct slot_info *s = &slot[slot_num]; + struct audio_format_info *a = afi + s->format; + int i; if (s->format < 0) return; - if (s->receiver_node && s->receiver_node->task.error != -E_TASK_UNREGISTERED) - return; - if (s->fc && s->fc->task.error != -E_TASK_UNREGISTERED) - return; - if (s->wng && s->wng->task.error != -E_TASK_UNREGISTERED) + if (s->receiver_node && s->receiver_node->task.error >= 0) return; + for (i = 0; i < a->num_filters; i++) + if (s->fns && s->fns[i].task.error >= 0) + return; + if (a->num_writers > 0) { + for (i = 0; i < a->num_writers; i++) + if (s->wns && s->wns[i].task.error >= 0) + return; + } else { + if (s->wns && s->wns[0].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_writers(s); + close_filters(s); close_receiver(slot_num); clear_slot(slot_num); } @@ -992,54 +1106,63 @@ static void try_to_close_slot(int slot_num) * Check if any receivers/filters/writers need to be started and do so if * necessary. */ -static void start_stop_decoders(struct sched *s) +static void start_stop_decoders(void) { - int i; + int i, ret; + struct slot_info *sl; + struct audio_format_info *a; FOR_EACH_SLOT(i) try_to_close_slot(i); if (audiod_status != AUDIOD_ON || !(stat_task->vss_status & VSS_STATUS_FLAG_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; - } + if (!must_start_decoder()) + return; + ret = open_receiver(stat_task->current_audio_format_num); + if (ret < 0) { + PARA_ERROR_LOG("%s\n", para_strerror(-ret)); + return; } + sl = slot + ret; + a = afi + sl->format; + if (a->num_filters) + open_filters(sl); + open_writers(sl); + activate_grab_clients(); + btr_log_tree(sl->receiver_node->btrn, LL_NOTICE); } +static void status_pre_select(struct sched *s, struct task *t) +{ + struct status_task *st = container_of(t, struct status_task, task); + int ret, cafn = stat_task->current_audio_format_num; + + if (must_start_decoder()) + goto min_delay; + ret = btr_node_status(st->btrn, 0, BTR_NT_LEAF); + if (ret > 0) + goto min_delay; + if (st->ct && audiod_status == AUDIOD_OFF) + goto min_delay; + if (!st->ct && audiod_status != AUDIOD_OFF) + sched_request_barrier_or_min_delay(&st->restart_barrier, s); + if (cafn >= 0) + sched_request_barrier(&afi[cafn].restart_barrier, s); + /* + * If para_server is playing we'd like to have a smooth time display + * even if we are running in standby mode. So we request a timeout that + * expires at the next full second. + */ + if (stat_task->vss_status & VSS_STATUS_FLAG_PLAYING) + sched_request_timeout_ms(1000 - now->tv_usec / 1000, s); + return; +min_delay: + sched_min_delay(s); +} /* restart the client task if necessary */ -static void status_pre_select(struct sched *s, struct task *t) +static void status_post_select(__a_unused struct sched *s, struct task *t) { struct status_task *st = container_of(t, struct status_task, task); @@ -1047,56 +1170,67 @@ static void status_pre_select(struct sched *s, struct task *t) if (!st->ct) goto out; if (st->ct->task.error >= 0) { - st->ct->task.error = -E_AUDIOD_OFF; + kill_btrn(st->ct->btrn, &st->ct->task, -E_AUDIOD_OFF); goto out; } - if (st->ct->task.error != -E_TASK_UNREGISTERED) + if (st->ct->task.error >= 0) goto out; close_stat_pipe(); st->clock_diff_count = conf.clock_diff_count_arg; goto out; } if (st->ct) { + char *buf; + size_t sz; int ret; if (st->ct->task.error < 0) { - if (st->ct->task.error != -E_TASK_UNREGISTERED) + if (st->ct->task.error >= 0) goto out; close_stat_pipe(); goto out; } if (st->ct->status != CL_RECEIVING) goto out; - ret = for_each_stat_item(st->ct->buf, st->ct->loaded, - update_item); - if (ret < 0) { - st->ct->task.error = ret; - goto out; - } - if (st->ct->loaded != ret) { - st->last_status_read = *now; - st->ct->loaded = ret; - } else { + ret = btr_node_status(st->btrn, st->min_iqs, BTR_NT_LEAF); + if (ret <= 0) { struct timeval diff; tv_diff(now, &st->last_status_read, &diff); if (diff.tv_sec > 61) - st->ct->task.error = -E_STATUS_TIMEOUT; + kill_btrn(st->ct->btrn, &st->ct->task, + -E_STATUS_TIMEOUT); + goto out; } + btr_merge(st->btrn, st->min_iqs); + sz = btr_next_buffer(st->btrn, &buf); + ret = for_each_stat_item(buf, sz, update_item); + if (ret < 0) { + kill_btrn(st->ct->btrn, &st->ct->task, ret); + goto out; + } + if (sz != ret) { + btr_consume(st->btrn, sz - ret); + st->last_status_read = *now; + st->min_iqs = 0; + } else /* current status item crosses buffers */ + st->min_iqs = sz + 1; goto out; } + btr_drain(st->btrn); + st->current_audio_format_num = -1; 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", "-p", "1", NULL}; + char *argv[] = {"audiod", "--", "stat", "-p", "-n=1", NULL}; int argc = 5; PARA_INFO_LOG("clock diff count: %d\n", st->clock_diff_count); st->clock_diff_count--; - client_open(argc, argv, &st->ct, NULL); + client_open(argc, argv, &st->ct, NULL, NULL, st->btrn); set_stat_task_restart_barrier(2); } else { char *argv[] = {"audiod", "--", "stat", "-p", NULL}; int argc = 4; - client_open(argc, argv, &st->ct, NULL); + client_open(argc, argv, &st->ct, NULL, NULL, st->btrn); set_stat_task_restart_barrier(5); } free(stat_item_values[SI_BASENAME]); @@ -1105,17 +1239,20 @@ static void status_pre_select(struct sched *s, struct task *t) stat_client_write_item(SI_BASENAME); st->last_status_read = *now; out: - start_stop_decoders(s); + start_stop_decoders(); } 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; - sprintf(st->task.status, "status task"); + sprintf(st->task.status, "stat"); + st->btrn = btr_new_node(&(struct btr_node_description) + EMBRACE(.name = "stat")); } static void set_initial_status(void) @@ -1154,17 +1291,14 @@ __noreturn static void print_help_and_die(void) static void init_colors_or_die(void) { - int ret, i; + int i; if (!want_colors()) return; daemon_set_default_log_colors(); daemon_set_flag(DF_COLOR_LOG); - for (i = 0; i < conf.log_color_given; i++) { - ret = daemon_set_log_color(conf.log_color_arg[i]); - if (ret < 0) - exit(EXIT_FAILURE); - } + for (i = 0; i < conf.log_color_given; i++) + daemon_set_log_color_or_die(conf.log_color_arg[i]); } /** @@ -1207,6 +1341,8 @@ int main(int argc, char *argv[]) daemon_set_flag(DF_LOG_TIME); daemon_set_flag(DF_LOG_HOSTNAME); daemon_set_flag(DF_LOG_LL); + if (conf.log_timing_given) + daemon_set_flag(DF_LOG_TIMING); if (conf.logfile_given) { daemon_set_logfile(conf.logfile_arg); daemon_open_log_or_die(); @@ -1221,7 +1357,6 @@ int main(int argc, char *argv[]) set_initial_status(); FOR_EACH_SLOT(i) clear_slot(i); - init_grabbing(); setup_signal_handling(); signal_setup_default(sig_task); @@ -1234,7 +1369,7 @@ int main(int argc, char *argv[]) register_task(&sig_task->task); register_task(&cmd_task->task); register_task(&stat_task->task); - s.default_timeout.tv_sec = 0; + s.default_timeout.tv_sec = 2; s.default_timeout.tv_usec = 999 * 1000; ret = schedule(&s);