Merge remote branch 'athcx/maint' into maint
authorAndre Noll <maan@systemlinux.org>
Sun, 17 Jan 2010 20:32:11 +0000 (21:32 +0100)
committerAndre Noll <maan@systemlinux.org>
Sun, 17 Jan 2010 20:32:11 +0000 (21:32 +0100)
22 files changed:
Makefile.in
NEWS
afh_common.c
alsa_write.c
audiod.c
blob.c
configure.ac
daemon.c
fecdec_filter.c
http_send.c
oss_write.c
osx_write.c
send_common.c
server.c
udp_send.c
user_list.c
versions/paraslash-0.3.6.tar.bz2 [new file with mode: 0644]
versions/paraslash-0.3.6.tar.bz2.asc [new file with mode: 0644]
vss.c
web/index.in.html
write.h
write_common.c

index b4392a3..d04d0ea 100644 (file)
@@ -13,7 +13,7 @@ build_date := $(shell date)
 uname_s := $(shell uname -s 2>/dev/null || echo "UNKNOWN_OS")
 uname_rs := $(shell uname -rs)
 cc_version := $(shell $(CC) --version | head -n 1)
-codename := symplectic separability
+codename := imaginary radiation
 
 DEBUG_CPPFLAGS += -Wno-sign-compare -g -Wunused -Wundef -W
 DEBUG_CPPFLAGS += -Wredundant-decls
diff --git a/NEWS b/NEWS
index 52d2b7c..66ce1f3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,24 @@
 NEWS
 ====
 
+-------------------------------------
+0.3.6 (2009-12-07) "cubic continuity"
+-------------------------------------
+
+Quite a few bugs have been found and fixed since 0.3.5, so here's
+another 0.3.x release. No new features.
+
+       - Always check return value of malloc().
+       - ogg vorbis/FEC: Do not write garbage after the audio file header.
+       - exit if root privileges could not be dropped.
+       - FEC: Fix computation of extra slices.
+       - oss: Fix check for empty input buffer.
+       - Avoid buffer underruns due to filter chain output buffer constraints.
+       - server: Fix assignment of afs_pid.
+       - Don't panic if the afs database contains unknown audio formats.
+       - http/dccp: Do not send the audio file header twice.
+       - FEC: Timing improvements.
+
 --------------------------------------------
 0.3.5 (2009-09-21) "symplectic separability"
 --------------------------------------------
index 8c0eed2..7d4ab08 100644 (file)
@@ -208,9 +208,9 @@ int compute_afhi(const char *path, char *data, size_t size, int fd,
  */
 const char *audio_format_name(int i)
 {
-       //PARA_NOTICE_LOG("array size: %u¸ requested: %d\n", ARRAY_SIZE(afl), i);
-       assert(i < 0 || i < ARRAY_SIZE(afl) - 1);
-       return i >= 0?  afl[i].name : "(none)";
+       if (i < 0 || i >= ARRAY_SIZE(afl) - 1)
+               return "???";
+       return afl[i].name;
 }
 
 /**
index d782894..53c9738 100644 (file)
@@ -79,18 +79,19 @@ static int alsa_init(struct private_alsa_write_data *pad,
                        pad->channels) < 0)
                return -E_CHANNEL_COUNT;
        if (snd_pcm_hw_params_set_rate_near(pad->handle, hwparams,
-                       &pad->samplerate, 0) < 0)
+                       &pad->samplerate, NULL) < 0)
                return -E_SET_RATE;
-       err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &pad->buffer_time, 0);
+       err = snd_pcm_hw_params_get_buffer_time_max(hwparams,
+               &pad->buffer_time, NULL);
        if (err < 0 || !pad->buffer_time)
                return -E_GET_BUFFER_TIME;
        PARA_INFO_LOG("buffer time: %d\n", pad->buffer_time);
        if (snd_pcm_hw_params_set_buffer_time_near(pad->handle, hwparams,
-                       &pad->buffer_time, 0) < 0)
+                       &pad->buffer_time, NULL) < 0)
                return -E_SET_BUFFER_TIME;
        if (snd_pcm_hw_params(pad->handle, hwparams) < 0)
                return -E_HW_PARAMS;
-       snd_pcm_hw_params_get_period_size(hwparams, &period_size, 0);
+       snd_pcm_hw_params_get_period_size(hwparams, &period_size, NULL);
        snd_pcm_hw_params_get_buffer_size(hwparams, &buffer_size);
        PARA_INFO_LOG("buffer size: %lu, period_size: %lu\n", buffer_size,
                period_size);
index d39f0f5..8fbd0a0 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -311,6 +311,7 @@ static void close_receiver(int slot_num)
        a->receiver->close(s->receiver_node);
        free(s->receiver_node);
        s->receiver_node = NULL;
+       stat_task->current_audio_format_num = -1;
 }
 
 static void kill_all_decoders(int error)
@@ -949,6 +950,7 @@ static void close_stat_pipe(void)
        stat_task->length_seconds = 0;
        stat_task->offset_seconds = 0;
        stat_task->vss_status = 0;
+       stat_task->current_audio_format_num = -1;
        audiod_status_dump();
 }
 
diff --git a/blob.c b/blob.c
index 9233227..5ab5c1c 100644 (file)
--- a/blob.c
+++ b/blob.c
@@ -477,7 +477,7 @@ static int blob_get_name_and_def_by_row(struct osl_table *table,
 
 /** Define the \p close function for this blob type. */
 #define DEFINE_BLOB_CLOSE(table_name) \
-       void table_name ## _close(void) \
+       static void table_name ## _close(void) \
        { \
                osl_close_table(table_name ## _table, OSL_MARK_CLEAN); \
                table_name ## _table = NULL; \
@@ -485,7 +485,7 @@ static int blob_get_name_and_def_by_row(struct osl_table *table,
 
 /** Define the \p create function for this blob type. */
 #define DEFINE_BLOB_CREATE(table_name) \
-       int table_name ## _create(const char *dir) \
+       static int table_name ## _create(const char *dir) \
        { \
                table_name ## _table_desc.dir = dir; \
                return osl_create_table(&table_name ## _table_desc); \
@@ -507,7 +507,7 @@ static int blob_open(struct osl_table **table,
 }
 
 #define DEFINE_BLOB_OPEN(table_name) \
-       int table_name ## _open(const char *dir) \
+       static int table_name ## _open(const char *dir) \
        { \
                return blob_open(&table_name ## _table, \
                        &table_name ## _table_desc, dir); \
index 85a3272..74a3c74 100644 (file)
@@ -498,7 +498,7 @@ AC_CHECK_HEADER(sys/soundcard.h, [
        ],
        [
                have_oss="no"
-               AC_MSG_WARN([no linux/soundcard.h $msg])
+               AC_MSG_WARN([no sys/soundcard.h $msg])
        ]
 )
 CPPFLAGS="$OLD_CPPFLAGS"
index 3bcb6e0..011ece6 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -269,7 +269,11 @@ void drop_privileges_or_die(const char *username, const char *groupname)
                exit(EXIT_FAILURE);
        }
        PARA_INFO_LOG("dropping root privileges\n");
-       setuid(p->pw_uid);
+       if (setuid(p->pw_uid) < 0) {
+               PARA_EMERG_LOG("failed to set effective user ID (%s)",
+                       strerror(errno));
+               exit(EXIT_FAILURE);
+       }
        PARA_DEBUG_LOG("uid: %d, euid: %d\n", (int)getuid(), (int)geteuid());
 }
 
@@ -327,7 +331,7 @@ __printf_2_3 void para_log(int ll, const char* fmt,...)
        FILE *fp;
        struct tm *tm;
        time_t t1;
-       char *color, str[MAXLINE] = "";
+       char *color;
 
        ll = PARA_MIN(ll, NUM_LOGLEVELS - 1);
        ll = PARA_MAX(ll, LL_DEBUG);
@@ -338,11 +342,11 @@ __printf_2_3 void para_log(int ll, const char* fmt,...)
        color = daemon_test_flag(DF_COLOR_LOG)? me->log_colors[ll] : NULL;
        if (color)
                fprintf(fp, "%s", color);
-       if (daemon_test_flag(DF_LOG_TIME)) {
-               /* date and time */
+       if (daemon_test_flag(DF_LOG_TIME)) { /* print date and time */
+               char str[100];
                time(&t1);
                tm = localtime(&t1);
-               strftime(str, MAXLINE, "%b %d %H:%M:%S", tm);
+               strftime(str, sizeof(str), "%b %d %H:%M:%S", tm);
                fprintf(fp, "%s ", str);
        }
        if (daemon_test_flag(DF_LOG_HOSTNAME)) {
index a7290e6..8cb17ad 100644 (file)
@@ -70,20 +70,6 @@ struct fecdec_group {
        unsigned char **data;
 };
 
-/**
- * The fecdec filter defers decoding of the first group until the first slice
- * of the next group was received. This avoids buffer underruns in subsequent
- * filters of the filter chain.
- */
-enum group_completion_status {
-       /** No complete group received so far. */
-       GCS_NO_COMPLETE_GROUP,
-       /** First group received, but not yet decoded. */
-       GCS_FIRST_GROUP_COMPLETE,
-       /** At least one complete group decoded. */
-       GCS_FIRST_GROUP_DECODED,
-};
-
 /**
  * Data private to the fecdec filter.
  */
@@ -94,15 +80,13 @@ struct private_fecdec_data {
        struct fecdec_group groups[NUM_FEC_GROUPS];
        /** Whether an audio file header was already received. */
        int have_header;
-       /** See \ref group_completion_status. */
-       unsigned completion_status;
        /** 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)
 {
@@ -124,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;
@@ -175,8 +161,7 @@ static struct fecdec_group *try_to_free_group(struct private_fecdec_data *pfd)
                 * Don't clear the first complete group if it has not yet been
                 * decoded.
                 */
-               if (pfd->completion_status == GCS_FIRST_GROUP_COMPLETE
-                               && pfd->first_complete_group == fg)
+               if (fg == pfd->first_complete_group)
                        continue;
                clear_group(fg);
                return fg;
@@ -196,8 +181,8 @@ static struct fecdec_group *free_oldest_group(struct private_fecdec_data *pfd)
                PARA_WARNING_LOG("Clearing incomplete group %d "
                        "(contains %d slices)\n", oldest->h.group_num,
                        oldest->num_received_slices);
-       assert(pfd->completion_status != GCS_FIRST_GROUP_COMPLETE
-               || oldest != pfd->first_complete_group);
+       if (oldest == pfd->first_complete_group)
+               pfd->first_complete_group = NULL;
        clear_group(oldest);
        return oldest;
 }
@@ -262,6 +247,7 @@ 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,
@@ -279,14 +265,14 @@ static enum fec_group_usability group_is_usable(struct fecdec_group *fg,
        if (fg->h.bos)
                return FEC_GROUP_USABLE;
        if (fg->h.audio_header_size)
-               return FEC_GROUP_USABLE;
+               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, need;
+       size_t written, need;
        struct private_fecdec_data *pfd = fn->private_data;
        enum fec_group_usability u = group_is_usable(fg, pfd);
 
@@ -317,6 +303,21 @@ static int decode_group(struct fecdec_group *fg, struct filter_node *fn)
                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)
@@ -374,37 +375,39 @@ 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 (!add_slice(buf, fg))
+       if (!add_slice(buf, fg)) /* group already complete */
                return 1;
-       if (group_complete(fg)) {
-               if (pfd->completion_status == GCS_NO_COMPLETE_GROUP) {
-                       pfd->completion_status = GCS_FIRST_GROUP_COMPLETE;
-                       pfd->first_complete_group = fg;
+       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;
-               }
-               assert(pfd->fec);
-               ret = decode_group(fg, fn);
-               if (ret < 0)
-                       return ret;
+               pfd->first_complete_group = fg; /* remember it */
                return 1;
        }
-       if (pfd->completion_status == GCS_NO_COMPLETE_GROUP)
-               return 1;
-       if (pfd->completion_status == GCS_FIRST_GROUP_DECODED)
-               return 1;
-       if (fg == pfd->first_complete_group)
-               return 1;
-       assert(!pfd->fec);
+       /* 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;
-       pfd->completion_status = GCS_FIRST_GROUP_DECODED;
+       clear_group(pfd->first_complete_group);
+       pfd->first_complete_group = NULL;
+decode:
+       ret = decode_group(fg, fn);
+       if (ret < 0)
+               return ret;
        return 1;
 }
 
@@ -448,7 +451,6 @@ static void fecdec_open(struct filter_node *fn)
        fn->bufsize = FECDEC_DEFAULT_OUTBUF_SIZE;
        fn->buf = para_malloc(fn->bufsize);
        pfd = para_calloc(sizeof(*pfd));
-       pfd->completion_status = GCS_NO_COMPLETE_GROUP;
        fn->private_data = pfd;
        fn->loaded = 0;
 }
index 5b98bbf..0c48934 100644 (file)
@@ -133,7 +133,7 @@ static void http_post_select(fd_set *rfds, __a_unused fd_set *wfds)
        phsd->status = HTTP_CONNECTED;
 }
 
-static void http_pre_select(int *max_fileno, fd_set *rfds, __a_unused fd_set *wfds)
+static void http_pre_select(int *max_fileno, fd_set *rfds, fd_set *wfds)
 {
        struct sender_client *sc, *tmp;
 
@@ -144,6 +144,9 @@ static void http_pre_select(int *max_fileno, fd_set *rfds, __a_unused fd_set *wf
                struct private_http_sender_data *phsd = sc->private_data;
                if (phsd->status == HTTP_CONNECTED) /* need to recv get request */
                        para_fd_set(sc->fd, rfds, max_fileno);
+               if (phsd->status == HTTP_GOT_GET_REQUEST ||
+                               phsd->status == HTTP_INVALID_GET_REQUEST)
+                       para_fd_set(sc->fd, wfds, max_fileno);
        }
 }
 
index 6a55005..3ef0d9e 100644 (file)
@@ -47,13 +47,13 @@ static int oss_pre_select(struct sched *s, struct writer_node *wn)
        struct private_oss_write_data *powd = wn->private_data;
        struct writer_node_group *wng = wn->wng;
 
-       if (!*wng->loaded)
+       if (*wng->loaded - wn->written < powd->bytes_per_frame)
                return 0;
        para_fd_set(powd->fd, &s->wfds, &s->max_fileno);
        return 1;
 }
-static int oss_post_select(__a_unused struct sched *s,
-               struct writer_node *wn)
+
+static int oss_post_select(struct sched *s, struct writer_node *wn)
 {
        int ret;
        struct private_oss_write_data *powd = wn->private_data;
index aa95d38..c404288 100644 (file)
@@ -13,7 +13,7 @@
 
 #include <sys/types.h>
 #include <dirent.h>
-#include <CoreAudio/CoreAudio.h>
+
 #include "para.h"
 #include "fd.h"
 #include "string.h"
 #include "osx_write.cmdline.h"
 #include "error.h"
 
-
-#include <CoreAudio/CoreAudio.h>
+#include <CoreServices/CoreServices.h>
 #include <AudioUnit/AudioUnit.h>
-#include <AudioToolbox/DefaultAudioOutput.h>
+#include <AudioToolbox/AudioToolbox.h>
 
 /** describes one input buffer for the osx writer */
 struct osx_buffer {
index 200e59a..8653c33 100644 (file)
@@ -159,8 +159,8 @@ void send_chunk(struct sender_client *sc, struct sender_status *ss,
                        if (ret < 0)
                                goto out;
                }
-               sc->header_sent = 1;
        }
+       sc->header_sent = 1;
        ret = send_queued_chunks(sc->fd, sc->cq, max_bytes_per_write);
        if (ret < 0) {
                shutdown_client(sc, ss);
index 2e5f5f7..95ff25c 100644 (file)
--- a/server.c
+++ b/server.c
@@ -470,18 +470,20 @@ err:
 static int init_afs(void)
 {
        int ret, afs_server_socket[2];
+       pid_t afs_pid;
 
        ret = socketpair(PF_UNIX, SOCK_DGRAM, 0, afs_server_socket);
        if (ret < 0)
                exit(EXIT_FAILURE);
        afs_socket_cookie = para_random((uint32_t)-1);
-       mmd->afs_pid = fork();
-       if (mmd->afs_pid < 0)
+       afs_pid = fork();
+       if (afs_pid < 0)
                exit(EXIT_FAILURE);
-       if (!mmd->afs_pid) { /* child (afs) */
+       if (afs_pid == 0) { /* child (afs) */
                close(afs_server_socket[0]);
                afs_init(afs_socket_cookie, afs_server_socket[1]);
        }
+       mmd->afs_pid = afs_pid;
        close(afs_server_socket[1]);
        ret = mark_fd_nonblocking(afs_server_socket[0]);
        if (ret < 0)
index 7217c96..7d13f14 100644 (file)
@@ -179,9 +179,7 @@ static int udp_init_session(struct udp_target *ut)
        }
        add_close_on_fork_list(ut->fd);
        ut->cq = cq_new(UDP_CQ_BYTES);
-       PARA_NOTICE_LOG("sending to udp %s#%d using fec parms %d:%d:%d\n",
-               ut->host, ut->port , ut->fcp.max_slice_bytes,
-               ut->fcp.data_slices_per_group, ut->fcp.slices_per_group);
+       PARA_NOTICE_LOG("sending to udp %s#%d\n", ut->host, ut->port);
        return 1;
 }
 
index 63bfdfc..f5aabc0 100644 (file)
@@ -39,7 +39,7 @@ static void populate_user_list(char *user_list_file)
                struct user *u;
                RSA *rsa;
 
-               ret = para_fgets(line, MAXLINE, file_ptr);
+               ret = para_fgets(line, sizeof(line), file_ptr);
                if (ret <= 0)
                        break;
                if (sscanf(line,"%200s %200s %200s %200s", w, n, k, p) < 3)
diff --git a/versions/paraslash-0.3.6.tar.bz2 b/versions/paraslash-0.3.6.tar.bz2
new file mode 100644 (file)
index 0000000..3433a01
Binary files /dev/null and b/versions/paraslash-0.3.6.tar.bz2 differ
diff --git a/versions/paraslash-0.3.6.tar.bz2.asc b/versions/paraslash-0.3.6.tar.bz2.asc
new file mode 100644 (file)
index 0000000..cb571bb
--- /dev/null
@@ -0,0 +1,7 @@
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.6 (GNU/Linux)
+
+iD8DBQBLHTO5Wto1QDEAkw8RAtzpAKCMU4BmDjwy+GBXUVDTCKkh9nTX3wCfe/2N
+8tyHsmUOB6MVi2CzFzEdzmE=
+=SUFL
+-----END PGP SIGNATURE-----
diff --git a/vss.c b/vss.c
index aae601d..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,46 +251,51 @@ 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);
-       k = fc->fcp->data_slices_per_group + fc->num_extra_slices;
        if (fc->first_stream_chunk < 0) {
-               uint32_t largest = afh_get_largest_chunk_size(&mmd->afd.afhi)
-                       + vsst->header_len;
-               uint8_t needed, want;
+               uint8_t hs, ds; /* needed header/data slices */
+               uint8_t rs = fc->fcp->slices_per_group
+                       - fc->fcp->data_slices_per_group; /* redundant slices */
+               int n;
 
-               ret = num_slices(largest, fc, &needed);
+               ret = num_slices(vsst->header_len, fc, &hs);
+               if (ret < 0)
+                       return ret;
+               ret = num_slices(afh_get_largest_chunk_size(&mmd->afd.afhi),
+                       fc, &ds);
+               if (ret < 0)
+                       return ret;
+               k = (int)hs + ds;
+               if (k > 255)
+                       return -E_BAD_CT;
+               if (k < fc->fcp->data_slices_per_group)
+                       k = fc->fcp->data_slices_per_group;
+               n = k + rs;
+               fc->num_extra_slices = k - fc->fcp->data_slices_per_group;
+               PARA_NOTICE_LOG("fec parms %d:%d:%d (%d extra slices)\n",
+                       slice_bytes, k, n, fc->num_extra_slices);
+               fec_free(fc->parms);
+               fc->src_data = para_realloc(fc->src_data, k * sizeof(char *));
+               ret = fec_new(k, n, &fc->parms);
                if (ret < 0)
                        return ret;
-               if (needed > fc->fcp->data_slices_per_group)
-                       PARA_WARNING_LOG("fec parms insufficient for this audio file\n");
-               want = PARA_MAX(needed, fc->fcp->data_slices_per_group);
-               if (want != k) {
-                       fec_free(fc->parms);
-                       fc->src_data = para_realloc(fc->src_data, want * sizeof(char *));
-                       ret = fec_new(want, want + fc->fcp->slices_per_group
-                               - fc->fcp->data_slices_per_group, &fc->parms);
-                       if (ret < 0)
-                               return ret;
-                       k = want;
-                       fc->num_extra_slices = 0;
-                       if (k > fc->fcp->data_slices_per_group) {
-                               fc->num_extra_slices = k - fc->fcp->data_slices_per_group;
-                               PARA_NOTICE_LOG("using %d extra slices\n",
-                                       fc->num_extra_slices);
-                       }
-               }
                fc->stream_start = *now;
                fc->first_stream_chunk = mmd->current_chunk;
                g->first_chunk = mmd->current_chunk;
                g->num = 0;
+               g->start = *now;
        } else {
-               /* use duration of the previous group for the timing of this group */
-               set_slice_duration(fc, g);
+               /*
+                * 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;
+               set_group_timing(fc, g);
                g->first_chunk += g->num_chunks;
                g->num++;
        }
@@ -315,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;
@@ -353,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;
 }
 
index 8460f84..65b7f0e 100644 (file)
@@ -1,6 +1,10 @@
 <h1>Events</h1>
 <hr>
 <ul>
+       <li>2009-12-07: <a href="versions/paraslash-0.3.6.tar.bz2">paraslash-0.3.6</a>
+               <a href="versions/paraslash-0.3.6.tar.bz2.asc">(sig)</a>
+               "cubic continuity"
+       </li>
        <li>2009-09-21: <a href="versions/paraslash-0.3.5.tar.bz2">paraslash-0.3.5</a>
                <a href="versions/paraslash-0.3.5.tar.bz2.asc">(sig)</a>
                "symplectic separability"
diff --git a/write.h b/write.h
index 1f316fc..8816be7 100644 (file)
--- a/write.h
+++ b/write.h
@@ -107,6 +107,8 @@ struct writer_node_group {
        struct task task;
        /** Whether the group is open, i.e. wng_open() was called. */
        int open;
+       /** Max number of bytes written in the previous post_select() call. */
+       int last_written;
 };
 
 /** Loop over each writer node in a writer group. */
index 476df8f..2dca309 100644 (file)
@@ -20,7 +20,7 @@ const char *writer_names[] ={WRITER_NAMES};
 /** the array of supported writers */
 struct writer writers[NUM_SUPPORTED_WRITERS] = {WRITER_ARRAY};
 
-static void wng_pre_select(__a_unused struct sched *s, struct task *t)
+static void wng_pre_select(struct sched *s, struct task *t)
 {
        struct writer_node_group *g = container_of(t, struct writer_node_group, task);
        int i;
@@ -34,13 +34,25 @@ static void wng_pre_select(__a_unused struct sched *s, struct task *t)
                if (t->error < 0)
                        return;
        }
+       /*
+        * Force a minimal delay if something was written during the previous
+        * call to wng_post_select(). This is necessary because the filter
+        * chain might still have data for us which it couldn't convert during
+        * the previous run due to its buffer size constraints. In this case we
+        * do not want to wait until the next input data arrives as this could
+        * lead to buffer underruns.
+        */
+       if (g->last_written == 0)
+               return;
+       s->timeout.tv_sec = 0;
+       s->timeout.tv_usec = 1;
 }
 
 static void wng_post_select(struct sched *s, struct task *t)
 {
        struct writer_node_group *g = container_of(t, struct writer_node_group, task);
        int i;
-       size_t min_written = 0;
+       size_t min_written = 0, max_written = 0;
 
        FOR_EACH_WRITER_NODE(i, g) {
                struct writer_node *wn = &g->writer_nodes[i];
@@ -52,7 +64,9 @@ static void wng_post_select(struct sched *s, struct task *t)
                        min_written = wn->written;
                else
                        min_written = PARA_MIN(min_written, wn->written);
+               max_written = PARA_MAX(max_written, wn->written);
        }
+       g->last_written = max_written;
        //PARA_INFO_LOG("loaded: %zd, min_written: %zd bytes\n", *g->loaded, min_written);
        if (min_written) {
                *g->loaded -= min_written;