FEC: Improve and simplify group timing.
authorAndre Noll <maan@systemlinux.org>
Fri, 27 Nov 2009 19:36:23 +0000 (20:36 +0100)
committerAndre Noll <maan@systemlinux.org>
Fri, 27 Nov 2009 19:36:23 +0000 (20:36 +0100)
The computation of the group start time was not as accurate as it
could be because it did not take into account that the first FEC
group has to be taken into account twice. This could lead to buffer
underruns between the first and the second group.

This patch fixes this flaw by computing the group start start(n)
of the nth FEC group as

start(n) = start(n - 1) + duration(n - 1),

which is not only more accurate but also a bit simpler than what we
had before. In order to do so, we have to remember the duration of
the previous group. The new ->duration member of struct fec_group is
used for this purpose.

The patch also renames set_slice_duration() to set_group_timing() as
this function now computes and stores both the slice duration and the
group duration.

vss.c

diff --git a/vss.c b/vss.c
index 6db42bd..c6e4053 100644 (file)
--- a/vss.c
+++ b/vss.c
@@ -121,6 +121,8 @@ struct fec_group {
        uint32_t num_chunks;
        /** When the first chunk was sent. */
        struct timeval start;
+       /** The duration of the full group. */
+       struct timeval duration;
        /** The group duration divided by the number of slices. */
        struct timeval slice_duration;
        /** Group contains the audio file header that occupies that many slices. */
@@ -232,15 +234,16 @@ 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)
+/* set group start and group duration */
+static void set_group_timing(struct fec_client *fc, struct fec_group *g)
 {
-       struct timeval group_duration, *chunk_tv = vss_chunk_time();
+       struct timeval *chunk_tv = vss_chunk_time();
 
-       tv_scale(g->num_chunks, chunk_tv, &group_duration);
+       tv_scale(g->num_chunks, chunk_tv, &g->duration);
        tv_divide(fc->fcp->slices_per_group + fc->num_extra_slices,
-               &group_duration, &g->slice_duration);
+               &g->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));
+               tv2ms(&g->duration), tv2ms(chunk_tv), tv2ms(&g->slice_duration));
 }
 
 static int setup_next_fec_group(struct fec_client *fc, struct vss_task *vsst)
@@ -248,12 +251,10 @@ static int setup_next_fec_group(struct fec_client *fc, struct vss_task *vsst)
        int ret, i, k, data_slices;
        size_t len;
        const char *buf, *start_buf;
-       struct timeval tmp, *chunk_tv = vss_chunk_time();
        struct fec_group *g = &fc->group;
        unsigned slice_bytes = fc->fcp->max_slice_bytes - FEC_HEADER_SIZE;
        uint32_t max_data_size;
 
-       assert(chunk_tv);
        if (fc->first_stream_chunk < 0) {
                uint8_t hs, ds; /* needed header/data slices */
                uint8_t rs = fc->fcp->slices_per_group
@@ -285,10 +286,16 @@ static int setup_next_fec_group(struct fec_client *fc, struct vss_task *vsst)
                fc->first_stream_chunk = mmd->current_chunk;
                g->first_chunk = mmd->current_chunk;
                g->num = 0;
+               g->start = *now;
        } else {
+               /*
+                * Start and duration of this group depend only on the previous
+                * group. Compute the new group start as g->start += g->duration.
+                */
+               struct timeval tmp = g->start;
+               tv_add(&tmp, &g->duration, &g->start);
                k = fc->fcp->data_slices_per_group + fc->num_extra_slices;
-               /* use duration of the previous group for the timing of this group */
-               set_slice_duration(fc, g);
+               set_group_timing(fc, g);
                g->first_chunk += g->num_chunks;
                g->num++;
        }
@@ -316,7 +323,7 @@ static int setup_next_fec_group(struct fec_client *fc, struct vss_task *vsst)
        assert(g->num_chunks);
        fc->current_slice_num = 0;
        if (g->num == 0)
-               set_slice_duration(fc, g);
+               set_group_timing(fc, g);
 
        /* setup header slices */
        buf = vsst->header_buf;
@@ -354,14 +361,6 @@ static int setup_next_fec_group(struct fec_client *fc, struct vss_task *vsst)
                g->first_chunk + g->num_chunks - 1,
                g->num_header_slices, data_slices
        );
-       /* set group start */
-       if (g->num != 0 && vsst->header_len != 0 && fc->first_stream_chunk == 0)
-               /* chunk #0 is the audio file header */
-               tv_scale(g->first_chunk - 1, chunk_tv, &tmp);
-       else
-               tv_scale(g->first_chunk - fc->first_stream_chunk,
-                       chunk_tv, &tmp);
-       tv_add(&fc->stream_start, &tmp, &g->start);
        return 1;
 }