[Asterisk-code-review] Binaural synthesis (confbridge): Adds two-channel capable OP... (asterisk[master])
Frank Haase
asteriskteam at digium.com
Fri Aug 12 11:33:28 CDT 2016
Frank Haase has uploaded a new change for review.
https://gerrit.asterisk.org/3526
Change subject: Binaural synthesis (confbridge): Adds two-channel capable OPUS (example).
......................................................................
Binaural synthesis (confbridge): Adds two-channel capable OPUS (example).
Adds speech codec OPUS with mono-channel and two-channel support.
Two-channel support is required for transmission of binaural-rendered signals.
ASTERISK-26292
Change-Id: I8e895c78f5e398da62d75b4a123ae5a3e189a8f4
---
A codecs/codec_opus.c
A codecs/ex_opus.h
M include/asterisk/interleaved_stereo.h
M main/Makefile
M main/codec_builtin.c
M res/res_format_attr_opus.c
6 files changed, 718 insertions(+), 6 deletions(-)
git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/26/3526/1
diff --git a/codecs/codec_opus.c b/codecs/codec_opus.c
new file mode 100644
index 0000000..2f1049c
--- /dev/null
+++ b/codecs/codec_opus.c
@@ -0,0 +1,653 @@
+/*
+ * * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2014, Lorenzo Miniero
+ * Copyright (C) 2016, Frank Haase
+ *
+ * Lorenzo Miniero <lorenzo at meetecho.com>
+ * Frank Haase <fra.haase at gmail.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Translate between signed linear and Opus (Open Codec), with stereo and mono support
+ *
+ * \author Lorenzo Miniero <lorenzo at meetecho.com>, Frank Haase <fra.haase at gmail.com>
+ *
+ *
+ * \ingroup codecs
+ *
+ * \extref The Opus library - http://opus-codec.org
+ *
+ */
+
+/*** MODULEINFO
+ <depend>opus</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include <opus/opus.h>
+#include <asterisk/opus.h>
+#include "asterisk/format.h"
+
+#include "asterisk/translate.h"
+#include "asterisk/module.h"
+#include "asterisk/cli.h"
+#include "asterisk/config.h"
+#include "asterisk/utils.h"
+#include "asterisk/linkedlists.h"
+
+#define BUFFER_SAMPLES 16000
+#define USE_FEC 0
+
+/* Sample frame data */
+#include "asterisk/slin.h"
+#include "ex_opus.h"
+
+static struct codec_usage {
+ int encoder_id;
+ int decoder_id;
+ int encoders;
+ int decoders;
+} usage;
+
+/* Private structures */
+struct opus_coder_pvt {
+ void *opus;
+ unsigned int sample_rate;
+ unsigned int stereo;
+ unsigned int multiplier;
+ unsigned int fec;
+ int id;
+ int16_t buf[BUFFER_SAMPLES];
+ int16_t out_buf[OPUS_FRAME_SIZE * 2];
+ unsigned int framesize;
+};
+
+static struct ast_frame *opus_sample(void)
+{
+ static struct ast_frame f = {
+ .frametype = AST_FRAME_VOICE,
+ .datalen = sizeof(ex_opus),
+ .samples = OPUS_FRAME_SIZE,
+ .mallocd = 0,
+ .offset = 0,
+ .src = __PRETTY_FUNCTION__,
+ .data.ptr = ex_opus,
+ };
+
+ f.subclass.format = ast_format_opus;
+
+ return &f;
+}
+
+static int valid_sample_rate(int rate)
+{
+ return rate == 8000
+ || rate == 12000
+ || rate == 16000
+ || rate == 24000
+ || rate == 48000;
+}
+
+static void set_bandwidth_fec_options(void *opus, unsigned int sample_rate, unsigned int fec)
+{
+ switch (sample_rate) {
+ case 8000:
+ opus_encoder_ctl(opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
+ break;
+ case 12000:
+ opus_encoder_ctl(opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_MEDIUMBAND));
+ break;
+ case 16000:
+ opus_encoder_ctl(opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND));
+ break;
+ case 24000:
+ opus_encoder_ctl(opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND));
+ break;
+ case 48000:
+ opus_encoder_ctl(opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND));
+ break;
+ }
+ opus_encoder_ctl(opus, OPUS_SET_INBAND_FEC(fec));
+}
+
+/* Translator callbacks */
+static int lintoopus_new(struct ast_trans_pvt *pvt)
+{
+ struct opus_coder_pvt *opvt = pvt->pvt;
+ int error;
+ if (!valid_sample_rate(pvt->t->src_codec.sample_rate)) {
+ ast_log(LOG_ERROR, "Invalid sampling rate. Valid sampling rates for opus are:\
+ 8000, 12000, 16000, 24000, 48000 hz.\n");
+ return -1;
+ }
+
+ opvt->sample_rate = pvt->t->src_codec.sample_rate;
+ opvt->multiplier = 48000 / pvt->t->src_codec.sample_rate;
+ opvt->fec = USE_FEC;
+
+ /* We will set opus to use stereo by default. If this is non
+ * stereo opus it will be changed at the first incoming frame.*/
+ opvt->stereo = 1;
+ error = 0;
+ opvt->opus = opus_encoder_create(pvt->t->src_codec.sample_rate, 2, OPUS_APPLICATION_VOIP, &error);
+ if (error != OPUS_OK) {
+ ast_log(LOG_ERROR, "Error creating the Opus encoder: %s\n", opus_strerror(error));
+ return -1;
+ }
+
+ opus_encoder_ctl(opvt->opus, OPUS_SET_FORCE_CHANNELS(2));
+ set_bandwidth_fec_options(opvt->opus, pvt->t->src_codec.sample_rate, opvt->fec);
+ opvt->framesize = pvt->t->src_codec.sample_rate / 50;
+ opvt->id = ast_atomic_fetchadd_int(&usage.encoder_id, 1) + 1;
+ ast_atomic_fetchadd_int(&usage.encoders, + 1);
+ ast_debug(3, "Created encoder #%d (%d -> opus)\n", opvt->id, pvt->t->src_codec.sample_rate);
+ return 0;
+}
+
+static int opustolin_new(struct ast_trans_pvt *pvt)
+{
+ struct opus_coder_pvt *opvt = pvt->pvt;
+ int error;
+ if (!valid_sample_rate(pvt->t->dst_codec.sample_rate)) {
+ return -1;
+ }
+
+ opvt->sample_rate = pvt->t->dst_codec.sample_rate;
+ opvt->multiplier = 48000 / pvt->t->dst_codec.sample_rate;
+ opvt->fec = USE_FEC;
+ opvt->stereo = 1;
+ error = 0;
+ opvt->opus = opus_decoder_create(pvt->t->dst_codec.sample_rate, 2, &error);
+ if (error != OPUS_OK) {
+ ast_log(LOG_ERROR, "Error creating the Opus decoder: %s\n", opus_strerror(error));
+ return -1;
+ }
+
+ opvt->id = ast_atomic_fetchadd_int(&usage.decoder_id, 1) + 1;
+ ast_atomic_fetchadd_int(&usage.decoders, +1);
+ ast_debug(3, "Created decoder #%d (opus -> %d)\n", opvt->id, pvt->t->dst_codec.sample_rate);
+ return 0;
+}
+
+static int lintoopus_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct opus_coder_pvt *opvt = pvt->pvt;
+ struct ast_format *format = pvt->f.subclass.format;
+ struct opus_attr *attr = ast_format_get_attribute_data(format);
+ int error;
+
+ if (attr != NULL) {
+ /* Check if we have to change form stereo to mono or vice versa. */
+ if (attr->stereo != opvt->stereo) {
+ if (attr->stereo == 0) {
+ opus_encoder_destroy(opvt->opus);
+ opvt->stereo = 0;
+ error = 0;
+ opvt->opus = opus_encoder_create(opvt->sample_rate, 1, OPUS_APPLICATION_VOIP, &error);
+ ast_debug(3, "Changing Opus encoder from stereo to mono.\n");
+ } else if (attr->stereo == 1) {
+ opus_encoder_destroy(opvt->opus);
+ opvt->stereo = 1;
+ opvt->opus = opus_encoder_create(opvt->sample_rate, 2, OPUS_APPLICATION_VOIP, &error);
+ opus_encoder_ctl(opvt->opus, OPUS_SET_FORCE_CHANNELS(2));
+ ast_debug(3, "Changing Opus encoder from mono to stereo.\n");
+ }
+ set_bandwidth_fec_options(opvt->opus, opvt->sample_rate, opvt->fec);
+ }
+ }
+ memcpy(opvt->buf + pvt->samples, f->data.ptr, f->datalen);
+ pvt->samples += f->samples;
+ return 0;
+}
+
+static struct ast_frame *lintoopus_frameout(struct ast_trans_pvt *pvt)
+{
+ struct opus_coder_pvt *opvt = pvt->pvt;
+ struct ast_frame *result = NULL;
+ struct ast_frame *last = NULL;
+ int out_samples = 0;
+ int k;
+ int i;
+ int interleaved;
+ interleaved = pvt->interleaved_stereo ? 2 : 1;
+
+ while (pvt->samples >= opvt->framesize * interleaved) {
+ int status = 0;
+ if (opvt->stereo == 1 && pvt->interleaved_stereo == 0) {
+ /* No stereo samples, but stereo output (put the same audio on both channels). */
+ opus_int16 stereobuf[opvt->framesize * 2];
+ i = 0;
+ k = 0;
+ for (i = 0; i < opvt->framesize * 2; i+=2) {
+ stereobuf[i] = opvt->buf[out_samples + k];
+ stereobuf[i + 1] = opvt->buf[out_samples + k];
+ k++;
+ }
+ status = opus_encode(opvt->opus, stereobuf, opvt->framesize, pvt->outbuf.uc, BUFFER_SAMPLES);
+ } else if ((opvt->stereo == 1 && pvt->interleaved_stereo == 1) || opvt->stereo == 0) {
+ /* Stereo source (interleaved format) and stereo output or everything mono. */
+ status = opus_encode(opvt->opus, opvt->buf, opvt->framesize, pvt->outbuf.uc, BUFFER_SAMPLES);
+ }
+ out_samples += opvt->framesize * interleaved;
+ pvt->samples -= opvt->framesize * interleaved;
+ if (status < 0) {
+ ast_log(LOG_ERROR, "Error encoding the Opus frame: %s\n", opus_strerror(status));
+ } else {
+ struct ast_frame *current;
+ current = ast_trans_frameout(pvt, status, opvt->multiplier * opvt->framesize);
+ if (!current) {
+ continue;
+ } else if (last) {
+ AST_LIST_NEXT(last, frame_list) = current;
+ } else {
+ result = current;
+ }
+ last = current;
+ }
+ }
+ /* Move the data at the end of the buffer to the front. */
+ if (out_samples) {
+ if (pvt->interleaved_stereo == 0)
+ memmove(opvt->buf, opvt->buf + out_samples, pvt->samples * 2);
+ else
+ memmove(opvt->buf, opvt->buf + out_samples, pvt->samples * 4);
+
+ }
+ return result;
+}
+
+static int opustolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
+{
+ struct opus_coder_pvt *opvt = pvt->pvt;
+ struct ast_format *format = f->subclass.format;
+ struct opus_attr *attr = ast_format_get_attribute_data(format);
+ int error = 0;
+ int samples = 0;
+ int i;
+ int k = 0;
+ if (attr != NULL) {
+ if (attr->stereo != opvt->stereo) {
+ opus_decoder_destroy(opvt->opus);
+ if (attr->stereo == 0) {
+ opvt->stereo = 0;
+ opvt->opus = opus_decoder_create(opvt->sample_rate, 1, &error);
+ ast_debug(3, "Changing Opus decoder from stereo to mono.\n");
+ } else {
+ opvt->stereo = 1;
+ opvt->opus = opus_decoder_create(opvt->sample_rate, 2, &error);
+ ast_debug(3, "Changing Opus decoder from mono to stereo.\n");
+ }
+ set_bandwidth_fec_options(opvt->opus, opvt->sample_rate, opvt->fec);
+ }
+ }
+ if (opvt->stereo == 0) {
+ if ((samples = opus_decode(opvt->opus, f->data.ptr, f->datalen, pvt->outbuf.i16, BUFFER_SAMPLES, opvt->fec)) < 0) {
+ ast_log(LOG_ERROR, "Error decoding the Opus frame: %s\n", opus_strerror(samples));
+ return -1;
+ }
+ } else {
+ /* If we have incoming stereo signals we will only copy one channel to lin. */
+ samples = opus_decode(opvt->opus, f->data.ptr, f->datalen, opvt->out_buf, BUFFER_SAMPLES, opvt->fec);
+ if (samples < 0)
+ ast_log(LOG_ERROR, "Error decoding the Opus stereo frame: %s\n", opus_strerror(samples));
+ for (i = 0; i < samples * 2; i += 2) {
+ pvt->outbuf.i16[k] = opvt->out_buf[i];
+ k++;
+ }
+ }
+ pvt->samples += samples;
+ pvt->datalen += samples * 2;
+ return 0;
+}
+
+static void lintoopus_destroy(struct ast_trans_pvt *arg)
+{
+ struct opus_coder_pvt *opvt = arg->pvt;
+
+ if (!opvt || !opvt->opus) {
+ return;
+ }
+
+ opus_encoder_destroy(opvt->opus);
+ opvt->opus = NULL;
+
+ ast_atomic_fetchadd_int(&usage.encoders, -1);
+
+ ast_debug(3, "Destroyed encoder #%d (%d->opus)\n", opvt->id, opvt->sample_rate);
+}
+
+static void opustolin_destroy(struct ast_trans_pvt *arg)
+{
+ struct opus_coder_pvt *opvt = arg->pvt;
+
+ if (!opvt || !opvt->opus) {
+ return;
+ }
+
+ opus_decoder_destroy(opvt->opus);
+ opvt->opus = NULL;
+
+ ast_atomic_fetchadd_int(&usage.decoders, -1);
+
+ ast_debug(3, "Destroyed decoder #%d (opus->%d)\n", opvt->id, opvt->sample_rate);
+}
+
+static char *handle_cli_opus_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct codec_usage copy;
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "opus show";
+ e->usage =
+ "Usage: opus show\n"
+ " Displays Opus encoder/decoder utilization.\n";
+ return NULL;
+ case CLI_GENERATE:
+ return NULL;
+ }
+
+ if (a->argc != 2) {
+ return CLI_SHOWUSAGE;
+ }
+
+ copy = usage;
+
+ ast_cli(a->fd, "%d/%d encoders/decoders are in use.\n", copy.encoders, copy.decoders);
+
+ return CLI_SUCCESS;
+}
+
+/* Translators */
+static struct ast_translator opustolin = {
+ .name = "opustolin",
+ .src_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .dst_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 8000,
+ },
+ .format = "slin",
+ .newpvt = opustolin_new,
+ .framein = opustolin_framein,
+ .destroy = opustolin_destroy,
+ .sample = opus_sample,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator lintoopus = {
+ .name = "lintoopus",
+ .src_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 8000,
+ },
+ .dst_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .format = "opus",
+ .newpvt = lintoopus_new,
+ .framein = lintoopus_framein,
+ .frameout = lintoopus_frameout,
+ .destroy = lintoopus_destroy,
+ .sample = slin8_sample,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator opustolin12 = {
+ .name = "opustolin12",
+ .src_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .dst_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 12000,
+ },
+ .format = "slin12",
+ .newpvt = opustolin_new,
+ .framein = opustolin_framein,
+ .destroy = opustolin_destroy,
+ .sample = opus_sample,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator lin12toopus = {
+ .name = "lin12toopus",
+ .src_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 12000,
+ },
+ .dst_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .format = "opus",
+ .newpvt = lintoopus_new,
+ .framein = lintoopus_framein,
+ .frameout = lintoopus_frameout,
+ .destroy = lintoopus_destroy,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator opustolin16 = {
+ .name = "opustolin16",
+ .src_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .dst_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 16000,
+ },
+ .format = "slin16",
+ .newpvt = opustolin_new,
+ .framein = opustolin_framein,
+ .destroy = opustolin_destroy,
+ .sample = opus_sample,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator lin16toopus = {
+ .name = "lin16toopus",
+ .src_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 16000,
+ },
+ .dst_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .format = "opus",
+ .newpvt = lintoopus_new,
+ .framein = lintoopus_framein,
+ .frameout = lintoopus_frameout,
+ .destroy = lintoopus_destroy,
+ .sample = slin16_sample,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator opustolin24 = {
+ .name = "opustolin24",
+ .src_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .dst_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 24000,
+ },
+ .format = "slin24",
+ .newpvt = opustolin_new,
+ .framein = opustolin_framein,
+ .destroy = opustolin_destroy,
+ .sample = opus_sample,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator lin24toopus = {
+ .name = "lin24toopus",
+ .src_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 24000,
+ },
+ .dst_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .format = "opus",
+ .newpvt = lintoopus_new,
+ .framein = lintoopus_framein,
+ .frameout = lintoopus_frameout,
+ .destroy = lintoopus_destroy,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator opustolin48 = {
+ .name = "opustolin48",
+ .src_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .dst_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .format = "slin48",
+ .newpvt = opustolin_new,
+ .framein = opustolin_framein,
+ .destroy = opustolin_destroy,
+ .sample = opus_sample,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_translator lin48toopus = {
+ .name = "lin48toopus",
+ .src_codec = {
+ .name = "slin",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .dst_codec = {
+ .name = "opus",
+ .type = AST_MEDIA_TYPE_AUDIO,
+ .sample_rate = 48000,
+ },
+ .format = "opus",
+ .newpvt = lintoopus_new,
+ .framein = lintoopus_framein,
+ .frameout = lintoopus_frameout,
+ .destroy = lintoopus_destroy,
+ .desc_size = sizeof(struct opus_coder_pvt),
+ .buffer_samples = BUFFER_SAMPLES,
+ .buf_size = BUFFER_SAMPLES * 2,
+};
+
+static struct ast_cli_entry cli[] = {
+ AST_CLI_DEFINE(handle_cli_opus_show, "Display Opus codec utilization.")
+};
+
+static int reload(void)
+{
+ /* Reload does nothing */
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ int res;
+
+ res = ast_unregister_translator(&opustolin);
+ res |= ast_unregister_translator(&lintoopus);
+ res |= ast_unregister_translator(&opustolin12);
+ res |= ast_unregister_translator(&lin12toopus);
+ res |= ast_unregister_translator(&opustolin16);
+ res |= ast_unregister_translator(&lin16toopus);
+ res |= ast_unregister_translator(&opustolin24);
+ res |= ast_unregister_translator(&lin24toopus);
+ res |= ast_unregister_translator(&opustolin48);
+ res |= ast_unregister_translator(&lin48toopus);
+
+ ast_cli_unregister_multiple(cli, ARRAY_LEN(cli));
+
+ return res;
+}
+
+static int load_module(void)
+{
+ int res;
+
+ res = ast_register_translator(&opustolin);
+ res |= ast_register_translator(&lintoopus);
+ res |= ast_register_translator(&opustolin12);
+ res |= ast_register_translator(&lin12toopus);
+ res |= ast_register_translator(&opustolin16);
+ res |= ast_register_translator(&lin16toopus);
+ res |= ast_register_translator(&opustolin24);
+ res |= ast_register_translator(&lin24toopus);
+ res |= ast_register_translator(&opustolin48);
+ res |= ast_register_translator(&lin48toopus);
+
+ ast_cli_register_multiple(cli, ARRAY_LEN(cli));
+
+ return res;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Opus Coder/Decoder",
+ .load = load_module,
+ .unload = unload_module,
+ .reload = reload,
+ );
+
diff --git a/codecs/ex_opus.h b/codecs/ex_opus.h
new file mode 100644
index 0000000..e98318f
--- /dev/null
+++ b/codecs/ex_opus.h
@@ -0,0 +1,38 @@
+/*! \file
+ * \brief 8-bit data
+ *
+ * Copyright (C) 2014, Lorenzo Miniero
+ *
+ * Distributed under the terms of the GNU General Public License
+ *
+ */
+
+#ifndef EX_OPUS_H
+#define EX_OPUS_H
+
+#define OPUS_FRAME_SIZE 960
+#define OPUS_DEFAULT_SAMPLE_RATE 48000
+
+/* Opus, a 20ms sample */
+uint8_t ex_opus[] = {
+ 0x4b, 0x41, 0x25, 0x0b, 0xe4, 0x55, 0xc6, 0x74,
+ 0xda, 0xbb, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+};
+
+struct opus_attr {
+ unsigned int maxbitrate; /* Default 64-128 kb/s for FB stereo music */
+ unsigned int maxplayrate /* Default 48000 */;
+ unsigned int minptime; /* Default 3, but it's 10 in format.c */
+ unsigned int stereo; /* Default 0 */
+ unsigned int cbr; /* Default 0 */
+ unsigned int fec; /* Default 0 */
+ unsigned int dtx; /* Default 0 */
+ unsigned int spropmaxcapturerate; /* Default 48000 */
+ unsigned int spropstereo; /* Default 0 */
+};
+
+#endif
diff --git a/include/asterisk/interleaved_stereo.h b/include/asterisk/interleaved_stereo.h
index 0a893b7..69dfcd9 100644
--- a/include/asterisk/interleaved_stereo.h
+++ b/include/asterisk/interleaved_stereo.h
@@ -4,11 +4,23 @@
*/
#include "asterisk/format.h"
+#include "../../codecs/ex_opus.h"
+
+static int opus_codec(const struct ast_format *format, int *sample_rate) {
+ struct opus_attr *attr = ast_format_get_attribute_data(format);
+ if (attr != NULL) {
+ if (attr->stereo == 1) {
+ *sample_rate = attr->maxplayrate;
+ return 1;
+ }
+ }
+ return 0;
+}
static int interleaved_stereo(const struct ast_format *format, int *sample_rate) {
- if (strcmp("PLACEHOLDER_STEREO_CODEC", ast_format_get_name(format)) == 0) {
- return 1;
+ if (strcmp("opus", ast_format_get_name(format)) == 0) {
+ return opus_codec(format, sample_rate);
}
return 0;
}
diff --git a/main/Makefile b/main/Makefile
index 729ae9c..0e00953 100644
--- a/main/Makefile
+++ b/main/Makefile
@@ -45,6 +45,7 @@
AST_LIBS+=$(CRYPT_LIB)
AST_LIBS+=$(AST_CLANG_BLOCKS_LIBS)
AST_LIBS+=$(RT_LIB)
+AST_LIBS+=-lopus
ifneq ($(findstring $(OSARCH), linux-gnu uclinux linux-uclibc linux-musl kfreebsd-gnu),)
ifneq ($(findstring LOADABLE_MODULES,$(MENUSELECT_CFLAGS)),)
diff --git a/main/codec_builtin.c b/main/codec_builtin.c
index 1514798..eda1aaa 100644
--- a/main/codec_builtin.c
+++ b/main/codec_builtin.c
@@ -38,6 +38,7 @@
#include "asterisk/format.h"
#include "asterisk/format_cache.h"
#include "asterisk/frame.h"
+#include <opus/opus.h>
int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name,
struct ast_module *mod);
@@ -88,6 +89,11 @@
}
return samples;
+}
+
+static int opus_samples(struct ast_frame *frame)
+{
+ return opus_packet_get_nb_samples(frame->data.ptr, frame->datalen, 48000);
}
static int g723_length(unsigned int samples)
@@ -716,6 +722,7 @@
.maximum_ms = 60,
.default_ms = 20,
.minimum_bytes = 10,
+ .samples_count = opus_samples,
};
static struct ast_codec jpeg = {
diff --git a/res/res_format_attr_opus.c b/res/res_format_attr_opus.c
index 4c09bef..1ef2c7f 100644
--- a/res/res_format_attr_opus.c
+++ b/res/res_format_attr_opus.c
@@ -75,7 +75,7 @@
static int opus_clone(const struct ast_format *src, struct ast_format *dst)
{
struct opus_attr *original = ast_format_get_attribute_data(src);
- struct opus_attr *attr = ast_malloc(sizeof(*attr));
+ struct opus_attr *attr = ast_malloc(sizeof(struct opus_attr));
if (!attr) {
return -1;
@@ -281,9 +281,10 @@
attr_res->cbr = attr1->cbr || attr2->cbr ? 1 : 0;
attr_res->spropstereo = attr1->spropstereo || attr2->spropstereo ? 1 : 0;
- /* Only do stereo if both sides want it. If a peer specifically requests not
- * to receive stereo signals, it may be a waste of bandwidth. */
- attr_res->stereo = attr1->stereo && attr2->stereo ? 1 : 0;
+ if (attr1->stereo || attr2->stereo) {
+ attr_res->stereo = 1;
+ attr_res->spropstereo = 1;
+ }
attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate);
attr_res->spropmaxcapturerate = MIN(attr1->spropmaxcapturerate, attr2->spropmaxcapturerate);
--
To view, visit https://gerrit.asterisk.org/3526
To unsubscribe, visit https://gerrit.asterisk.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: I8e895c78f5e398da62d75b4a123ae5a3e189a8f4
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Frank Haase <fra.haase at googlemail.com>
More information about the asterisk-code-review
mailing list