FEC timing improvements.
authorAndre Noll <maan@systemlinux.org>
Sat, 12 Sep 2009 16:16:56 +0000 (18:16 +0200)
committerAndre Noll <maan@systemlinux.org>
Sat, 12 Sep 2009 18:23:07 +0000 (20:23 +0200)
Currently we compute the time of a FEC group as the number of
containing chunks times the chunk time. The time between sending
two slices therefore depends only on the number of chunks the group
contains. Groups containing many slices are sent with larger delays
than groups containing few slices.

This approach is not optimal for the following reason: Consider a group
containing only few slices which is followed by a group containing
many slices. This happens frequently at the end of VBR MP3 files which
contain some seconds of silence or applause at the end because this
last part is often encoded at a lower bitrate than the preceding part.

In this scenario buffer underruns in the receiving application can
easily occur if the previous FEC group has been decoded and completely
fed to the writer before enough slices of the next group have arrived
to decode the second group.

This patch changes the timing of FEC groups such that all but the
first group use the duration of the _previous_ group as the basis
for the timing.

vss.c

diff --git a/vss.c b/vss.c
index 400df84..babcd24 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -232,6 +232,17 @@ static int num_slices(long unsigned bytes, struct fec_client *fc, uint8_t *resul
        return 1;
 }
 
+static void set_slice_duration(struct fec_client *fc, struct fec_group *g)
+{
+       struct timeval group_duration, *chunk_tv = vss_chunk_time();
+
+       tv_scale(g->num_chunks, chunk_tv, &group_duration);
+       tv_divide(fc->fcp->slices_per_group + fc->num_extra_slices,
+               &group_duration, &g->slice_duration);
+       PARA_DEBUG_LOG("durations (group/chunk/slice): %lu/%lu/%lu\n",
+               tv2ms(&group_duration), tv2ms(chunk_tv), tv2ms(&g->slice_duration));
+}
+
 static int setup_next_fec_group(struct fec_client *fc, struct vss_task *vsst)
 {
        int ret, i, k, data_slices;
@@ -275,6 +286,8 @@ static int setup_next_fec_group(struct fec_client *fc, struct vss_task *vsst)
                g->first_chunk = mmd->current_chunk;
                g->num = 0;
        } else {
+               /* use duration of the previous group for the timing of this group */
+               set_slice_duration(fc, g);
                g->first_chunk += g->num_chunks;
                g->num++;
        }
@@ -301,6 +314,8 @@ static int setup_next_fec_group(struct fec_client *fc, struct vss_task *vsst)
        g->num_chunks = i - g->first_chunk;
        assert(g->num_chunks);
        fc->current_slice_num = 0;
+       if (g->num == 0)
+               set_slice_duration(fc, g);
 
        /* setup header slices */
        buf = vsst->header_buf;
@@ -332,21 +347,15 @@ static int setup_next_fec_group(struct fec_client *fc, struct vss_task *vsst)
                for (; i < k; i++)
                        fc->src_data[i] = (const unsigned char *)buf;
        }
-
-       /* setup group timing */
-       tv_scale(g->first_chunk - fc->first_stream_chunk, chunk_tv, &tmp);
-       tv_add(&fc->stream_start, &tmp, &g->start);
-       tv_scale(g->num_chunks, chunk_tv, &tmp); /* group duration */
-       tv_divide(fc->fcp->slices_per_group + fc->num_extra_slices,
-               &tmp, &g->slice_duration);
-
-       PARA_DEBUG_LOG("FEC group %d: %d chunks (%d - %d), %d header slices, %d data slices\n",
+       PARA_DEBUG_LOG("FEC group %d: %d chunks (%d - %d), "
+               "%d header slices, %d data slices\n",
                g->num, g->num_chunks, g->first_chunk,
                g->first_chunk + g->num_chunks - 1,
                g->num_header_slices, data_slices
        );
-       PARA_DEBUG_LOG("durations (group/chunk/slice): %lu/%lu/%lu\n",
-               tv2ms(&tmp), tv2ms(chunk_tv), tv2ms(&g->slice_duration));
+       /* set group start */
+       tv_scale(g->first_chunk - fc->first_stream_chunk, chunk_tv, &tmp);
+       tv_add(&fc->stream_start, &tmp, &g->start);
        return 1;
 }