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.
* */
struct slot_info slot[MAX_STREAM_SLOTS];
* */
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).
*
/**
* The task for obtaining para_server's status (para_client stat).
*
struct timeval sa_time_diff;
/** Whether client time is ahead of server time. */
int sa_time_diff_sign;
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. */
/** Number of times the clock difference is to be checked. */
unsigned clock_diff_count;
/** When to start the next check for clock difference. */
int total = 0, use_server_time = 1,
length_seconds = stat_task->length_seconds;
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]);
if (length_seconds)
return NULL;
return make_message("%s:\n", status_item_list[SI_PLAY_TIME]);
+/* return: 0: Not running, 1: Running, -1: Running but eof (or error) */
static int receiver_running(int format)
{
static int receiver_running(int format)
{
FOR_EACH_SLOT(i) {
struct slot_info *s = &slot[i];
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)
}
static void open_current_receiver(struct sched *s)
{
struct timeval diff;
}
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 (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)
+ 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) {
if (tv_diff(now, &afi[cafn].restart_barrier, &diff) < 0) {
s->timeout = diff;
return;
}
s->timeout = diff;
return;
}
+ /* start a new receiver */
stat_item_values[itemnum] = para_strdup(line);
ilen = strlen(status_item_list[itemnum]);
switch (itemnum) {
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);
break;
case SI_OFFSET:
stat_task->offset_seconds = atoi(line + ilen + 1);
dump_empty_status();
stat_task->length_seconds = 0;
stat_task->offset_seconds = 0;
dump_empty_status();
stat_task->length_seconds = 0;
stat_task->offset_seconds = 0;
+ stat_task->vss_status = 0;
- stat_task->playing = 0;
FOR_EACH_SLOT(i)
try_to_close_slot(i);
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) {
return kill_all_decoders(-E_NOT_PLAYING);
open_current_receiver(s);
FOR_EACH_SLOT(i) {
static void vss_eof(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)
if (!vsst->map)
return;
if (mmd->new_vss_status_flags & VSS_NOMORE)
senders[i].shutdown_clients();
list_for_each_entry_safe(fc, tmp, &fec_client_list, node)
fc->first_stream_chunk = -1;
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);
}
if (vss_next())
vss_eof(vsst);