+ FOR_EACH_SLOT(i) {
+ struct slot_info *s = &slot[i];
+ struct timeval wstime;
+ if (!s->wns || !s->wns[0].btrn)
+ continue;
+ btr_get_node_start(s->wns[0].btrn, &wstime);
+ if (oldest_slot >= 0 && tv_diff(&wstime, &oldest_wstime, NULL) > 0)
+ continue;
+ oldest_wstime = wstime;
+ oldest_slot = i;
+ }
+ return oldest_slot;
+}
+
+/**
+ * Compute the play time based on information of the current 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 current slot.
+ *
+ * It has to take into account that the stream was probably 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.
+ *
+ * If no writer is active, for example because 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(void)
+{
+ int ret, seconds = 0, length = stat_task->length_seconds;
+ struct timeval *tmp, sum, sss, /* server stream start */
+ rstime, /* receiver start time */
+ wstime, /* writer start time */
+ wtime, /* now - writer start */
+ rskip; /* receiver start - sss */
+ int slot_num = get_play_time_slot_num();
+ struct slot_info *s = slot_num < 0? NULL : &slot[slot_num];
+ char *msg;
+
+ if (audiod_status == AUDIOD_OFF)
+ goto empty;
+ if (stat_task->server_stream_start.tv_sec == 0) {
+ if (stat_task->vss_status & VSS_STATUS_FLAG_PLAYING)
+ goto out; /* server is about to change file */
+ if (length > 0) /* paused */
+ return NULL;
+ goto empty; /* stopped */
+ }
+ /*
+ * 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.
+ */
+ tmp = &stat_task->server_stream_start;
+ if (s && s->wns && s->wns[0].btrn) { /* 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 || !s->wns || !s->wns[0].btrn || wstime.tv_sec == 0) {
+ struct timeval diff;
+ tv_diff(now, &sss, &diff);
+ seconds = diff.tv_sec + stat_task->offset_seconds;
+ goto out;
+ }
+ tv_diff(now, &wstime, &wtime);
+ //PARA_CRIT_LOG("offset %d\n", s->offset_seconds);
+ seconds = s->offset_seconds;
+ if (s->receiver_node->btrn) {
+ btr_get_node_start(s->receiver_node->btrn, &rstime);
+ ret = tv_diff(&rstime, &sss, &rskip);
+ if (ret > 0 && rskip.tv_sec > 2) {
+ /* audiod was started in the middle of the stream */
+ tv_add(&wtime, &rskip, &sum);
+ seconds += sum.tv_sec;
+ } else
+ seconds += wtime.tv_sec;
+ } else
+ seconds += wtime.tv_sec;
+out:
+ seconds = PARA_MIN(seconds, length);
+ seconds = PARA_MAX(seconds, 0);
+ msg = make_message(
+ "%s%d:%02d [%d:%02d] (%d%%/%d:%02d)",
+ s? "" : "~",
+ seconds / 60,
+ seconds % 60,
+ (length - seconds) / 60,
+ (length - seconds) % 60,
+ length? (seconds * 100 + length / 2) / length : 0,
+ length / 60,
+ length % 60
+ );
+ //PARA_DEBUG_LOG("slot %d: %s\n", slot_num, msg);
+ return msg;
+empty:
+ return para_strdup(NULL);
+}
+
+static void parse_config_or_die(void)
+{
+ int ret, i;
+ char *config_file;
+ struct audiod_cmdline_parser_params params = {
+ .override = 0,
+ .initialize = 0,
+ .check_required = 1,
+ .check_ambiguity = 0,
+ .print_errors = 1
+ };
+
+ if (conf.config_file_given)
+ config_file = para_strdup(conf.config_file_arg);
+ else {