+ struct fec_group *g = &fc->group;
+ int k = fc->fcp->data_slices_per_group + fc->num_extra_slices;
+ int n = fc->fcp->slices_per_group + fc->num_extra_slices;
+ int ret, k1, k2, h, d, min, max, sum;
+ int max_slice_bytes = fc->mps - FEC_HEADER_SIZE;
+ int max_group_bytes;
+
+ if (!need_audio_header(fc, vsst)) {
+ max_group_bytes = k * max_slice_bytes;
+ g->num_header_slices = 0;
+ compute_group_size(vsst, g, max_group_bytes);
+ g->slice_bytes = DIV_ROUND_UP(g->bytes, k);
+ if (g->slice_bytes == 0)
+ g->slice_bytes = 1;
+ return 1;
+ }
+ if (!need_data_slices(fc, vsst)) {
+ g->bytes = 0;
+ g->num_chunks = 0;
+ g->slice_bytes = DIV_ROUND_UP(vsst->header_len, k);
+ g->num_header_slices = k;
+ return 1;
+ }
+ h = vsst->header_len;
+ max_group_bytes = (k - num_slices(h, max_slice_bytes, n - k))
+ * max_slice_bytes;
+ compute_group_size(vsst, g, max_group_bytes);
+ d = g->bytes;
+ if (d == 0) {
+ g->slice_bytes = DIV_ROUND_UP(h, k);
+ ret = num_slices(vsst->header_len, g->slice_bytes, n - k);
+ if (ret < 0)
+ return ret;
+ g->num_header_slices = ret;
+ return 1;
+ }
+ min = PARA_MIN(h, d);
+ max = PARA_MAX(h, d);
+ sum = h + d;
+ k1 = DIV_ROUND_UP(k * min, sum);
+ k2 = k - k1;
+ assert(k1 > 0);
+ assert(k2 > 0);
+
+ g->slice_bytes = PARA_MAX(DIV_ROUND_UP(min, k1), DIV_ROUND_UP(max, k2));
+ /*
+ * This value of s := g->slice_bytes satisfies inequality (*) above,
+ * but it might be larger than max_slice_bytes. However, we know that
+ * max_slice_bytes are sufficient to store header and data, so:
+ */
+ g->slice_bytes = PARA_MIN((int)g->slice_bytes, max_slice_bytes);
+
+ ret = num_slices(vsst->header_len, g->slice_bytes, n - k);
+ if (ret < 0)
+ return ret;
+ g->num_header_slices = ret;
+ return 1;
+}
+
+static int setup_next_fec_group(struct fec_client *fc, struct vss_task *vsst)
+{
+ int ret, i, k, n, data_slices;
+ size_t len;
+ char *buf;
+ struct fec_group *g = &fc->group;
+
+ if (fc->state == FEC_STATE_NONE) {
+ ret = initialize_fec_client(fc, vsst);
+ if (ret < 0)
+ return ret;
+ g->first_chunk = mmd->current_chunk;
+ g->num = 0;
+ g->start = *now;
+ } else {
+ struct timeval tmp;
+ if (g->first_chunk + g->num_chunks >= mmd->afd.afhi.chunks_total)
+ return 0;
+ /*
+ * Start and duration of this group depend only on the previous
+ * group. Compute the new group start as g->start += g->duration.
+ */
+ tmp = g->start;
+ tv_add(&tmp, &g->duration, &g->start);
+ set_group_timing(fc, vsst);
+ g->first_chunk += g->num_chunks;
+ g->num++;
+ }
+ k = fc->fcp->data_slices_per_group + fc->num_extra_slices;
+ n = fc->fcp->slices_per_group + fc->num_extra_slices;
+
+ compute_slice_size(fc, vsst);
+ assert(g->slice_bytes > 0);
+ ret = num_slices(g->bytes, g->slice_bytes, n - k);
+ if (ret < 0)
+ return ret;
+ data_slices = ret;
+ assert(g->num_header_slices + data_slices <= k);
+ fc->current_slice_num = 0;
+ if (g->num == 0)
+ set_group_timing(fc, vsst);
+ /* setup header slices */
+ buf = vsst->header_buf;
+ for (i = 0; i < g->num_header_slices; i++) {
+ uint32_t payload_size;
+ if (buf + g->slice_bytes <= vsst->header_buf + vsst->header_len) {
+ fc->src_data[i] = (const unsigned char *)buf;
+ buf += g->slice_bytes;
+ continue;
+ }
+ /*
+ * Can not use vss->header_buf for this slice as it
+ * goes beyond the buffer. This slice will not be fully
+ * used.
+ */
+ payload_size = vsst->header_buf + vsst->header_len - buf;
+ memcpy(fc->extra_header_buf, buf, payload_size);
+ if (payload_size < g->slice_bytes)
+ memset(fc->extra_header_buf + payload_size, 0,
+ g->slice_bytes - payload_size);
+ fc->src_data[i] = fc->extra_header_buf;
+ assert(i == g->num_header_slices - 1);
+ }
+
+ /* setup data slices */
+ vss_get_chunk(g->first_chunk, vsst, &buf, &len);
+ for (; i < g->num_header_slices + data_slices; i++) {
+ if (buf + g->slice_bytes > vsst->map + mmd->size) {
+ /*
+ * Can not use the memory mapped audio file for this
+ * slice as it goes beyond the map.
+ */
+ uint32_t payload_size = vsst->map + mmd->size - buf;
+ memcpy(fc->extra_src_buf, buf, payload_size);
+ if (payload_size < g->slice_bytes)
+ memset(fc->extra_src_buf + payload_size, 0,
+ g->slice_bytes - payload_size);
+ fc->src_data[i] = fc->extra_src_buf;
+ i++;
+ break;
+ }
+ fc->src_data[i] = (const unsigned char *)buf;
+ buf += g->slice_bytes;
+ }
+ if (i < k) {
+ /* use arbitrary data for all remaining slices */
+ buf = vsst->map;
+ for (; i < k; i++)
+ fc->src_data[i] = (const unsigned char *)buf;
+ }
+ PARA_DEBUG_LOG("FEC group %d: %d chunks (%d - %d), %d bytes\n",
+ g->num, g->num_chunks, g->first_chunk,
+ g->first_chunk + g->num_chunks - 1, g->bytes
+ );
+ PARA_DEBUG_LOG("slice_bytes: %d, %d header slices, %d data slices\n",
+ g->slice_bytes, g->num_header_slices, data_slices
+ );
+ return 1;
+}
+
+static int compute_next_fec_slice(struct fec_client *fc, struct vss_task *vsst)
+{
+ if (fc->state == FEC_STATE_NONE || fc->current_slice_num
+ == fc->fcp->slices_per_group + fc->num_extra_slices) {
+ int ret = setup_next_fec_group(fc, vsst);
+ if (ret == 0)
+ return 0;
+ if (ret < 0) {
+ PARA_ERROR_LOG("%s\n", para_strerror(-ret));
+ PARA_ERROR_LOG("FEC client temporarily disabled\n");
+ fc->state = FEC_STATE_DISABLED;
+ return ret;
+ }
+ }
+ write_fec_header(fc, vsst);
+ fec_encode(fc->parms, fc->src_data, fc->enc_buf + FEC_HEADER_SIZE,
+ fc->current_slice_num, fc->group.slice_bytes);
+ return 1;