X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=blobdiff_plain;f=fecdec_filter.c;h=8cb17ad642098e81621e2be6f6c0643ac6de4038;hp=5c01a0395e79139a0bbd09633f127b06321d5268;hb=58151b60c9782a52aac5d39ace7e69c3635ee260;hpb=2b3cc3af64d5574473c2f1ea9c32d7589abbb811 diff --git a/fecdec_filter.c b/fecdec_filter.c index 5c01a039..8cb17ad6 100644 --- a/fecdec_filter.c +++ b/fecdec_filter.c @@ -4,7 +4,7 @@ * Licensed under the GPL v2. For licencing details see COPYING. */ -/** \file fecdec_filter.c A filter fec-decodes an audio stream. */ +/** \file fecdec_filter.c A filter that fec-decodes an audio stream. */ #include #include "para.h" @@ -27,8 +27,10 @@ */ #define NUM_FEC_GROUPS 3 -/** Size of the output buffer of the fecdec filter. */ -#define FECDEC_OUTBUF_SIZE (128 * 1024) +/** Default size of the output buffer of the fecdec filter. */ +#define FECDEC_DEFAULT_OUTBUF_SIZE (3 * 1024) +/** Maximal size of the output buffer of the fecdec filter. */ +#define FECDEC_MAX_OUTBUF_SIZE (1024 * 1024) /** Data read from the header of a slice. */ struct fec_header { @@ -46,6 +48,10 @@ struct fec_header { uint8_t slice_num; /** Used data bytes of this slice. */ uint16_t slice_bytes; + /** Non-zero if this group is the beginning of the stream. */ + uint8_t bos; + /** Non-zero if this stream embedds audio headers into fec groups. */ + uint8_t header_stream; }; /** @@ -72,11 +78,15 @@ struct private_fecdec_data { struct fec_parms *fec; /** Keeps track of what was received so far. */ struct fecdec_group groups[NUM_FEC_GROUPS]; + /** Whether an audio file header was already received. */ + int have_header; + /** Points to the first received group. */ + struct fecdec_group *first_complete_group; }; /** Iterate over all fecdec groups. */ #define FOR_EACH_FECDEC_GROUP(g, d) for (g = (d)->groups; \ - (g) - (d)->groups < NUM_FEC_GROUPS; (g)++) + (g) < (d)->groups + NUM_FEC_GROUPS; (g)++) static int group_complete(struct fecdec_group *fg) { @@ -98,7 +108,9 @@ static void clear_group(struct fecdec_group *fg) fg->idx[i] = -1; } free(fg->data); + fg->data = NULL; free(fg->idx); + fg->idx = NULL; fg->num_slices = 0; memset(&fg->h, 0, sizeof(struct fec_header)); fg->num_received_slices = 0; @@ -112,10 +124,15 @@ static int find_group(struct fec_header *h, FOR_EACH_FECDEC_GROUP(fg, pfd) { if (fg->h.group_num != h->group_num) continue; + if (fg->num_received_slices == 0) + goto success; if (fg->h.slices_per_group != h->slices_per_group) - continue; + return -E_BAD_FEC_HEADER; if (fg->h.data_slices_per_group != h->data_slices_per_group) - continue; + return -E_BAD_FEC_HEADER; + if (fg->h.group_bytes != h->group_bytes) + return -E_BAD_FEC_HEADER; +success: *result = fg; return 1; } @@ -140,6 +157,12 @@ static struct fecdec_group *try_to_free_group(struct private_fecdec_data *pfd) FOR_EACH_FECDEC_GROUP(fg, pfd) { if (!group_complete(fg)) continue; + /* + * Don't clear the first complete group if it has not yet been + * decoded. + */ + if (fg == pfd->first_complete_group) + continue; clear_group(fg); return fg; } @@ -156,12 +179,15 @@ static struct fecdec_group *free_oldest_group(struct private_fecdec_data *pfd) } if (!group_complete(oldest) && !group_empty(oldest)) PARA_WARNING_LOG("Clearing incomplete group %d " - "(contains %d slices)\n", fg->h.group_num, - fg->num_received_slices); + "(contains %d slices)\n", oldest->h.group_num, + oldest->num_received_slices); + if (oldest == pfd->first_complete_group) + pfd->first_complete_group = NULL; clear_group(oldest); return oldest; } +/* returns 1 if the group was found, 0 if not, negative on errors */ static int get_group(struct fec_header *h, struct private_fecdec_data *pfd, struct fecdec_group **result) { @@ -180,51 +206,122 @@ static int get_group(struct fec_header *h, struct private_fecdec_data *pfd, if (fg) goto success; fg = free_oldest_group(pfd); + ret = 0; success: fg->h = *h; *result = fg; - return 1; + return ret; } +/* + * returns 1 if slice was added, zero otherwise (because the group was already + * complete). In any case the number of received slices is being increased by + * one. + */ static int add_slice(char *buf, struct fecdec_group *fg) { int r, slice_num; - if (group_complete(fg)) + if (group_complete(fg)) { + PARA_DEBUG_LOG("group %d complete, ignoring slice %d\n", + fg->h.group_num, fg->h.slice_num); + fg->num_received_slices++; return 0; + } slice_num = fg->h.slice_num; if (fg->num_slices == 0) { fg->num_slices = fg->h.slices_per_group; - fg->idx = malloc(fg->num_slices * sizeof(int)); - fg->data = malloc(fg->num_slices * sizeof(unsigned char *)); + fg->idx = para_malloc(fg->num_slices * sizeof(int)); + fg->data = para_malloc(fg->num_slices * sizeof(unsigned char *)); memset(fg->data, 0, fg->num_slices * sizeof(unsigned char *)); } r = fg->num_received_slices; fg->idx[r] = slice_num; - fg->data[r] = malloc(fg->h.slice_bytes); + fg->data[r] = para_malloc(fg->h.slice_bytes); memcpy(fg->data[r], buf, fg->h.slice_bytes); fg->num_received_slices++; return 1; } +enum fec_group_usability { + FEC_GROUP_UNUSABLE, + FEC_GROUP_USABLE, + FEC_GROUP_USABLE_SKIP_HEADER, + FEC_GROUP_USABLE_WITH_HEADER +}; + +static enum fec_group_usability group_is_usable(struct fecdec_group *fg, + struct private_fecdec_data *pfd) +{ + struct fec_header *h = &fg->h; + + if (!h->header_stream) + return FEC_GROUP_USABLE; + if (pfd->have_header) { + if (h->audio_header_size) + return FEC_GROUP_USABLE_SKIP_HEADER; + return FEC_GROUP_USABLE; + } + if (fg->h.bos) + return FEC_GROUP_USABLE; + if (fg->h.audio_header_size) + return FEC_GROUP_USABLE_WITH_HEADER; + return FEC_GROUP_UNUSABLE; +} + static int decode_group(struct fecdec_group *fg, struct filter_node *fn) { int i, ret, sb = fg->h.slice_bytes; - size_t written = 0; + size_t written, need; struct private_fecdec_data *pfd = fn->private_data; + enum fec_group_usability u = group_is_usable(fg, pfd); + if (u == FEC_GROUP_UNUSABLE) { + PARA_INFO_LOG("dropping unusable group %d\n", fg->h.group_num); + return 0; + } + PARA_DEBUG_LOG("decoding group %d (%d slices)\n", fg->h.group_num, + fg->h.data_slices_per_group); ret = fec_decode(pfd->fec, fg->data, fg->idx, sb); if (ret < 0) return ret; + pfd->have_header = 1; + i = 0; + if (u == FEC_GROUP_USABLE_SKIP_HEADER) { + i = ROUND_UP(fg->h.audio_header_size, fg->h.slice_bytes) + / fg->h.slice_bytes; + PARA_DEBUG_LOG("skipping %d header slices\n", i); + } PARA_DEBUG_LOG("writing group %d (%d/%d decoded data bytes)\n", fg->h.group_num, fg->h.group_bytes, fg->h.data_slices_per_group * sb); - for (i = 0; i < fg->h.data_slices_per_group; i++) { + need = fn->loaded + (fg->h.data_slices_per_group - i)* sb; + if (need > fn->bufsize) { + fn->bufsize = PARA_MAX(fn->bufsize * 2, need); + if (fn->bufsize > FECDEC_MAX_OUTBUF_SIZE) + return -E_FECDEC_OVERRUN; + PARA_INFO_LOG("increasing fec buf to %zu\n", fn->bufsize); + fn->buf = para_realloc(fn->buf, fn->bufsize); + } + if (u == FEC_GROUP_USABLE_WITH_HEADER) { + PARA_INFO_LOG("writing audio file header\n"); + written = 0; + for (i = 0; i < fg->h.data_slices_per_group; i++) { + size_t n = sb; + if (written >= fg->h.audio_header_size) + break; + if (sb + written > fg->h.audio_header_size) + n = fg->h.audio_header_size - written; + memcpy(fn->buf + fn->loaded, fg->data[i], n); + fn->loaded += n; + written += n; + } + } + written = 0; + for (; i < fg->h.data_slices_per_group; i++) { size_t n = sb; if (n + written > fg->h.group_bytes) n = fg->h.group_bytes - written; - if (fn->loaded + n > fn->bufsize) - return -E_FECDEC_OVERRUN; memcpy(fn->buf + fn->loaded, fg->data[i], n); fn->loaded += n; written += n; @@ -256,18 +353,21 @@ static int read_fec_header(char *buf, size_t len, struct fec_header *h) h->slice_num = read_u8(buf + 18); h->slice_bytes = read_u16(buf + 20); - if (!h->group_bytes && & h->slice_bytes) + h->bos = read_u8(buf + 22); + h->header_stream = read_u8(buf + 23); + if (!memcmp(buf, FEC_EOF_PACKET, FEC_EOF_PACKET_LEN)) return -E_FECDEC_EOF; // PARA_DEBUG_LOG("group %u, slize %u, slices per group: %u\n", // h->group_num, h->slice_num, h->slices_per_group); return 1; } +/* returns 1 if we used the buffer, 0 if we didn't, negative on errors */ static int dispatch_slice(char *buf, size_t len, struct fec_header *h, struct filter_node *fn) { struct fecdec_group *fg; - int ret; + int ret, k, n; struct private_fecdec_data *pfd = fn->private_data; if (h->slice_bytes > len) /* can not use the thing, try to read more */ @@ -275,30 +375,43 @@ static int dispatch_slice(char *buf, size_t len, struct fec_header *h, ret = get_group(h, pfd, &fg); if (ret < 0) return ret; - if (group_complete(fg)) { - PARA_DEBUG_LOG("group complete, ignoring slice %d\n", - h->slice_num); + if (!add_slice(buf, fg)) /* group already complete */ + return 1; + if (!group_complete(fg)) + return 1; + /* this slice completed the group */ + if (pfd->fec) + goto decode; + /* it's either the first or the second complete group */ + if (!pfd->first_complete_group) { /* it's the first group */ + enum fec_group_usability u = group_is_usable(fg, pfd); + assert(u != FEC_GROUP_USABLE_SKIP_HEADER); + if (u == FEC_GROUP_UNUSABLE) /* forget it */ + return 1; + pfd->first_complete_group = fg; /* remember it */ return 1; } - ret = add_slice(buf, fg); + /* we have two complete groups, let's go */ + k = h->data_slices_per_group; + n = h->slices_per_group; + PARA_NOTICE_LOG("init fec (%d, %d)\n", k, n); + ret = fec_new(k, n, &pfd->fec); + if (ret < 0) + return ret; + /* decode and clear the first group */ + ret = decode_group(pfd->first_complete_group, fn); + if (ret < 0) + return ret; + clear_group(pfd->first_complete_group); + pfd->first_complete_group = NULL; +decode: + ret = decode_group(fg, fn); if (ret < 0) return ret; - if (group_complete(fg)) { - if (!pfd->fec) { - int k = h->data_slices_per_group, n = h->slices_per_group; - PARA_NOTICE_LOG("init fec (%d, %d)\n", k, n); - ret = fec_new(k, n, &pfd->fec); - if (ret < 0) - return ret; - } - ret = decode_group(fg, fn); - if (ret < 0) - return ret; - } return 1; } -static int fecdec(char *buf, size_t len, struct filter_node *fn) +static ssize_t fecdec(char *buf, size_t len, struct filter_node *fn) { int ret; struct fec_header h; @@ -306,7 +419,7 @@ static int fecdec(char *buf, size_t len, struct filter_node *fn) ret = read_fec_header(buf, len, &h); if (ret <= 0) return ret; - if (h.slice_bytes > fn->bufsize) + if (!h.slice_bytes) return -E_BAD_SLICE_SIZE; if (h.slice_num > h.slices_per_group) return -E_BAD_SLICE_NUM; @@ -327,15 +440,18 @@ static void fecdec_close(struct filter_node *fn) clear_group(fg); free(fn->buf); fn->buf = NULL; + fec_free(pfd->fec); free(fn->private_data); fn->private_data = NULL; } static void fecdec_open(struct filter_node *fn) { - fn->bufsize = FECDEC_OUTBUF_SIZE; + struct private_fecdec_data *pfd; + fn->bufsize = FECDEC_DEFAULT_OUTBUF_SIZE; fn->buf = para_malloc(fn->bufsize); - fn->private_data = para_calloc(sizeof(struct private_fecdec_data)); + pfd = para_calloc(sizeof(*pfd)); + fn->private_data = pfd; fn->loaded = 0; }