From 89e28a4361bd120dfe4202a16d85d03fb1139de8 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Sun, 15 Mar 2009 15:29:12 +0100 Subject: [PATCH] audiod: Improve receiver restart logic. Use the vss status flags and the stream_start time announced by para_server to decide when to start a new receiver. This is better than basing this decision only on whether the vss status equals "playing" as we did before. This allows to reliably detect when it is safe to start a new receiver while the filter/writer of the current stream are still active. The old code would simply wait until filter/writer complete which, due to buffering, might be much too late. --- audiod.c | 60 ++++++++++++++++++++++++++++++++++++++++++++------------ vss.c | 3 ++- 2 files changed, 49 insertions(+), 14 deletions(-) diff --git a/audiod.c b/audiod.c index d771fa68..3f46acbc 100644 --- a/audiod.c +++ b/audiod.c @@ -66,6 +66,14 @@ struct audio_format_info { * */ struct slot_info slot[MAX_STREAM_SLOTS]; +/** The vss status flags audiod is interested in. */ +enum vss_status_flags { + /** Whether the 'N' flag is set. */ + VSS_STATUS_FLAG_NEXT = 1, + /** The 'P' flag is set. */ + VSS_STATUS_FLAG_PLAYING = 2, +}; + /** * The task for obtaining para_server's status (para_client stat). * @@ -90,8 +98,8 @@ struct status_task { struct timeval sa_time_diff; /** Whether client time is ahead of server time. */ int sa_time_diff_sign; - /** Non-zero if para_server's status is "playing". */ - int playing; + /** The 'P' and the 'N' flags as announced by para_server. */ + enum vss_status_flags vss_status; /** Number of times the clock difference is to be checked. */ unsigned clock_diff_count; /** When to start the next check for clock difference. */ @@ -170,7 +178,7 @@ char *get_time_string(struct timeval *newest_stime) int total = 0, use_server_time = 1, length_seconds = stat_task->length_seconds; - if (!stat_task->playing) { + if (!(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING)) { if (length_seconds) return NULL; return make_message("%s:\n", status_item_list[SI_PLAY_TIME]); @@ -462,31 +470,53 @@ err: return ret; } +/* return: 0: Not running, 1: Running, -1: Running but eof (or error) */ static int receiver_running(int format) { - int i; + int i, ret = 0; FOR_EACH_SLOT(i) { struct slot_info *s = &slot[i]; - if (s->format == format && s->receiver_node) + if (s->format != format) + continue; + if (!s->receiver_node) + continue; + if (s->receiver_node->task.error >= 0) return 1; + ret = -1; } - return 0; + return ret; } static void open_current_receiver(struct sched *s) { struct timeval diff; - int cafn = stat_task->current_audio_format_num; + int ret, cafn = stat_task->current_audio_format_num; if (cafn < 0 || !stat_task->ct) return; - if (receiver_running(cafn)) + /* 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; } + /* start a new receiver */ open_receiver(cafn); } @@ -556,9 +586,12 @@ static int check_stat_line(char *line, __a_unused void *data) stat_item_values[itemnum] = para_strdup(line); ilen = strlen(status_item_list[itemnum]); switch (itemnum) { - case SI_STATUS: - stat_task->playing = strstr(line, "playing")? 1 : 0; - PARA_INFO_LOG("stat task playing: %d\n", stat_task->playing); + case SI_STATUS_FLAGS: + stat_task->vss_status = 0; + if (strchr(line, 'N')) + stat_task->vss_status |= VSS_STATUS_FLAG_NEXT; + if (strchr(line, 'P')) + stat_task->vss_status |= VSS_STATUS_FLAG_PLAYING; break; case SI_OFFSET: stat_task->offset_seconds = atoi(line + ilen + 1); @@ -899,8 +932,8 @@ static void close_stat_pipe(void) dump_empty_status(); stat_task->length_seconds = 0; stat_task->offset_seconds = 0; + stat_task->vss_status = 0; audiod_status_dump(); - stat_task->playing = 0; } /** @@ -961,7 +994,8 @@ static void start_stop_decoders(struct sched *s) FOR_EACH_SLOT(i) try_to_close_slot(i); - if (audiod_status != AUDIOD_ON || !stat_task->playing) + 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) { diff --git a/vss.c b/vss.c index e1a3c374..7b36ae31 100644 --- a/vss.c +++ b/vss.c @@ -587,7 +587,6 @@ static struct timeval *vss_compute_timeout(struct vss_task *vsst) static void vss_eof(struct vss_task *vsst) { - mmd->stream_start = *now; if (!vsst->map) return; if (mmd->new_vss_status_flags & VSS_NOMORE) @@ -671,6 +670,8 @@ static void vss_pre_select(struct sched *s, struct task *t) senders[i].shutdown_clients(); list_for_each_entry_safe(fc, tmp, &fec_client_list, node) fc->first_stream_chunk = -1; + mmd->stream_start.tv_sec = 0; + mmd->stream_start.tv_usec = 0; } if (vss_next()) vss_eof(vsst); -- 2.30.2