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 d771fa68b719edd33b228f94eec0d6b542bbb7be..3f46acbcee062f71bd5a5d7e582262a5f26117ac 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 e1a3c3742a76b66c614e6aaf65f04c7a31277309..7b36ae31c95a14ca24d87d7f3f9c27d29de6fa5c 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);