Was cooking for two weeks with no problems.
273756 The opus decoder.
7007ae The opus audio format handler.
8bcf75 ogg/opus: Infrastructure.
53133e speex: Don't export spx_ctl().
------------------------------------------
0.?.? (to be announced) "spectral gravity"
------------------------------------------
+A new audio format, UTF-8 support, and tons of other improvements
+and fixes all over the place.
+ - New audio format: ogg/opus.
- UTF8 support for para_gui and the mp3 audio format handler.
- Scheduler improvements and fixes.
- The obsolete gettimeofday() function has been replaced
void flac_afh_init(struct audio_format_handler *);
#endif
+#ifdef HAVE_OPUS
+ void opus_afh_init(struct audio_format_handler *);
+#endif
+
void wma_afh_init(struct audio_format_handler *);
/** The list of all status items */
.name = "flac",
#ifdef HAVE_FLAC
.init = flac_afh_init,
+#endif
+ },
+ {
+ .name = "opus",
+#ifdef HAVE_OPUS
+ .init = opus_afh_init,
#endif
},
{
default_writer="OSX_WRITE"
AC_DEFINE(HAVE_CORE_AUDIO, 1, define to 1 on Mac Os X)
fi
-########################################################### ogg/vorbis/speex
+####################################################### ogg/vorbis/speex/opus
have_ogg="yes"
OLD_CPPFLAGS="$CPPFLAGS"
OLD_LD_FLAGS="$LDFLAGS"
[look for speex headers also in dir])])
AC_ARG_WITH(speex_libs, [AS_HELP_STRING(--with-speex-libs=dir,
[look for speex libs also in dir])])
+AC_ARG_WITH(opus_headers, [AS_HELP_STRING(--with-opus-headers=dir,
+ [look for opus headers also in dir])])
+AC_ARG_WITH(opus_libs, [AS_HELP_STRING(--with-opus-libs=dir,
+ [look for opus libs also in dir])])
if test -n "$with_ogg_headers"; then
ogg_cppflags="-I$with_ogg_headers"
have_vorbis="yes"
have_speex="yes"
+have_opus="yes"
if test "$have_ogg" = "yes"; then
# vorbis
if test -n "$with_vorbis_headers"; then
fi
AC_CHECK_LIB([speex], [speex_decoder_init], [], [ have_speex="no" ])
AC_CHECK_HEADERS([speex/speex.h], [], [ have_speex="no" ])
+
+ # opus
+ if test -n "$with_opus_headers"; then
+ opus_cppflags="-I$with_opus_headers"
+ CPPFLAGS="$CPPFLAGS $opus_cppflags"
+ fi
+ if test -n "$with_opus_libs"; then
+ speex_libs="-L$with_opus_libs"
+ LDFLAGS="$LDFLAGS $opus_libs"
+ fi
+ AC_CHECK_LIB([opus], [opus_multistream_decode], [], [ have_opus="no" ])
+ AC_CHECK_HEADERS([opus/opus.h], [], [ have_opus="no" ])
else
- AC_MSG_WARN([vorbis/speex depend on libogg, which was not detected])
+ AC_MSG_WARN([vorbis/speex/opus depend on libogg, which was not detected])
have_vorbis="no"
have_speex="no"
+ have_opus="no"
fi
msg="support in para_server/para_filter/para_afh"
-if test "$have_vorbis" = "yes" || test "$have_speex" = "yes"; then
+if test "$have_vorbis" = "yes" || \
+ test "$have_speex" = "yes" || \
+ test "$have_opus" = "yes"; then
AC_SUBST(ogg_cppflags)
ogg_libs="$ogg_libs -logg"
if test "$OSTYPE" = "Darwin"; then
else
AC_MSG_WARN([no ogg/speex $msg])
fi
+if test "$have_opus" = "yes"; then
+ all_errlist_objs="$all_errlist_objs opusdec_filter opus_afh opus_common"
+ AC_DEFINE(HAVE_OPUS, 1, define to 1 to turn on ogg/opus support)
+ filters="$filters opusdec"
+ opus_libs="-lopus"
+ server_ldflags="$server_ldflags $opus_libs"
+ filter_ldflags="$filter_ldflags $opus_libs"
+ audiod_ldflags="$audiod_ldflags $opus_libs"
+ afh_ldflags="$afh_ldflags $opus_libs"
+ play_ldflags="$play_ldflags $opus_libs"
+ recv_ldflags="$recv_ldflags $opus_libs"
+
+ server_errlist_objs="$server_errlist_objs opus_afh opus_common"
+ filter_errlist_objs="$filter_errlist_objs opusdec_filter opus_common"
+ audiod_errlist_objs="$audiod_errlist_objs opusdec_filter opus_common"
+ afh_errlist_objs="$afh_errlist_objs opus_afh opus_common"
+ play_errlist_objs="$play_errlist_objs opusdec_filter opus_afh opus_common"
+ recv_errlist_objs="$recv_errlist_objs opus_afh opus_common"
+
+ audiod_audio_formats="$audiod_audio_formats opus"
+ server_audio_formats="$server_audio_formats opus"
+else
+ AC_MSG_WARN([no ogg/opus $msg])
+fi
CPPFLAGS="$OLD_CPPFLAGS"
LDFLAGS="$OLD_LDFLAGS"
LIBS="$OLD_LIBS"
#define OSS_MIX_ERRORS \
PARA_ERROR(OSS_MIXER_CHANNEL, "invalid mixer channel"), \
+
#define ALSA_MIX_ERRORS \
PARA_ERROR(ALSA_MIX_OPEN, "could not open mixer"), \
PARA_ERROR(ALSA_MIX_BAD_ELEM, "invalid/unsupported control element"), \
PARA_ERROR(LIBSAMPLERATE, "secret rabbit code error"), \
+#define OPUS_COMMON_ERRORS \
+ PARA_ERROR(OPUS_HEADER, "invalid opus header"), \
+
+
+#define OPUS_AFH_ERRORS \
+ PARA_ERROR(OPUS_COMMENT, "invalid or corrupted opus comment"), \
+
+
+#define OPUSDEC_FILTER_ERRORS \
+ PARA_ERROR(CREATE_OPUS_DECODER, "could not create opus decoder"), \
+ PARA_ERROR(OPUS_SET_GAIN, "opus: could not set gain"), \
+ PARA_ERROR(OPUS_DECODE, "opus decode error"), \
+
#define SIDEBAND_ERRORS \
PARA_ERROR(BAD_BAND, "invalid or unexpected band designator"), \
PARA_ERROR(SB_PACKET_SIZE, "invalid sideband packet size or protocol error"), \
* Licensed under the GPL v2. For licencing details see COPYING.
*/
-/** \file ogg_afh_common.c Functions common to ogg/vorbis and ogg/speex. */
+/** \file ogg_afh_common.c Functions common to all ogg/ codecs. */
#include <ogg/ogg.h>
#include <regex.h>
--- /dev/null
+/*
+ * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file opus_afh.c Audio format handler for ogg/opus files. */
+
+#include <ogg/ogg.h>
+#include <regex.h>
+
+#include "para.h"
+#include "afh.h"
+#include "error.h"
+#include "portable_io.h"
+#include "string.h"
+#include "opus_common.h"
+#include "ogg_afh_common.h"
+
+static const char* opus_suffixes[] = {"opus", NULL};
+
+static bool copy_if_tag_type(const char *tag, int taglen, const char *type,
+ char **p)
+{
+ char *q = key_value_copy(tag, taglen, type);
+ if (!q)
+ return false;
+ free(*p);
+ *p = q;
+ return true;
+}
+
+static int opus_get_comments(char *comments, int length,
+ struct taginfo *tags)
+{
+ char *p = comments, *end = comments + length;
+ int i;
+ uint32_t val, ntags;
+
+ /* min size of a opus header is 16 bytes */
+ if (length < 16)
+ return -E_OPUS_COMMENT;
+ if (memcmp(p, "OpusTags", 8) != 0)
+ return -E_OPUS_COMMENT;
+ p += 8;
+ val = read_u32(p);
+ p += 4;
+ if (p + val > end)
+ return -E_OPUS_COMMENT;
+ tags->comment = safe_strdup(p, val);
+ p += val;
+ ntags = read_u32(p);
+ p += 4;
+ if (p + ntags * 4 > end)
+ return -E_OPUS_COMMENT;
+ PARA_INFO_LOG("found %d tag(s)\n", ntags);
+ for (i = 0; i < ntags; i++, p += val) {
+ char *tag;
+
+ if (p + 4 > end)
+ return -E_OPUS_COMMENT;
+ val = read_u32(p);
+ p += 4;
+ if (p + val > end)
+ return -E_OPUS_COMMENT;
+ if (copy_if_tag_type(p, val, "author", &tags->artist))
+ continue;
+ if (copy_if_tag_type(p, val, "artist", &tags->artist))
+ continue;
+ if (copy_if_tag_type(p, val, "title", &tags->title))
+ continue;
+ if (copy_if_tag_type(p, val, "album", &tags->album))
+ continue;
+ if (copy_if_tag_type(p, val, "year", &tags->year))
+ continue;
+ if (copy_if_tag_type(p, val, "comment", &tags->comment))
+ continue;
+ tag = safe_strdup(p, val);
+ PARA_NOTICE_LOG("unrecognized tag: %s\n", tag);
+ free(tag);
+ }
+ return 1;
+}
+
+static int opus_packet_callback(ogg_packet *packet, int packet_num,
+ __a_unused int serial, struct afh_info *afhi,
+ void *private_data)
+{
+ int ret;
+ struct opus_header *oh = private_data;
+
+ if (packet_num == 0) {
+ ret = opus_parse_header((char *)packet->packet, packet->bytes, oh);
+ if (ret < 0)
+ return ret;
+ afhi->channels = oh->channels;
+ afhi->techinfo = make_message("header version %d, input sample rate: %dHz",
+ oh->version, oh->input_sample_rate);
+ /*
+ * The input sample rate is irrelevant for afhi->frequency as
+ * we always decode to 48kHz.
+ */
+ afhi->frequency = 48000;
+ return 1;
+ }
+ if (packet_num == 1) {
+ ret = opus_get_comments((char *)packet->packet, packet->bytes,
+ &afhi->tags);
+ if (ret < 0)
+ return ret;
+ return 0; /* header complete */
+ }
+ /* never reached */
+ assert(0);
+}
+
+static int opus_get_file_info(char *map, size_t numbytes, __a_unused int fd,
+ struct afh_info *afhi)
+{
+ int ret, ms;
+ struct opus_header oh = {.version = 0};
+
+ struct ogg_afh_callback_info opus_callback_info = {
+ .packet_callback = opus_packet_callback,
+ .private_data = &oh,
+ };
+ ret = ogg_get_file_info(map, numbytes, afhi, &opus_callback_info);
+ if (ret < 0)
+ return ret;
+ ret = (afhi->chunk_table[afhi->chunks_total] - afhi->chunk_table[0]) * 8; /* bits */
+ ms = tv2ms(&afhi->chunk_tv) * afhi->chunks_total;
+ afhi->bitrate = ret / ms;
+ return 1;
+}
+
+/**
+ * The init function of the ogg/opus audio format handler.
+ *
+ * \param afh Pointer to the struct to initialize.
+ */
+void opus_afh_init(struct audio_format_handler *afh)
+{
+ afh->get_file_info = opus_get_file_info,
+ afh->suffixes = opus_suffixes;
+}
--- /dev/null
+/* Copyright (C)2012 Xiph.Org Foundation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * \file opus_common.c Common functions of the opus decoder and audio format
+ * handler.
+ */
+
+#include <ogg/ogg.h>
+
+#include "para.h"
+#include "error.h"
+#include "opus_common.h"
+#include "portable_io.h"
+
+struct packet {
+ const char *data;
+ int maxlen;
+ int pos;
+};
+
+static int read_chars(struct packet *p, unsigned char *str, int nb_chars)
+{
+ int i;
+
+ if (p->pos > p->maxlen - nb_chars)
+ return 0;
+ for (i = 0; i < nb_chars; i++)
+ str[i] = p->data[p->pos++];
+ return 1;
+}
+
+static int read_uint32(struct packet *p, ogg_uint32_t *val)
+{
+ if (p->pos > p->maxlen - 4)
+ return 0;
+ *val = read_u32(p->data + p->pos);
+ p->pos += 4;
+ return 1;
+}
+
+static int read_uint16(struct packet *p, ogg_uint16_t *val)
+{
+ if (p->pos > p->maxlen - 2)
+ return 0;
+ *val = read_u16(p->data + p->pos);
+ p->pos += 2;
+ return 1;
+}
+
+/**
+ * Get metadata of an opus stream.
+ *
+ * This is called from both the audio format handler (which passes ogg packet
+ * 0) and from the decoder.
+ *
+ * \param packet Start of the packet.
+ * \param len Number of bytes.
+ * \param h Result.
+ *
+ * \return Standard.
+ */
+int opus_parse_header(const char *packet, int len, struct opus_header *h)
+{
+ int i;
+ char str[9];
+ struct packet p;
+ unsigned char ch, channel_mapping;
+ ogg_uint16_t shortval;
+
+ p.data = packet;
+ p.maxlen = len;
+ p.pos = 0;
+ str[8] = 0;
+ if (len < 19)
+ return -E_OPUS_HEADER;
+ read_chars(&p, (unsigned char*)str, 8);
+ if (memcmp(str, "OpusHead", 8) != 0)
+ return -E_OPUS_HEADER;
+
+ if (!read_chars(&p, &ch, 1))
+ return -E_OPUS_HEADER;
+ h->version = ch;
+ if((h->version & 240) != 0) /* Only major version 0 supported. */
+ return -E_OPUS_HEADER;
+
+ if (!read_chars(&p, &ch, 1))
+ return -E_OPUS_HEADER;
+ h->channels = ch;
+ if (h->channels == 0)
+ return -E_OPUS_HEADER;
+
+ if (!read_uint16(&p, &shortval))
+ return -E_OPUS_HEADER;
+ h->preskip = shortval;
+
+ if (!read_uint32(&p, &h->input_sample_rate))
+ return -E_OPUS_HEADER;
+
+ if (!read_uint16(&p, &shortval))
+ return -E_OPUS_HEADER;
+ h->gain = (short)shortval;
+
+ if (!read_chars(&p, &ch, 1))
+ return -E_OPUS_HEADER;
+ channel_mapping = ch;
+
+ if (channel_mapping != 0) {
+ if (!read_chars(&p, &ch, 1))
+ return -E_OPUS_HEADER;
+
+ if (ch < 1)
+ return -E_OPUS_HEADER;
+ h->nb_streams = ch;
+
+ if (!read_chars(&p, &ch, 1))
+ return -E_OPUS_HEADER;
+
+ if (ch > h->nb_streams || (ch + h->nb_streams) > 255)
+ return -E_OPUS_HEADER;
+ h->nb_coupled = ch;
+
+ /* Multi-stream support */
+ for (i = 0; i < h->channels; i++) {
+ if (!read_chars(&p, &h->stream_map[i], 1))
+ return -E_OPUS_HEADER;
+ if (h->stream_map[i] > (h->nb_streams + h->nb_coupled)
+ && h->stream_map[i] != 255)
+ return -E_OPUS_HEADER;
+ }
+ } else {
+ if (h->channels > 2)
+ return -E_OPUS_HEADER;
+ h->nb_streams = 1;
+ h->nb_coupled = h->channels > 1;
+ h->stream_map[0] = 0;
+ h->stream_map[1] = 1;
+ }
+ /*
+ * For version 0/1 we know there won't be any more data so reject any
+ * that have data past the end.
+ */
+ if ((h->version == 0 || h->version == 1) && p.pos != len)
+ return -E_OPUS_HEADER;
+ return 1;
+}
--- /dev/null
+/** Various bits stored in the header of an opus stream. */
+struct opus_header {
+ /** lower 4 bits of the version byte, must be 0. */
+ int version;
+ /** 1..255 */
+ int channels;
+ /** Number of bytes to skip from the beginning. */
+ int preskip;
+ /** Sample rate of the input stream, used by the audio format handler. */
+ ogg_uint32_t input_sample_rate;
+ /** In dB, should be zero whenever possible. */
+ int gain;
+ /** Number of logical streams (usually 1). */
+ int nb_streams;
+ /** Number of streams to decode as 2 channel streams. */
+ int nb_coupled;
+ /** Mapping from coded channels to output channels. */
+ unsigned char stream_map[255];
+};
+
+int opus_parse_header(const char *packet, int len, struct opus_header *h);
--- /dev/null
+/*
+ * Copyright (c) 2002-2007 Jean-Marc Valin
+ * Copyright (c) 2008 CSIRO
+ * Copyright (c) 2007-2012 Xiph.Org Foundation
+ * Copyright (C) 2012-2013 Andre Noll <maan@systemlinux.org>
+ *
+ * Licensed under the GPL v2. For licencing details see COPYING.
+ */
+
+/** \file opusdec_filter.c The ogg/opus decoder. */
+
+/* This file is based on opusdec.c, by Jean-Marc Valin, see below. */
+
+/*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
+ * IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <regex.h>
+#include <opus/opus.h>
+#include <opus/opus_multistream.h>
+#include <ogg/ogg.h>
+#include <math.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"
+#include "opus_common.h"
+
+/** 120ms at 48000 */
+#define MAX_FRAME_SIZE (960*6)
+
+struct opusdec_context {
+ OpusMSDecoder *st;
+ opus_int64 packet_count;
+ int total_links;
+ bool stream_init;
+ ogg_sync_state oy;
+ ogg_stream_state os;
+ ogg_page ogg_page;
+ bool eos;
+ int channels;
+ int preskip;
+ bool have_opus_stream;
+ ogg_int32_t opus_serialno;
+};
+
+static int opusdec_execute(struct btr_node *btrn, const char *cmd,
+ char **result)
+{
+ struct filter_node *fn = btr_context(btrn);
+ struct opusdec_context *ctx = fn->private_data;
+
+ return decoder_execute(cmd, 48000, ctx->channels, result);
+}
+
+static void opusdec_open(struct filter_node *fn)
+{
+ struct opusdec_context *ctx = para_calloc(sizeof(*ctx));
+
+ ogg_sync_init(&ctx->oy);
+ fn->private_data = ctx;
+}
+
+static void opusdec_close(struct filter_node *fn)
+{
+ struct opusdec_context *ctx = fn->private_data;
+
+ if (ctx->st) {
+ opus_multistream_decoder_destroy(ctx->st);
+ if (ctx->stream_init)
+ ogg_stream_clear(&ctx->os);
+ ogg_sync_clear(&ctx->oy);
+ }
+ free(ctx);
+ fn->private_data = NULL;
+}
+
+/* Process an Opus header and setup the opus decoder based on it. */
+static int opusdec_init(ogg_packet *op, struct opusdec_context *ctx)
+{
+ int ret;
+ struct opus_header header;
+
+ ctx->st = NULL;
+ ret = opus_parse_header((char *)op->packet, op->bytes, &header);
+ if (ret < 0)
+ return ret;
+ PARA_INFO_LOG("detected header v%d\n", header.version);
+ ctx->channels = header.channels;
+ ctx->preskip = header.preskip;
+ ctx->st = opus_multistream_decoder_create(48000, header.channels,
+ header.nb_streams, header.nb_coupled, header.stream_map, &ret);
+ if (ret != OPUS_OK || !ctx->st) {
+ PARA_ERROR_LOG("%s\n", opus_strerror(ret));
+ return -E_CREATE_OPUS_DECODER;
+ }
+ if (header.gain != 0) {
+ ret = opus_multistream_decoder_ctl(ctx->st,
+ OPUS_SET_GAIN(header.gain));
+ if (ret != OPUS_OK) {
+ PARA_ERROR_LOG("%s\n", opus_strerror(ret));
+ return -E_OPUS_SET_GAIN;
+ }
+ PARA_INFO_LOG("playback gain: %fdB\n", header.gain / 256.);
+ }
+ PARA_INFO_LOG("%d channel(s), 48KHz\n", ctx->channels);
+ return 1;
+}
+
+static void opusdec_add_output(short *pcm, int frames_available,
+ struct btr_node *btrn, struct opusdec_context *ctx)
+{
+ int tmp_skip, num_frames, bytes;
+
+ tmp_skip = PARA_MIN(ctx->preskip, frames_available);
+ ctx->preskip -= tmp_skip;
+ num_frames = frames_available - tmp_skip;
+ if (num_frames <= 0)
+ return;
+ bytes = sizeof(short) * num_frames * ctx->channels;
+
+ if (tmp_skip > 0) {
+ short *in = pcm + ctx->channels * tmp_skip;
+ short *out = para_malloc(bytes);
+ memcpy(out, in, bytes);
+ free(pcm);
+ pcm = out;
+ }
+ btr_add_output((char *)pcm, bytes, btrn);
+}
+
+/* returns > 1 if packet was decoded, 0 if it was ignored, negative on errors. */
+static int decode_packet(struct opusdec_context *ctx, ogg_packet *op,
+ struct btr_node *btrn)
+{
+ int ret;
+ short *output;
+
+ /*
+ * OggOpus streams are identified by a magic string in the initial
+ * stream header.
+ */
+ if (op->b_o_s && op->bytes >= 8 && !memcmp(op->packet, "OpusHead", 8)) {
+ if (!ctx->have_opus_stream) {
+ ctx->opus_serialno = ctx->os.serialno;
+ ctx->have_opus_stream = true;
+ ctx->packet_count = 0;
+ ctx->eos = false;
+ ctx->total_links++;
+ } else
+ PARA_NOTICE_LOG("ignoring opus stream %llu\n",
+ (long long unsigned)ctx->os.serialno);
+ }
+ if (!ctx->have_opus_stream || ctx->os.serialno != ctx->opus_serialno)
+ return 0;
+ /* If first packet in a logical stream, process the Opus header. */
+ if (ctx->packet_count == 0)
+ return opusdec_init(op, ctx);
+ if (ctx->packet_count == 1)
+ return 1;
+ /* don't care for anything except opus eos */
+ if (op->e_o_s && ctx->os.serialno == ctx->opus_serialno)
+ ctx->eos = true;
+ output = para_malloc(sizeof(short) * MAX_FRAME_SIZE * ctx->channels);
+ ret = opus_multistream_decode(ctx->st, (unsigned char *)op->packet,
+ op->bytes, output, MAX_FRAME_SIZE, 0);
+ if (ret < 0) {
+ PARA_ERROR_LOG("%s\n", opus_strerror(ret));
+ free(output);
+ return -E_OPUS_DECODE;
+ }
+ opusdec_add_output(output, ret, btrn, ctx);
+ return 1;
+}
+
+static void opusdec_pre_select(struct sched *s, struct task *t)
+{
+ struct filter_node *fn = container_of(t, struct filter_node, task);
+ struct btr_node *btrn = fn->btrn;
+ int ns;
+
+ ns = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
+ if (ns != 0)
+ return sched_min_delay(s);
+ sched_request_timeout_ms(100, s);
+}
+
+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)
+ goto out;
+ if (!ctx->stream_init) {
+ ogg_stream_init(&ctx->os, ogg_page_serialno(&ctx->ogg_page));
+ 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;
+ }
+ }
+out:
+ if (ret < 0)
+ btr_remove_node(&fn->btrn);
+ return ret;
+}
+
+/**
+ * The init function of the opusdec filter.
+ *
+ * \param f Pointer to the filter struct to initialize.
+ *
+ * \sa filter::init.
+ */
+void opusdec_filter_init(struct filter *f)
+{
+ 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;
+}
int spx_process_header(unsigned char *packet, long bytes,
struct spx_header_info *shi);
-int spx_ctl(void *state, int request, void *ptr);
*
* \return Standard.
*/
-int spx_ctl(void *state, int request, void *ptr)
+static int spx_ctl(void *state, int request, void *ptr)
{
int ret = speex_decoder_ctl(state, request, ptr);