audiod: Improve receiver restart logic.
authorAndre Noll <maan@systemlinux.org>
Sun, 15 Mar 2009 14:29:12 +0000 (15:29 +0100)
committerAndre Noll <maan@systemlinux.org>
Sun, 15 Mar 2009 14:29:12 +0000 (15:29 +0100)
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
vss.c

index d771fa6..3f46acb 100644 (file)
--- 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 e1a3c37..7b36ae3 100644 (file)
--- 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);