0.5.1 (to be announced) "temporary implication"
----------------------------------------------
- - audiod improvements and fixes.
- - buffer tree robustness improvements.
- - cleanup of the mood subsystem.
+Lots of fixes and improvements all over the place, and a major overhaul
+of the build system.
+
+ - Audiod improvements and fixes.
+ - Buffer tree robustness improvements.
+ - Cleanup of the mood subsystem.
+ - Fixes and cleanups for the flac decoder.
+ - Latency improvements for the ogg/opus decoder.
+ - Crypto support is now optional. On systems without
+ openssl/gcrypt, the build succeeds but para_server,
+ para_audiod, para_client won't be built.
+ - The build system now works for cross-compile setups.
+ - The dependency tree has been flattened, which speeds up
+ builds and avoids to recreate the man pages on every change.
+ - The error code helper has been rewritten from perl to C,
+ which further improves build time.
+ - Many small bugs in the build system have been identified
+ and fixed.
----------------------------------------
0.5.0 (2013-08-23) "invertible validity"
int open_and_update_audio_file(struct osl_row *aft_row, long score,
struct audio_file_data *afd);
int load_afd(int shmid, struct audio_file_data *afd);
-int load_afsi(struct afs_info *afsi, struct osl_object *obj);
-void save_afsi(struct afs_info *afsi, struct osl_object *obj);
int get_afsi_of_row(const struct osl_row *row, struct afs_info *afsi);
int get_afhi_of_row(const struct osl_row *row, struct afh_info *afhi);
-int get_afsi_of_path(const char *path, struct afs_info *afsi);
int get_audio_file_path_of_row(const struct osl_row *row, char **path);
-int get_afsi_object_of_row(const struct osl_row *row, struct osl_object *obj);
int audio_file_loop(void *private_data, osl_rbtree_loop_func *func);
void aft_check_callback(int fd, __a_unused const struct osl_object *query);
AFSI_SIZE = 32
};
-/**
+/*
* Convert a struct afs_info to an osl object.
*
* \param afsi Pointer to the audio file info to be converted.
*
* \sa load_afsi().
*/
-void save_afsi(struct afs_info *afsi, struct osl_object *obj)
+static void save_afsi(struct afs_info *afsi, struct osl_object *obj)
{
char *buf = obj->data;
memset(buf + AFSI_AUDIO_FORMAT_UNUSED_OFFSET, 0, 2);
}
-/**
- * Get the audio file selector info struct stored in an osl object.
+/*
+ * Get the audio file selector info struct stored in an osl object.
*
* \param afsi Points to the audio_file info structure to be filled in.
* \param obj The osl object holding the data.
*
- * \return Positive on success, negative on errors. Possible errors: \p E_BAD_AFS.
+ * \return Standard.
*
* \sa save_afsi().
*/
-int load_afsi(struct afs_info *afsi, struct osl_object *obj)
+static int load_afsi(struct afs_info *afsi, struct osl_object *obj)
{
char *buf = obj->data;
if (obj->size < AFSI_SIZE)
return osl(osl_get_row(audio_file_table, AFTCOL_HASH, &obj, row));
}
-/**
- * Get the osl object holding the audio file selector info of a row.
+/*
+ * Get the audio file selector info object of a row.
*
* \param row Pointer to a row in the audio file table.
* \param obj Result pointer.
*
* \return Standard.
*/
-int get_afsi_object_of_row(const struct osl_row *row, struct osl_object *obj)
+static int get_afsi_object_of_row(const struct osl_row *row,
+ struct osl_object *obj)
{
return osl(osl_get_object(audio_file_table, row, AFTCOL_AFSI, obj));
}
return load_afsi(afsi, &obj);
}
-/**
+/*
* Get the audio file selector info, given the path of an audio table.
*
* \param path The full path of the audio file.
* \param afsi Result pointer.
*
- * \return Positive on success, negative on errors.
+ * \return Standard.
*/
-int get_afsi_of_path(const char *path, struct afs_info *afsi)
+static int get_afsi_of_path(const char *path, struct afs_info *afsi)
{
struct osl_object obj;
int ret = get_afsi_object_of_path(path, &obj);
*
* \return Zero or a negative error code. If the underlying call to readv(2)
* returned zero (indicating an end of file condition) or failed for some
- * reason other than \p EAGAIN, a negative return value is returned.
+ * reason other than \p EAGAIN, a negative error code is returned.
*
* In any case, \a num_bytes contains the number of bytes that have been
* successfully read from \a fd (zero if the first readv() call failed with
* We can not consume directly what was copied by the read callback
* because we might need to feed unconsumend bytes to the decoder again
* after the read callback ran out of data and returned ABORT. So we
- * track how many bytes are unconsumed so far.
+ * track how many bytes have been fed to libflac but are unconsumed so far.
*/
size_t unconsumed;
};
}
if (*bytes > 0)
return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+ /**
+ * Nothing was copied. If the input queue of the btrn is smaller than
+ * the minimal input queue size, our parent must have been gone, so
+ * we're not going to get more input. Since our remaining data is not
+ * sufficient do decode a single frame, we have an EOF condition.
+ */
+ if (btr_get_input_queue_size(btrn) < fn->min_iqs) {
+ assert(btr_no_parent(btrn));
+ return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+ }
/*
* We are kind of screwed here. Returning CONTINUE with a byte count of
* zero leads to an endless loop, so we must return either EOF or
write_int16_host_endian(outbuffer + 4 * k + 2, right);
}
}
- btr_add_output(outbuffer, n * 4, btrn);
+ btr_add_output(outbuffer, n * channels * 2, btrn);
flac_consume(fn);
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
struct private_flacdec_data *pfd = fn->private_data;
struct btr_node *btrn = fn->btrn;
int ret;
+ FLAC__StreamDecoderState state;
if (output_queue_full(btrn))
return 0;
goto out;
}
pfd->have_more = false;
- FLAC__StreamDecoderState state;
FLAC__stream_decoder_process_single(pfd->decoder);
state = FLAC__stream_decoder_get_state(pfd->decoder);
ret = -E_FLACDEC_EOF;
goto out;
if (state == FLAC__STREAM_DECODER_ABORTED) {
FLAC__stream_decoder_flush(pfd->decoder);
- fn->min_iqs = pfd->unconsumed + 1;
+ pfd->unconsumed = 0; /* feed unconsumed bytes again */
+ fn->min_iqs = btr_get_input_queue_size(btrn) + 1;
ret = 1;
goto out;
}
+ pfd->have_more = true;
fn->min_iqs = 0;
ret = 1;
out:
int channels;
int preskip;
bool have_opus_stream;
+ bool have_more;
ogg_int32_t opus_serialno;
};
return 1;
}
+#define OPUSDEC_MAX_OUTPUT_SIZE (1024 * 1024)
+
static int opusdec_post_select(__a_unused struct sched *s, struct task *t)
{
struct filter_node *fn = container_of(t, struct filter_node, task);
struct opusdec_context *ctx = fn->private_data;
struct btr_node *btrn = fn->btrn;
int ret;
- char *btr_buf, *data;
- size_t nbytes;
ogg_packet op;
ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
- if (ret <= 0)
- goto out;
- btr_merge(btrn, fn->min_iqs);
- nbytes = btr_next_buffer(btrn, &btr_buf);
- nbytes = PARA_MIN(nbytes, (size_t)32768);
- ret = 0;
- if (nbytes == 0)
- goto out;
- data = ogg_sync_buffer(&ctx->oy, nbytes);
- memcpy(data, btr_buf, nbytes);
- btr_consume(btrn, nbytes);
- ogg_sync_wrote(&ctx->oy, nbytes);
- for (;;) { /* loop over all ogg pages we got */
- ret = 0;
- if (ogg_sync_pageout(&ctx->oy, &ctx->ogg_page) != 1)
+ if (ret < 0) {
+ if (ret != -E_BTR_EOF) /* fatal error */
+ goto out;
+ if (!ctx->have_more) /* EOF */
goto out;
- if (!ctx->stream_init) {
- ogg_stream_init(&ctx->os, ogg_page_serialno(&ctx->ogg_page));
+ } else if (ret == 0 && !ctx->have_more) /* nothing to do */
+ goto out;
+ if (btr_get_output_queue_size(btrn) > OPUSDEC_MAX_OUTPUT_SIZE)
+ return 0;
+ for (;;) {
+ int serial;
+ if (ctx->stream_init) {
+ ret = ogg_stream_packetout(&ctx->os, &op);
+ if (ret == 1)
+ break;
+ }
+ while (ogg_sync_pageout(&ctx->oy, &ctx->ogg_page) != 1) {
+ char *btr_buf, *data;
+ size_t nbytes = btr_next_buffer(btrn, &btr_buf);
+ nbytes = PARA_MIN(nbytes, (size_t)24 * 1024);
+ //PARA_CRIT_LOG("nbytes: %d\n", nbytes);
+ ctx->have_more = false;
+ if (nbytes == 0)
+ return 0;
+ data = ogg_sync_buffer(&ctx->oy, nbytes);
+ memcpy(data, btr_buf, nbytes);
+ btr_consume(btrn, nbytes);
+ ogg_sync_wrote(&ctx->oy, nbytes);
+ }
+ ctx->have_more = true;
+ serial = ogg_page_serialno(&ctx->ogg_page);
+ if (ctx->stream_init) {
+ if (serial != ctx->os.serialno)
+ ogg_stream_reset_serialno(&ctx->os, serial);
+ } else {
+ ogg_stream_init(&ctx->os, serial);
ctx->stream_init = true;
}
- if (ogg_page_serialno(&ctx->ogg_page) != ctx->os.serialno)
- ogg_stream_reset_serialno(&ctx->os,
- ogg_page_serialno(&ctx->ogg_page));
/* Add page to the bitstream */
ogg_stream_pagein(&ctx->os, &ctx->ogg_page);
- for (;;) { /* loop over all opus packets */
- ret = ogg_stream_packetout(&ctx->os, &op);
- if (ret != 1)
- break;
- ret = decode_packet(ctx, &op, btrn);
- if (ret < 0)
- goto out;
- ctx->packet_count++;
- if (ctx->eos)
- ctx->have_opus_stream = false;
- }
}
+ ret = decode_packet(ctx, &op, btrn);
+ if (ret < 0)
+ goto out;
+ ctx->packet_count++;
+ if (ctx->eos)
+ ctx->have_opus_stream = false;
out:
if (ret < 0)
btr_remove_node(&fn->btrn);
return ret;
}
+static void opusdec_pre_select(struct sched *s, struct task *t)
+{
+ struct filter_node *fn = container_of(t, struct filter_node, task);
+ struct opusdec_context *ctx = fn->private_data;
+ int ret = btr_node_status(fn->btrn, fn->min_iqs, BTR_NT_INTERNAL);
+
+ if (ret != 0)
+ return sched_min_delay(s);
+ if (ctx->have_more)
+ return;
+ if (btr_get_output_queue_size(fn->btrn) <= OPUSDEC_MAX_OUTPUT_SIZE)
+ return sched_min_delay(s);
+}
+
/**
* The init function of the opusdec filter.
*
{
f->open = opusdec_open;
f->close = opusdec_close;
- f->pre_select = generic_filter_pre_select;
+ f->pre_select = opusdec_pre_select;
f->post_select = opusdec_post_select;
f->execute = opusdec_execute;
}