/** \file aacdec_filter.c paraslash's aac (m4a) decoder. */
#include <regex.h>
+#include <stdbool.h>
#include "para.h"
#include "list.h"
#include "sched.h"
#include "ggo.h"
+#include "buffer_tree.h"
#include "filter.h"
#include "error.h"
#include "string.h"
/** \file amp_filter.c Paraslash's amplify filter. */
#include <regex.h>
+#include <stdbool.h>
#include "para.h"
#include "amp_filter.cmdline.h"
#include "list.h"
#include "sched.h"
#include "ggo.h"
+#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
#include "error.h"
#include <dirent.h>
#include <signal.h>
#include <openssl/rc4.h>
+#include <stdbool.h>
#include "para.h"
#include "error.h"
#include "sched.h"
#include "ggo.h"
#include "recv.h"
+#include "buffer_tree.h"
#include "filter.h"
#include "grab_client.h"
#include "client.cmdline.h"
#include <regex.h>
#include <sys/types.h>
#include <dirent.h>
+#include <stdbool.h>
#include "para.h"
#include "audiod.cmdline.h"
#include "list.h"
#include "sched.h"
#include "ggo.h"
+#include "buffer_tree.h"
#include "filter.h"
#include "grab_client.h"
#include "error.h"
#define FOR_EACH_BUFFER_REF_SAFE(_br, _tmp, _btrn) \
list_for_each_entry_safe((_br), (_tmp), &(_btrn)->input_queue, node)
-struct btr_node *btr_new_node(char *name, struct btr_node *parent,
+struct btr_node *btr_new_node(const char *name, struct btr_node *parent,
btr_command_handler handler, void *context)
{
struct btr_node *btrn = para_malloc(sizeof(*btrn));
{
return btrn->context;
}
+
+/**
+ * Merge the first two input buffers into one.
+ *
+ * This is a quite expensive operation.
+ *
+ * \return The number of buffers that have been merged (zero, one or two).
+ */
+int btr_merge(struct btr_node *btrn)
+{
+ struct btr_buffer_reference *brs[2], *br;
+ char *bufs[2], *buf;
+ size_t szs[2], sz;
+ int i;
+
+ if (list_empty(&btrn->input_queue))
+ return 0;
+ if (list_is_singular(&btrn->input_queue))
+ return 1;
+ i = 0;
+ /* get references to the first two buffers */
+ FOR_EACH_BUFFER_REF(br, btrn) {
+ brs[i] = br;
+ szs[i] = btr_get_buffer_by_reference(brs[i], bufs + i);
+ i++;
+ if (i == 2)
+ break;
+ }
+ /* make a new btrb that combines the two buffers and a br to it. */
+ sz = szs[0] + szs[1];
+ //PARA_CRIT_LOG("merging input buffers: (%zu, %zu) -> %zu\n",
+ // szs[0], szs[1], sz);
+ buf = para_malloc(sz);
+ /* TODO: Avoid this memcopy by introducing btr buffer pool. */
+ memcpy(buf, bufs[0], szs[0]);
+ memcpy(buf + szs[0], bufs[1], szs[1]);
+
+ br = para_malloc(sizeof(*br));
+ br->btrb = new_btrb(buf, sz);
+ br->btrb->refcount = 1;
+ br->consumed = 0;
+
+ /* replace the first two refs by the new one */
+ btr_drop_buffer_reference(brs[0]);
+ btr_drop_buffer_reference(brs[1]);
+ para_list_add(&br->node, &btrn->input_queue);
+ return 2;
+}
typedef int (*btr_command_handler)(struct btr_node *btrn,
const char *command, char **result);
-struct btr_node *btr_new_node(char *name, struct btr_node *parent,
+struct btr_node *btr_new_node(const char *name, struct btr_node *parent,
btr_command_handler handler, void *context);
void btr_del_node(struct btr_node *btrn);
void btr_add_output(char *buf, size_t size, struct btr_node *btrn);
int btr_splice_out_node(struct btr_node *btrn);
void btr_pushdown(struct btr_node *btrn);
void *btr_context(struct btr_node *btrn);
+int btr_merge(struct btr_node *btrn);
*/
#include <regex.h>
+#include <stdbool.h>
#include "para.h"
#include "compress_filter.cmdline.h"
#include "list.h"
#include "sched.h"
#include "ggo.h"
+#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
#include "error.h"
PARA_ERROR(MAD_FRAME_DECODE, "mad frame decode error"), \
PARA_ERROR(MP3DEC_OVERRUN, "mp3 output buffer overrun"), \
PARA_ERROR(MP3DEC_SYNTAX, "syntax error in mp3dec config"), \
+ PARA_ERROR(MP3DEC_EOF, "mp3dec: end of file"), \
#define FILTER_ERRORS \
/** \file fecdec_filter.c A filter that fec-decodes an audio stream. */
#include <regex.h>
+#include <stdbool.h>
#include <dirent.h>
#include "para.h"
#include "list.h"
#include "sched.h"
#include "ggo.h"
+#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
#include "portable_io.h"
#include "list.h"
#include "sched.h"
#include "ggo.h"
+#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
#include "stdin.h"
}
/* TODO: support more than one filter, actually parse options */
-static int main_btr(void)
+static int __noreturn main_btr(int argc, char *argv[])
{
- return 42;
+ static struct sched s;
+ struct filter_node *fn = para_calloc(sizeof(*fn));
+ int ret;
+ struct filter *f;
+
+ sit->btrn = btr_new_node("stdin", NULL, NULL, NULL);
+ stdin_set_defaults(sit);
+ register_task(&sit->task);
+
+ ret = check_filter_arg("mp3dec ", &fn->conf);
+ if (ret < 0)
+ goto err;
+ fn->filter_num = ret;
+ f = filters + fn->filter_num;
+ fn->btrn = btr_new_node(f->name, sit->btrn, f->execute, fn);
+ fn->task.pre_select = f->pre_select;
+ fn->task.post_select = f->post_select;
+ sprintf(fn->task.status, "mp3dec");
+ f->open(fn);
+ register_task(&fn->task);
+
+ sot->btrn = btr_new_node("stdout", fn->btrn, NULL, NULL);
+ stdout_set_defaults(sot);
+ register_task(&sot->task);
+
+ s.default_timeout.tv_sec = 1;
+ s.default_timeout.tv_usec = 0;
+ ret = schedule(&s);
+err:
+ free(fn);
+ if (ret < 0)
+ PARA_EMERG_LOG("%s\n", para_strerror(-ret));
+ exit(ret < 0? EXIT_FAILURE : EXIT_SUCCESS);
}
/**
if (ret < 0)
goto out;
if (conf.buffer_tree_given) {
- ret = main_btr();
+ ret = main_btr(argc, argv);
goto out;
}
stdin_set_defaults(sit);
/** The help texts for this filter. */
struct ggo_help help;
+
+ void (*pre_select)(struct sched *s, struct task *t);
+ void (*post_select)(struct sched *s, struct task *t);
+ btr_command_handler execute;
};
void close_filters(struct filter_chain *fc);
#include <regex.h>
#include <sys/types.h>
#include <dirent.h>
+#include <stdbool.h>
#include "para.h"
#include "list.h"
#include "sched.h"
#include "fd.h"
#include "ggo.h"
+#include "buffer_tree.h"
#include "filter.h"
#include "error.h"
#include "string.h"
#include <regex.h>
#include <sys/types.h>
#include <dirent.h>
+#include <stdbool.h>
#include "para.h"
#include "list.h"
#include "sched.h"
#include "ggo.h"
+#include "buffer_tree.h"
#include "filter.h"
#include "grab_client.h"
#include "audiod.h"
#include <mad.h>
#include <regex.h>
+#include <stdbool.h>
#include "para.h"
#include "mp3dec_filter.cmdline.h"
#include "list.h"
#include "sched.h"
#include "ggo.h"
+#include "buffer_tree.h"
#include "filter.h"
#include "error.h"
#include "string.h"
MP3DEC_FLAG_BAD_DATA = 1,
/** Some output has already been produced. */
MP3DEC_FLAG_DECODE_STARTED = 2,
+ MP3DEC_FLAG_NEED_MORE = 4,
};
/** Data specific to the mp3dec filter. */
struct timeval stream_start_barrier;
/** Wait until this many input bytes are available. */
size_t input_len_barrier;
+ /** The number of channels of the current stream. */
+ unsigned int channels;
+ /** Current sample rate in Hz. */
+ unsigned int samplerate;
};
static int need_bad_data_delay(struct private_mp3dec_data *pmd,
*/
static int handle_decode_error(struct private_mp3dec_data *pmd, size_t len)
{
+ const struct timeval delay = {0, 60 * 1000};
if (!MAD_RECOVERABLE(pmd->stream.error)
&& pmd->stream.error != MAD_ERROR_BUFLEN) {
PARA_ERROR_LOG("%s\n", mad_stream_errorstr(&pmd->stream));
*/
pmd->flags |= MP3DEC_FLAG_BAD_DATA;
pmd->input_len_barrier = len;
- tv_add(now, &(struct timeval){0, 60 * 1000},
- &pmd->stream_start_barrier);
+ tv_add(now, &delay, &pmd->stream_start_barrier);
return 1;
}
+/** 640K ought to be enough for everybody ;) */
+#define MP3DEC_MAX_PENDING (640 * 1024)
+
+static void mp3dec_pre_select(struct sched *s, struct task *t)
+{
+ struct filter_node *fn = container_of(t, struct filter_node, task);
+ size_t iqs = btr_get_input_queue_size(fn->btrn);
+ struct private_mp3dec_data *pmd = fn->private_data;
+
+ t->error = 0;
+ if (iqs <= pmd->input_len_barrier)
+ return;
+ if (btr_bytes_pending(fn->btrn) > MP3DEC_MAX_PENDING)
+ return; /* FIXME, should use reasonable bound on timeout */
+ s->timeout.tv_sec = 0;
+ s->timeout.tv_usec = 1;
+}
+
static ssize_t mp3dec(char *inbuffer, size_t len, struct filter_node *fn)
{
int i, ret;
return copy;
}
+static size_t used_mad_buffer_bytes(struct mad_stream *s, size_t max)
+{
+ size_t rv;
+
+ if (!s->next_frame)
+ return max;
+ /* we still have some data */
+ rv = s->next_frame - s->buffer;
+ assert(rv <= max);
+ return rv;
+}
+
static void mp3dec_close(struct filter_node *fn)
{
struct private_mp3dec_data *pmd = fn->private_data;
fn->private_data = NULL;
}
+static void mp3dec_post_select(__a_unused struct sched *s, struct task *t)
+{
+ struct filter_node *fn = container_of(t, struct filter_node, task);
+ int i, ret;
+ struct private_mp3dec_data *pmd = fn->private_data;
+ struct btr_node *btrn = fn->btrn;
+ size_t loaded, used, len = btr_get_input_queue_size(btrn);
+ char *inbuffer, *outbuffer;
+
+ pmd->stream.error = 0;
+ t->error = 0;
+ if (btr_bytes_pending(btrn) > MP3DEC_MAX_PENDING)
+ return;
+ if (need_bad_data_delay(pmd, len))
+ return;
+ if (pmd->input_len_barrier != 0 && btr_no_parent(btrn)) {
+ ret = -E_MP3DEC_EOF;
+ goto err;
+ }
+next_buffer:
+ len = btr_next_buffer(btrn, &inbuffer);
+ if (len == 0)
+ return;
+ mad_stream_buffer(&pmd->stream, (unsigned char *)inbuffer, len);
+next_frame:
+ ret = mad_header_decode(&pmd->frame.header, &pmd->stream);
+ if (ret < 0) {
+ used = used_mad_buffer_bytes(&pmd->stream, len);
+ btr_consume(btrn, used);
+ if (pmd->stream.error == MAD_ERROR_BUFLEN) {
+ pmd->input_len_barrier = len - used;
+ ret = btr_merge(btrn);
+ if (ret != 2)
+ return;
+ } else if (pmd->stream.error != MAD_ERROR_LOSTSYNC)
+ PARA_DEBUG_LOG("header decode: %s\n",
+ mad_stream_errorstr(&pmd->stream));
+ goto next_buffer;
+ }
+ pmd->input_len_barrier = 0;
+ pmd->samplerate = pmd->frame.header.samplerate;
+ pmd->channels = MAD_NCHANNELS(&pmd->frame.header);
+ ret = mad_frame_decode(&pmd->frame, &pmd->stream);
+ if (ret != 0) {
+ PARA_CRIT_LOG("frame decode: %d\n", ret);
+ used = used_mad_buffer_bytes(&pmd->stream, len);
+ ret = handle_decode_error(pmd, used);
+ btr_consume(btrn, used);
+ if (ret < 0)
+ goto err;
+ if (ret == 0)
+ goto next_buffer;
+ return;
+ }
+ mad_synth_frame(&pmd->synth, &pmd->frame);
+ pmd->flags |= MP3DEC_FLAG_DECODE_STARTED;
+
+ outbuffer = para_malloc(pmd->synth.pcm.length * 4);
+ loaded = 0;
+ for (i = 0; i < pmd->synth.pcm.length; i++) {
+ int sample = MAD_TO_SHORT(pmd->synth.pcm.samples[0][i]);
+ write_int16_host_endian(outbuffer + loaded, sample);
+ loaded += 2;
+ if (MAD_NCHANNELS(&pmd->frame.header) == 2) { /* stereo */
+ sample = MAD_TO_SHORT(pmd->synth.pcm.samples[1][i]);
+ write_int16_host_endian(outbuffer + loaded, sample);
+ loaded += 2;
+ }
+ }
+ btr_add_output(outbuffer, loaded, btrn);
+ goto next_frame;
+err:
+ assert(ret < 0);
+ mp3dec_close(fn);
+ t->error = ret;
+ btr_del_node(btrn);
+}
+
static void mp3dec_open(struct filter_node *fn)
{
struct private_mp3dec_data *pmd = para_calloc(sizeof(*pmd));
f->convert = mp3dec;
f->close = mp3dec_close;
f->parse_config = mp3dec_parse_config;
+ f->pre_select = mp3dec_pre_select;
+ f->post_select = mp3dec_post_select;
f->help = (struct ggo_help) {
.short_help = mp3dec_filter_args_info_help,
.detailed_help = mp3dec_filter_args_info_detailed_help
#include <regex.h>
#include <vorbis/vorbisfile.h>
+#include <stdbool.h>
#include "para.h"
#include "oggdec_filter.cmdline.h"
#include "list.h"
#include "sched.h"
#include "ggo.h"
+#include "buffer_tree.h"
#include "filter.h"
#include "error.h"
#include "string.h"
/** \file prebuffer_filter.c Paraslash's prebuffering filter. */
#include <regex.h>
+#include <stdbool.h>
#include "para.h"
#include "prebuffer_filter.cmdline.h"
#include "list.h"
#include "sched.h"
#include "ggo.h"
+#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
#include "error.h"
{
struct stdin_task *sit = container_of(t, struct stdin_task, task);
- if (btr_no_children(sit->btrn)) {
+ if (btr_no_children(sit->btrn)) { /* TODO: defer node deletion to post select */
t->error = -E_STDIN_NO_CHILD;
btr_del_node(sit->btrn);
sit->btrn = NULL;
/** \file wav_filter.c A filter that inserts a wave header. */
#include <regex.h>
+#include <stdbool.h>
#include "para.h"
#include "error.h"
#include "list.h"
#include "sched.h"
#include "ggo.h"
+#include "buffer_tree.h"
#include "filter.h"
#include "string.h"
#include "portable_io.h"
#include <string.h>
#include <regex.h>
#include <sys/select.h>
+#include <stdbool.h>
#include "para.h"
#include "error.h"
#include "ggo.h"
#include "string.h"
#include "sched.h"
+#include "buffer_tree.h"
#include "filter.h"
#include "bitstream.h"
#include "imdct.h"