[Pkg-voip-commits] [asterisk] 01/01: Update opus patch, add libopusfile-dev build-dep

Bernhard Schmidt berni at moszumanska.debian.org
Wed Nov 2 12:12:04 UTC 2016


This is an automated email from the git hooks/post-receive script.

berni pushed a commit to branch master
in repository asterisk.

commit 6400f660ec6b62b68b7df84e2df588552b7c1ad0
Author: Bernhard Schmidt <berni at debian.org>
Date:   Wed Nov 2 12:53:23 2016 +0100

    Update opus patch, add libopusfile-dev build-dep
    
    Asterisk 13.12.1 now includes codec_opus which is a stub to the Digium
    OPUS implementation only distributed in binary form. See
    http://blogs.digium.com/2016/09/30/opus-in-asterisk/
    
    This patch builds a codec_opus_open_source.so
---
 debian/control            |    1 +
 debian/patches/opus.patch | 1322 +++++++++++++++++++++++++++++++++++++++------
 debian/rules              |    1 +
 3 files changed, 1168 insertions(+), 156 deletions(-)

diff --git a/debian/control b/debian/control
index 3b6ea68..6056e65 100644
--- a/debian/control
+++ b/debian/control
@@ -43,6 +43,7 @@ Build-Depends:
  libopencore-amrwb-dev,
  libopenr2-dev [linux-any],
  libopus-dev,
+ libopusfile-dev,
  libpjproject-dev,
  libpopt-dev,
  libpq-dev,
diff --git a/debian/patches/opus.patch b/debian/patches/opus.patch
index 980f6c5..2397bab 100644
--- a/debian/patches/opus.patch
+++ b/debian/patches/opus.patch
@@ -1,62 +1,13 @@
 Description: Add Opus codec module supporting transcoding
-Origin: https://github.com/seanbright/asterisk-opus
+Origin: https://github.com/traud/asterisk-opus
 Author: Lorenzo Miniero <lorenzo at meetecho.com>
 Forwarded: yes
 Bug-Debian: http://bugs.debian.org/786972
-Last-Update: 2016-04-02
+Last-Update: 2016-11-02
 
---- a/main/Makefile
-+++ b/main/Makefile
-@@ -43,6 +43,7 @@
- AST_LIBS+=$(URIPARSER_LIB)
- AST_LIBS+=$(UUID_LIB)
- AST_LIBS+=$(CRYPT_LIB)
-+AST_LIBS+=$(OPUS_LIB)
- AST_LIBS+=$(AST_CLANG_BLOCKS_LIBS)
- AST_LIBS+=$(RT_LIB)
- AST_LIBS+=$(SYSTEMD_LIB)
-@@ -163,6 +164,7 @@
- bucket.o: _ASTCFLAGS+=$(URIPARSER_INCLUDE)
- crypt.o: _ASTCFLAGS+=$(CRYPT_INCLUDE)
- uuid.o: _ASTCFLAGS+=$(UUID_INCLUDE)
-+codec_builtin.o: _ASTCFLAGS+=$(OPUS_INCLUDE)
- 
- ifneq ($(findstring ENABLE_UPLOADS,$(MENUSELECT_CFLAGS)),)
- http.o: _ASTCFLAGS+=$(GMIME_INCLUDE)
---- a/main/codec_builtin.c
-+++ b/main/codec_builtin.c
-@@ -41,6 +41,8 @@
- int __ast_codec_register_with_format(struct ast_codec *codec, const char *format_name,
- 	struct ast_module *mod);
- 
-+#include <opus/opus.h>
-+
- enum frame_type {
- 	TYPE_HIGH,     /* 0x0 */
- 	TYPE_LOW,      /* 0x1 */
-@@ -701,6 +703,11 @@
- 	.get_length = g719_length,
- };
- 
-+static int opus_samples(struct ast_frame *frame)
-+{
-+	return opus_packet_get_nb_samples(frame->data.ptr, frame->datalen, 48000);
-+}
-+
- static struct ast_codec opus = {
- 	.name = "opus",
- 	.description = "Opus Codec",
-@@ -710,6 +717,7 @@
- 	.maximum_ms = 60,
- 	.default_ms = 20,
- 	.minimum_bytes = 10,
-+	.samples_count = opus_samples,
- };
- 
- static struct ast_codec jpeg = {
 --- /dev/null
 +++ b/codecs/ex_opus.h
-@@ -0,0 +1,35 @@
+@@ -0,0 +1,38 @@
 +/*! \file
 + * \brief 8-bit data
 + *
@@ -66,6 +17,9 @@ Last-Update: 2016-04-02
 + *
 + */
 +
++#include "asterisk/format_cache.h"      /* for ast_format_opus */
++#include "asterisk/frame.h"             /* for ast_frame, etc */
++
 +/* Opus, a 20ms sample */
 +static uint8_t ex_opus[] = {
 +	0x4b, 0x41, 0x25, 0x0b, 0xe4, 0x55, 0xc6, 0x74,
@@ -81,7 +35,7 @@ Last-Update: 2016-04-02
 +	static struct ast_frame f = {
 +		.frametype = AST_FRAME_VOICE,
 +		.datalen = sizeof(ex_opus),
-+		.samples = 960,	// ARRAY_LEN(ex_opus),
++		.samples = OPUS_SAMPLES,
 +		.mallocd = 0,
 +		.offset = 0,
 +		.src = __PRETTY_FUNCTION__,
@@ -93,8 +47,8 @@ Last-Update: 2016-04-02
 +	return &f;
 +}
 --- /dev/null
-+++ b/codecs/codec_opus.c
-@@ -0,0 +1,604 @@
++++ b/codecs/codec_opus_open_source.c
+@@ -0,0 +1,853 @@
 +/*
 + * Asterisk -- An open source telephony toolkit.
 + *
@@ -123,32 +77,43 @@ Last-Update: 2016-04-02
 + *
 + * \ingroup codecs
 + *
-+ * \extref The Opus library - http://opus-codec.org
++ * \extref http://www.opus-codec.org/docs/html_api-1.1.0/
 + *
 + */
 +
 +/*** MODULEINFO
 +	 <depend>opus</depend>
-+	 <support_level>core</support_level>
++	 <conflict>codec_opus</conflict>
++	 <defaultenabled>yes</defaultenabled>
 +***/
 +
 +#include "asterisk.h"
 +
++#if defined(ASTERISK_REGISTER_FILE)
++ASTERISK_REGISTER_FILE()
++#elif defined(ASTERISK_FILE_VERSION)
 +ASTERISK_FILE_VERSION(__FILE__, "$Revision: $")
++#endif
++
++#include "asterisk/astobj2.h"           /* for ao2_ref */
++#include "asterisk/cli.h"               /* for ast_cli_entry, ast_cli, etc */
++#include "asterisk/codec.h"             /* for ast_codec_get */
++#include "asterisk/format.h"            /* for ast_format_get_attribute_data */
++#include "asterisk/frame.h"             /* for ast_frame, etc */
++#include "asterisk/linkedlists.h"       /* for AST_LIST_NEXT, etc */
++#include "asterisk/lock.h"              /* for ast_atomic_fetchadd_int */
++#include "asterisk/logger.h"            /* for ast_log, LOG_ERROR, etc */
++#include "asterisk/module.h"
++#include "asterisk/translate.h"         /* for ast_trans_pvt, etc */
++#include "asterisk/utils.h"             /* for ARRAY_LEN */
 +
 +#include <opus/opus.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	8000
-+#define	OPUS_SAMPLES	160
++#include "asterisk/opus.h"              /* for CODEC_OPUS_DEFAULT_* */
 +
-+#define USE_FEC		0
++#define	BUFFER_SAMPLES	5760
++#define	MAX_CHANNELS	2
++#define	OPUS_SAMPLES	960
 +
 +/* Sample frame data */
 +#include "asterisk/slin.h"
@@ -161,61 +126,81 @@ Last-Update: 2016-04-02
 +	int decoders;
 +} usage;
 +
++/*
++ * Stores the function pointer 'sample_count' of the cached ast_codec
++ * before this module was loaded. Allows to restore this previous
++ * function pointer, when this module in unloaded.
++ */
++static struct ast_codec *opus_codec; /* codec of the cached format */
++static int (*opus_samples_previous)(struct ast_frame *frame);
++
 +/* Private structures */
 +struct opus_coder_pvt {
 +	void *opus;	/* May be encoder or decoder */
 +	int sampling_rate;
 +	int multiplier;
-+	int fec;
 +	int id;
-+	int16_t buf[BUFFER_SAMPLES];	/* FIXME */
++	int16_t buf[BUFFER_SAMPLES];
 +	int framesize;
++	int inited;
++	int channels;
++	int decode_fec_incoming;
++	int previous_lost;
 +};
 +
-+static int valid_sampling_rate(int rate)
-+{
-+	return rate == 8000
-+		|| rate == 12000
-+		|| rate == 16000
-+		|| rate == 24000
-+		|| rate == 48000;
-+}
++struct opus_attr {
++	unsigned int maxbitrate;
++	unsigned int maxplayrate;
++	unsigned int unused; /* was minptime */
++	unsigned int stereo;
++	unsigned int cbr;
++	unsigned int fec;
++	unsigned int dtx;
++	unsigned int spropmaxcapturerate; /* FIXME: not utilised, yet */
++	unsigned int spropstereo; /* FIXME: currently, we are just mono */
++};
 +
 +/* Helper methods */
 +static int opus_encoder_construct(struct ast_trans_pvt *pvt, int sampling_rate)
 +{
 +	struct opus_coder_pvt *opvt = pvt->pvt;
-+	int error = 0;
-+
-+	if (!valid_sampling_rate(sampling_rate)) {
++	struct opus_attr *attr = pvt->explicit_dst ? ast_format_get_attribute_data(pvt->explicit_dst) : NULL;
++	const opus_int32 bitrate = attr ? attr->maxbitrate  : CODEC_OPUS_DEFAULT_BITRATE;
++	const int maxplayrate    = attr ? attr->maxplayrate : CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE;
++	const int channels       = attr ? attr->stereo + 1  : CODEC_OPUS_DEFAULT_STEREO + 1;
++	const opus_int32 vbr     = attr ? !(attr->cbr)      : !CODEC_OPUS_DEFAULT_CBR;
++	const opus_int32 fec     = attr ? attr->fec         : CODEC_OPUS_DEFAULT_FEC;
++	const opus_int32 dtx     = attr ? attr->dtx         : CODEC_OPUS_DEFAULT_DTX;
++	const int application    = OPUS_APPLICATION_VOIP;
++	int status = 0;
++
++	opvt->opus = opus_encoder_create(sampling_rate, channels, application, &status);
++
++	if (status != OPUS_OK) {
++		ast_log(LOG_ERROR, "Error creating the Opus encoder: %s\n", opus_strerror(status));
 +		return -1;
 +	}
 +
-+	opvt->sampling_rate = sampling_rate;
-+	opvt->multiplier = 48000/sampling_rate;
-+	opvt->fec = USE_FEC;
-+
-+	opvt->opus = opus_encoder_create(sampling_rate, 1, OPUS_APPLICATION_VOIP, &error);
++	if (sampling_rate <= 8000 || maxplayrate <= 8000) {
++		status = opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
++	} else if (sampling_rate <= 12000 || maxplayrate <= 12000) {
++		status = opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_MEDIUMBAND));
++	} else if (sampling_rate <= 16000 || maxplayrate <= 16000) {
++		status = opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND));
++	} else if (sampling_rate <= 24000 || maxplayrate <= 24000) {
++		status = opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND));
++	} /* else we use the default: OPUS_BANDWIDTH_FULLBAND */
++
++	if (0 < bitrate && bitrate != 510000) {
++		status = opus_encoder_ctl(opvt->opus, OPUS_SET_BITRATE(bitrate));
++	} /* else we use the default: OPUS_AUTO */
++	status = opus_encoder_ctl(opvt->opus, OPUS_SET_VBR(vbr));
++	status = opus_encoder_ctl(opvt->opus, OPUS_SET_INBAND_FEC(fec));
++	status = opus_encoder_ctl(opvt->opus, OPUS_SET_DTX(dtx));
 +
-+	if (error != OPUS_OK) {
-+		ast_log(LOG_ERROR, "Error creating the Opus encoder: %s\n", opus_strerror(error));
-+		return -1;
-+	}
-+
-+	if (sampling_rate == 8000) {
-+		opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_NARROWBAND));
-+	} else if (sampling_rate == 12000) {
-+		opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_MEDIUMBAND));
-+	} else if (sampling_rate == 16000) {
-+		opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND));
-+	} else if (sampling_rate == 24000) {
-+		opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_SUPERWIDEBAND));
-+	} else if (sampling_rate == 48000) {
-+		opus_encoder_ctl(opvt->opus, OPUS_SET_MAX_BANDWIDTH(OPUS_BANDWIDTH_FULLBAND));
-+	}
-+
-+	opus_encoder_ctl(opvt->opus, OPUS_SET_INBAND_FEC(opvt->fec));
-+	opvt->framesize = sampling_rate/50;
++	opvt->sampling_rate = sampling_rate;
++	opvt->multiplier = 48000 / sampling_rate;
++	opvt->framesize = sampling_rate / 50;
 +	opvt->id = ast_atomic_fetchadd_int(&usage.encoder_id, 1) + 1;
 +
 +	ast_atomic_fetchadd_int(&usage.encoders, +1);
@@ -225,20 +210,17 @@ Last-Update: 2016-04-02
 +	return 0;
 +}
 +
-+static int opus_decoder_construct(struct ast_trans_pvt *pvt, int sampling_rate)
++static int opus_decoder_construct(struct ast_trans_pvt *pvt, struct ast_frame *f)
 +{
 +	struct opus_coder_pvt *opvt = pvt->pvt;
++	/* struct opus_attr *attr = ast_format_get_attribute_data(f->subclass.format); */
 +	int error = 0;
 +
-+	if (!valid_sampling_rate(sampling_rate)) {
-+		return -1;
-+	}
-+
-+	opvt->sampling_rate = sampling_rate;
-+	opvt->multiplier = 48000/sampling_rate;
-+	opvt->fec = USE_FEC;	/* FIXME: should be triggered by chan_sip */
++	opvt->sampling_rate = pvt->t->dst_codec.sample_rate;
++	opvt->multiplier = 48000 / opvt->sampling_rate;
++	opvt->channels = /* attr ? attr->spropstereo + 1 :*/ 1; /* FIXME */;
 +
-+	opvt->opus = opus_decoder_create(sampling_rate, 1, &error);
++	opvt->opus = opus_decoder_create(opvt->sampling_rate, opvt->channels, &error);
 +
 +	if (error != OPUS_OK) {
 +		ast_log(LOG_ERROR, "Error creating the Opus decoder: %s\n", opus_strerror(error));
@@ -249,7 +231,7 @@ Last-Update: 2016-04-02
 +
 +	ast_atomic_fetchadd_int(&usage.decoders, +1);
 +
-+	ast_debug(3, "Created decoder #%d (opus -> %d)\n", opvt->id, sampling_rate);
++	ast_debug(3, "Created decoder #%d (opus -> %d)\n", opvt->id, opvt->sampling_rate);
 +
 +	return 0;
 +}
@@ -262,7 +244,12 @@ Last-Update: 2016-04-02
 +
 +static int opustolin_new(struct ast_trans_pvt *pvt)
 +{
-+	return opus_decoder_construct(pvt, pvt->t->dst_codec.sample_rate);
++	struct opus_coder_pvt *opvt = pvt->pvt;
++
++	opvt->previous_lost = 0; /* we are new and have not lost anything */
++	opvt->inited = 0; /* we do not know the "sprop" values, yet */
++
++	return 0;
 +}
 +
 +static int lintoopus_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
@@ -293,12 +280,6 @@ Last-Update: 2016-04-02
 +			pvt->outbuf.uc,
 +			BUFFER_SAMPLES);
 +
-+		ast_debug(3, "[Encoder #%d (%d)] %d samples, %d bytes\n",
-+				  opvt->id,
-+				  opvt->sampling_rate,
-+				  opvt->framesize,
-+				  opvt->framesize * 2);
-+
 +		samples += opvt->framesize;
 +		pvt->samples -= opvt->framesize;
 +
@@ -307,13 +288,7 @@ Last-Update: 2016-04-02
 +		} else {
 +			struct ast_frame *current = ast_trans_frameout(pvt,
 +				status,
-+				opvt->multiplier * opvt->framesize);
-+
-+			ast_debug(3, "[Encoder #%d (%d)]   >> Got %d samples, %d bytes\n",
-+					  opvt->id,
-+					  opvt->sampling_rate,
-+					  opvt->multiplier * opvt->framesize,
-+					  status);
++				OPUS_SAMPLES);
 +
 +			if (!current) {
 +				continue;
@@ -337,30 +312,229 @@ Last-Update: 2016-04-02
 +static int opustolin_framein(struct ast_trans_pvt *pvt, struct ast_frame *f)
 +{
 +	struct opus_coder_pvt *opvt = pvt->pvt;
-+	int samples = 0;
++	int decode_fec;
++	int frame_size;
++	opus_int16 *dst;
++	opus_int32 len;
++	unsigned char *src;
++	int status;
++
++	if (!opvt->inited && f->datalen == 0) {
++		return 0; /* we cannot start without data */
++	} else if (!opvt->inited) { /* 0 < f->datalen */
++		status = opus_decoder_construct(pvt, f);
++		opvt->inited = 1;
++		if (status) {
++			return status;
++		}
++	}
 +
-+	/* Decode */
-+	ast_debug(3, "[Decoder #%d (%d)] %d samples, %d bytes\n",
-+		opvt->id,
-+		opvt->sampling_rate,
-+		f->samples,
-+		f->datalen);
++	/*
++	 * When we get a frame indicator (ast_null_frame), format is NULL. Because FEC
++	 * status can change any time (SDP re-negotiation), we save again and again.
++	 */
++	if (f->subclass.format) {
++		struct opus_attr *attr = ast_format_get_attribute_data(f->subclass.format);
 +
-+	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;
++		if (attr) {
++			opvt->decode_fec_incoming = attr->fec;
++		}
++	}
++	decode_fec = opvt->decode_fec_incoming;
++
++	/*
++	 * The Opus Codec, actually its library allows
++	 * - Forward-Error Correction (FEC), and
++	 * - native Packet-Loss Concealment (PLC).
++	 * The sender might include FEC. If there is no FEC, because it was not send
++	 * or the FEC data got lost, the API of the Opus library does PLC instead.
++	 * Therefore we have three boolean variables:
++	 * - current frame got lost: f->datalen == 0,
++	 * - previous frame got lost: opvt->previous_lost, and
++	 * - FEC negotiated on SDP layer: decode_fec.
++	 * Now, we go through all cases. Because some cases use the same source code
++	 * we have less than 8 (2^3) cases.
++	 *
++	 * Some notes on the coding style of this section:
++	 * This code section is passed for each incoming frame, normally every
++	 * 20 milliseconds. For each channel, this code is passed individually.
++	 * Therefore, this code should be as performant as possible. On the other
++	 * hand, PLC plus FEC is complicated. Therefore, code readability is one
++	 * prerequisite to understand, debug, and review this code section. Because
++	 * we do have optimising compilers, we are able to sacrify optimised code
++	 * for code readability. If you find an error or unnecessary calculation
++	 * which is not optimised = removed by your compiler, please, create an
++	 * issue on <https://github.com/traud/asterisk-opus/issues>. I am just
++	 * a human and human do mistakes. However, humans love to learn.
++	 *
++	 * Source-code examples are
++	 * - <https://git.xiph.org/?p=opus.git;a=history;f=src/opus_demo.c>,
++	 * - <https://freeswitch.org/stash/projects/FS/repos/freeswitch/browse/src/mod/codecs/mod_opus/mod_opus.c>
++	 * and the official mailing list itself:
++	 * <https://www.google.de/search?q=site:lists.xiph.org+opus>.
++	 */
++
++	/* Case 1 and 2 */
++	if (f->datalen == 0 && opvt->previous_lost) {
++		/*
++		 * If this frame and the previous frame got lost, we do not have any
++		 * data for FEC. Therefore, we go for PLC on the previous frame. However,
++		 * the next frame could include FEC for the currently lost frame.
++		 * Therefore, we "wait" for the next frame to fix the current frame.
++		 */
++		decode_fec = 0; /* = do PLC */
++		opus_decoder_ctl(opvt->opus, OPUS_GET_LAST_PACKET_DURATION(&frame_size));
++		dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels);
++		len = 0;
++		src = NULL;
++		status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec);
++		if (status < 0) {
++			ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
++		} else {
++			pvt->samples += status;
++			pvt->datalen += status * opvt->channels * sizeof(int16_t);
++		}
++		/*
++		 * Save the state of the current frame, whether it is lost = "wait".
++		 * That way, we are able to decide whether to do FEC next time.
++		 */
++		opvt->previous_lost = (f->datalen == 0 || status < 0);
++		return 0;
 +	}
 +
-+	pvt->samples += samples;
-+	pvt->datalen += samples * 2;
++	/* Case 3 */
++	if (f->datalen == 0 && !decode_fec) { /* !opvt->previous_lost */
++		/*
++		 * The sender stated in SDP: "I am not going to provide FEC". Therefore,
++		 * we do not wait for the next frame and do PLC right away.
++		 */
++		decode_fec = 0;
++		opus_decoder_ctl(opvt->opus, OPUS_GET_LAST_PACKET_DURATION(&frame_size));
++		dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels);
++		len = f->datalen;
++		src = NULL;
++		status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec);
++		if (status < 0) {
++			ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
++		} else {
++			pvt->samples += status;
++			pvt->datalen += status * opvt->channels * sizeof(int16_t);
++		}
++		opvt->previous_lost = (f->datalen == 0 || status < 0);
++		return 0;
++	}
 +
-+	ast_debug(3, "[Decoder #%d (%d)]   >> Got %d samples, %d bytes\n",
-+		opvt->id,
-+		opvt->sampling_rate,
-+		pvt->samples,
-+		pvt->datalen);
++	/* Case 4 */
++	if (f->datalen == 0) { /* decode_fec && !opvt->previous_lost */
++		/*
++		 * The previous frame was of no issue. Therefore, we do not have to
++		 * reconstruct it. We do not have any data in the current frame but the
++		 * sender might give us FEC with the next frame. We cannot do anything
++		 * but wait for the next frame. Till Asterisk 13.7, this creates the
++		 * warning "opustolin48 did not update samples 0". Please, ignore this
++		 * warning or apply the patch included in the GitHub repository.
++		 */
++		status = 0; /* no samples to add currently */
++		if (status < 0) {
++			ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
++		} else {
++			pvt->samples += status;
++			pvt->datalen += status * opvt->channels * sizeof(int16_t);
++		}
++		opvt->previous_lost = (f->datalen == 0 || status < 0);
++		return 0;
++	}
 +
-+	return 0;
++	/* Case 5 and 6 */
++	if (!opvt->previous_lost) { /* 0 < f->datalen */
++		/*
++		 * The perfect case - the previous frame was not lost and we have data
++		 * in the current frame. Therefore, neither FEC nor PLC are required.
++		 */
++		decode_fec = 0;
++		frame_size = BUFFER_SAMPLES / opvt->multiplier; /* parse everything */
++		dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels);
++		len = f->datalen;
++		src = f->data.ptr;
++		status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec);
++		if (status < 0) {
++			ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
++		} else {
++			pvt->samples += status;
++			pvt->datalen += status * opvt->channels * sizeof(int16_t);
++		}
++		opvt->previous_lost = (f->datalen == 0 || status < 0);
++		return 0;
++	}
++
++	/* Case 7 */
++	if (!decode_fec) { /* 0 < f->datalen && opvt->previous_lost */
++		/*
++		 * The previous frame got lost and the sender stated in SDP: "I am not
++		 * going to provide FEC". Therefore, we do PLC. Furthermore, we try to
++		 * decode the current frame because we have data. This creates jitter
++		 * because we create double the amount of frames as normal, see
++		 * <https://issues.asterisk.org/jira/browse/ASTERISK-25483>. If this is
++		 * an issue for your use-case, please, file and issue report on
++		 * <https://github.com/traud/asterisk-opus/issues>.
++		 */
++		decode_fec = 0;
++		opus_decoder_ctl(opvt->opus, OPUS_GET_LAST_PACKET_DURATION(&frame_size));
++		dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels);
++		len = 0;
++		src = NULL;
++		status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec);
++		if (status < 0) {
++			ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
++		} else {
++			pvt->samples += status;
++			pvt->datalen += status * opvt->channels * sizeof(int16_t);
++		}
++		decode_fec = 0;
++		frame_size = BUFFER_SAMPLES / opvt->multiplier; /* parse everything */
++		dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels); /* append after PLC data */
++		len = f->datalen;
++		src = f->data.ptr;
++		status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec);
++		if (status < 0) {
++			ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
++		} else {
++			pvt->samples += status;
++			pvt->datalen += status * opvt->channels * sizeof(int16_t);
++		}
++		opvt->previous_lost = (f->datalen == 0 || status < 0);
++		return 0;
++	}
++
++	/* Case 8; Last Case */
++	{ /* 0 < f->datalen && opvt->previous_lost && decode_fec */
++		decode_fec = 1;
++		opus_decoder_ctl(opvt->opus, OPUS_GET_LAST_PACKET_DURATION(&frame_size));
++		dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels);
++		len = f->datalen;
++		src = f->data.ptr;
++		status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec);
++		if (status < 0) {
++			ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
++		} else {
++			pvt->samples += status;
++			pvt->datalen += status * opvt->channels * sizeof(int16_t);
++		}
++		decode_fec = 0;
++		frame_size = BUFFER_SAMPLES / opvt->multiplier; /* parse everything */
++		dst = pvt->outbuf.i16 + (pvt->samples * opvt->channels); /* append after FEC data */
++		len = f->datalen;
++		src = f->data.ptr;
++		status = opus_decode(opvt->opus, src, len, dst, frame_size, decode_fec);
++		if (status < 0) {
++			ast_log(LOG_ERROR, "%s\n", opus_strerror(status));
++		} else {
++			pvt->samples += status;
++			pvt->datalen += status * opvt->channels * sizeof(int16_t);
++		}
++		opvt->previous_lost = (f->datalen == 0 || status < 0);
++		return 0;
++	}
 +}
 +
 +static void lintoopus_destroy(struct ast_trans_pvt *arg)
@@ -423,6 +597,7 @@ Last-Update: 2016-04-02
 +
 +/* Translators */
 +static struct ast_translator opustolin = {
++        .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP,
 +        .name = "opustolin",
 +        .src_codec = {
 +                .name = "opus",
@@ -440,11 +615,13 @@ Last-Update: 2016-04-02
 +        .destroy = opustolin_destroy,
 +        .sample = opus_sample,
 +        .desc_size = sizeof(struct opus_coder_pvt),
-+        .buffer_samples = BUFFER_SAMPLES,
-+        .buf_size = BUFFER_SAMPLES * 2,
++        .buffer_samples = (BUFFER_SAMPLES / (48000 / 8000)) * 2, /* because of possible FEC */
++        .buf_size = (BUFFER_SAMPLES / (48000 / 8000)) * MAX_CHANNELS * sizeof(opus_int16) * 2,
++        .native_plc = 1,
 +};
 +
 +static struct ast_translator lintoopus = {
++        .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP,
 +        .name = "lintoopus",
 +        .src_codec = {
 +                .name = "slin",
@@ -468,6 +645,7 @@ Last-Update: 2016-04-02
 +};
 +
 +static struct ast_translator opustolin12 = {
++        .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP - 1,
 +        .name = "opustolin12",
 +        .src_codec = {
 +                .name = "opus",
@@ -485,11 +663,13 @@ Last-Update: 2016-04-02
 +        .destroy = opustolin_destroy,
 +        .sample = opus_sample,
 +        .desc_size = sizeof(struct opus_coder_pvt),
-+        .buffer_samples = BUFFER_SAMPLES,
-+        .buf_size = BUFFER_SAMPLES * 2,
++        .buffer_samples = (BUFFER_SAMPLES / (48000 / 12000)) * 2, /* because of possible FEC */
++        .buf_size = (BUFFER_SAMPLES / (48000 / 12000)) * MAX_CHANNELS * sizeof(opus_int16) * 2,
++        .native_plc = 1,
 +};
 +
 +static struct ast_translator lin12toopus = {
++        .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP - 1,
 +        .name = "lin12toopus",
 +        .src_codec = {
 +                .name = "slin",
@@ -512,6 +692,7 @@ Last-Update: 2016-04-02
 +};
 +
 +static struct ast_translator opustolin16 = {
++        .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP - 2,
 +        .name = "opustolin16",
 +        .src_codec = {
 +                .name = "opus",
@@ -529,11 +710,13 @@ Last-Update: 2016-04-02
 +        .destroy = opustolin_destroy,
 +        .sample = opus_sample,
 +        .desc_size = sizeof(struct opus_coder_pvt),
-+        .buffer_samples = BUFFER_SAMPLES,
-+        .buf_size = BUFFER_SAMPLES * 2,
++        .buffer_samples = (BUFFER_SAMPLES / (48000 / 16000)) * 2, /* because of possible FEC */
++        .buf_size = (BUFFER_SAMPLES / (48000 / 16000)) * MAX_CHANNELS * sizeof(opus_int16) * 2,
++        .native_plc = 1,
 +};
 +
 +static struct ast_translator lin16toopus = {
++        .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP - 2,
 +        .name = "lin16toopus",
 +        .src_codec = {
 +                .name = "slin",
@@ -557,6 +740,7 @@ Last-Update: 2016-04-02
 +};
 +
 +static struct ast_translator opustolin24 = {
++        .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP - 4,
 +        .name = "opustolin24",
 +        .src_codec = {
 +                .name = "opus",
@@ -574,11 +758,13 @@ Last-Update: 2016-04-02
 +        .destroy = opustolin_destroy,
 +        .sample = opus_sample,
 +        .desc_size = sizeof(struct opus_coder_pvt),
-+        .buffer_samples = BUFFER_SAMPLES,
-+        .buf_size = BUFFER_SAMPLES * 2,
++        .buffer_samples = (BUFFER_SAMPLES / (48000 / 24000)) * 2, /* because of possible FEC */
++        .buf_size = (BUFFER_SAMPLES / (48000 / 24000)) * MAX_CHANNELS * sizeof(opus_int16) * 2,
++        .native_plc = 1,
 +};
 +
 +static struct ast_translator lin24toopus = {
++        .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP - 4,
 +        .name = "lin24toopus",
 +        .src_codec = {
 +                .name = "slin",
@@ -601,6 +787,7 @@ Last-Update: 2016-04-02
 +};
 +
 +static struct ast_translator opustolin48 = {
++        .table_cost = AST_TRANS_COST_LY_LL_ORIGSAMP - 8,
 +        .name = "opustolin48",
 +        .src_codec = {
 +                .name = "opus",
@@ -618,11 +805,13 @@ Last-Update: 2016-04-02
 +        .destroy = opustolin_destroy,
 +        .sample = opus_sample,
 +        .desc_size = sizeof(struct opus_coder_pvt),
-+        .buffer_samples = BUFFER_SAMPLES,
-+        .buf_size = BUFFER_SAMPLES * 2,
++        .buffer_samples = BUFFER_SAMPLES * 2, /* twice, because of possible FEC */
++        .buf_size = BUFFER_SAMPLES * MAX_CHANNELS * sizeof(opus_int16) * 2,
++        .native_plc = 1,
 +};
 +
 +static struct ast_translator lin48toopus = {
++        .table_cost = AST_TRANS_COST_LL_LY_ORIGSAMP - 8,
 +        .name = "lin48toopus",
 +        .src_codec = {
 +                .name = "slin",
@@ -648,6 +837,13 @@ Last-Update: 2016-04-02
 +	AST_CLI_DEFINE(handle_cli_opus_show, "Display Opus codec utilization.")
 +};
 +
++static int opus_samples(struct ast_frame *frame)
++{
++	opus_int32 sampling_rate = 48000; /* FIXME */
++
++	return opus_packet_get_nb_samples(frame->data.ptr, frame->datalen, sampling_rate);
++}
++
 +static int reload(void)
 +{
 +	/* Reload does nothing */
@@ -658,6 +854,9 @@ Last-Update: 2016-04-02
 +{
 +	int res;
 +
++	opus_codec->samples_count = opus_samples_previous;
++	ao2_ref(opus_codec, -1);
++
 +	res = ast_unregister_translator(&opustolin);
 +	res |= ast_unregister_translator(&lintoopus);
 +	res |= ast_unregister_translator(&opustolin12);
@@ -678,6 +877,10 @@ Last-Update: 2016-04-02
 +{
 +	int res;
 +
++	opus_codec = ast_codec_get("opus", AST_MEDIA_TYPE_AUDIO, 48000);
++	opus_samples_previous = opus_codec->samples_count;
++	opus_codec->samples_count = opus_samples;
++
 +	res = ast_register_translator(&opustolin);
 +	res |= ast_register_translator(&lintoopus);
 +	res |= ast_register_translator(&opustolin12);
@@ -699,3 +902,810 @@ Last-Update: 2016-04-02
 +	.unload = unload_module,
 +	.reload = reload,
 +	);
+--- a/include/asterisk/opus.h
++++ b/include/asterisk/opus.h
+@@ -55,9 +55,9 @@
+ #define CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE 48000
+ #define CODEC_OPUS_DEFAULT_MAX_PTIME 120
+ #define CODEC_OPUS_DEFAULT_PTIME 20
+-#define CODEC_OPUS_DEFAULT_BITRATE -1000 /* OPUS_AUTO */
++#define CODEC_OPUS_DEFAULT_BITRATE 510000
+ #define CODEC_OPUS_DEFAULT_CBR 0
+-#define CODEC_OPUS_DEFAULT_FEC 0
++#define CODEC_OPUS_DEFAULT_FEC 1
+ #define CODEC_OPUS_DEFAULT_DTX 0
+ #define CODEC_OPUS_DEFAULT_STEREO 0
+ 
+--- a/res/res_format_attr_opus.c
++++ b/res/res_format_attr_opus.c
+@@ -29,14 +29,18 @@
+ 
+ #include "asterisk.h"
+ 
+-ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
++#if defined(ASTERISK_REGISTER_FILE)
++ASTERISK_REGISTER_FILE()
++#elif defined(ASTERISK_FILE_VERSION)
++ASTERISK_FILE_VERSION(__FILE__, "$Revision: $")
++#endif
+ 
+ #include "asterisk/module.h"
+ #include "asterisk/format.h"
+-#include "asterisk/logger.h"
+-#include "asterisk/strings.h"
+-#include "asterisk/utils.h"
+-#include "asterisk/opus.h"
++#include "asterisk/logger.h"            /* for ast_log, LOG_WARNING */
++#include "asterisk/opus.h"              /* for CODEC_OPUS_DEFAULT_* */
++#include "asterisk/strings.h"           /* for ast_str_append */
++#include "asterisk/utils.h"             /* for MIN, ast_malloc, ast_free */
+ 
+ /*!
+  * \brief Opus attribute structure.
+@@ -44,42 +48,32 @@
+  * \note http://tools.ietf.org/html/rfc7587#section-6
+  */
+ struct opus_attr {
+-	int maxbitrate;
+-	int maxplayrate;
+-	int ptime;
+-	int stereo;
+-	int cbr;
+-	int fec;
+-	int dtx;
+-	int spropmaxcapturerate;
+-	int spropstereo;
+-	int maxptime;
+-	/* Note data is expected to be an ao2_object type */
+-	void *data;
++	unsigned int maxbitrate;
++	unsigned int maxplayrate;
++	unsigned int unused; /* was minptime, kept for binary compatibility */
++	unsigned int stereo;
++	unsigned int cbr;
++	unsigned int fec;
++	unsigned int dtx;
++	unsigned int spropmaxcapturerate;
++	unsigned int spropstereo;
+ };
+ 
+ static struct opus_attr default_opus_attr = {
+-	.maxbitrate = CODEC_OPUS_DEFAULT_BITRATE,
+-	.maxplayrate = CODEC_OPUS_DEFAULT_SAMPLE_RATE,
+-	.ptime = CODEC_OPUS_DEFAULT_PTIME,
+-	.stereo = CODEC_OPUS_DEFAULT_STEREO,
+-	.cbr = CODEC_OPUS_DEFAULT_CBR,
+-	.fec = CODEC_OPUS_DEFAULT_FEC,
+-	.dtx = CODEC_OPUS_DEFAULT_DTX,
+-	.spropmaxcapturerate = CODEC_OPUS_DEFAULT_SAMPLE_RATE,
+-	.spropstereo = CODEC_OPUS_DEFAULT_STEREO,
+-	.maxptime = CODEC_OPUS_DEFAULT_MAX_PTIME
++	.maxplayrate         = CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE,
++	.spropmaxcapturerate = CODEC_OPUS_DEFAULT_MAX_PLAYBACK_RATE,
++	.maxbitrate          = CODEC_OPUS_DEFAULT_BITRATE,
++	.stereo              = CODEC_OPUS_DEFAULT_STEREO,
++	.spropstereo         = CODEC_OPUS_DEFAULT_STEREO,
++	.cbr                 = CODEC_OPUS_DEFAULT_CBR,
++	.fec                 = CODEC_OPUS_DEFAULT_FEC,
++	.dtx                 = CODEC_OPUS_DEFAULT_DTX,
+ };
+ 
+ static void opus_destroy(struct ast_format *format)
+ {
+ 	struct opus_attr *attr = ast_format_get_attribute_data(format);
+ 
+-	if (!attr) {
+-		return;
+-	}
+-
+-	ao2_cleanup(attr->data);
+ 	ast_free(attr);
+ }
+ 
+@@ -92,129 +86,172 @@
+ 		return -1;
+ 	}
+ 
+-	*attr = original ? *original : default_opus_attr;
+-	ao2_bump(attr->data);
++	if (original) {
++		*attr = *original;
++	} else {
++		*attr = default_opus_attr;
++	}
+ 
+ 	ast_format_set_attribute_data(dst, attr);
+ 
+ 	return 0;
+ }
+ 
+-static void sdp_fmtp_get(const char *attributes, const char *name, int *attr)
+-{
+-	const char *kvp = "";
+-	int val;
+-
+-	if (attributes && !(kvp = strstr(attributes, name))) {
+-		return;
+-	}
+-
+-	/*
+-	 * If the named attribute is not at the start of the given attributes, and
+-	 * the preceding character is not a space or semicolon then it's not the
+-	 * attribute we are looking for. It's an attribute with the name embedded
+-	 * within it (e.g. ptime in maxptime, stereo in sprop-stereo).
+-	 */
+-	if (kvp != attributes && *(kvp - 1) != ' ' && *(kvp - 1) != ';') {
+-		/* Keep searching as it might still be in the attributes string */
+-		sdp_fmtp_get(strchr(kvp, ';'), name, attr);
+-	/*
+-	 * Otherwise it's a match, so retrieve the value and set the attribute.
+-	 */
+-	} else if (sscanf(kvp, "%*[^=]=%30d", &val) == 1) {
+-		*attr = val;
+-	}
+-}
+-
+ static struct ast_format *opus_parse_sdp_fmtp(const struct ast_format *format, const char *attributes)
+ {
+ 	struct ast_format *cloned;
+ 	struct opus_attr *attr;
++	const char *kvp;
++	unsigned int val;
+ 
+ 	cloned = ast_format_clone(format);
+ 	if (!cloned) {
+ 		return NULL;
+ 	}
+-
+ 	attr = ast_format_get_attribute_data(cloned);
+ 
+-	sdp_fmtp_get(attributes, CODEC_OPUS_ATTR_MAX_PLAYBACK_RATE, &attr->maxplayrate);
+-	sdp_fmtp_get(attributes, CODEC_OPUS_ATTR_MAX_CODED_AUDIO_BANDWIDTH,
+-		&attr->maxplayrate);
+-	sdp_fmtp_get(attributes, CODEC_OPUS_ATTR_SPROP_MAX_CAPTURE_RATE,
+-		&attr->spropmaxcapturerate);
+-	sdp_fmtp_get(attributes, CODEC_OPUS_ATTR_MAX_PTIME, &attr->maxptime);
+-	sdp_fmtp_get(attributes, CODEC_OPUS_ATTR_PTIME, &attr->ptime);
+-	sdp_fmtp_get(attributes, CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE, &attr->maxbitrate);
+-	sdp_fmtp_get(attributes, CODEC_OPUS_ATTR_STEREO, &attr->stereo);
+-	sdp_fmtp_get(attributes, CODEC_OPUS_ATTR_SPROP_STEREO, &attr->spropstereo);
+-	sdp_fmtp_get(attributes, CODEC_OPUS_ATTR_CBR, &attr->cbr);
+-	sdp_fmtp_get(attributes, CODEC_OPUS_ATTR_FEC, &attr->fec);
+-	sdp_fmtp_get(attributes, CODEC_OPUS_ATTR_DTX, &attr->dtx);
+-
+-	return cloned;
+-}
+-
+-static void opus_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
+-{
+-	struct opus_attr *attr = ast_format_get_attribute_data(format);
+-	int size;
+-
+-	if (!attr) {
+-		/*
+-		 * (Only) cached formats do not have attribute data assigned because
+-		 * they were created before this attribute module was registered.
+-		 * Therefore, we assume the default attribute values here.
+-		 */
+-		attr = &default_opus_attr;
++	if ((kvp = strstr(attributes, "maxplaybackrate")) && sscanf(kvp, "maxplaybackrate=%30u", &val) == 1) {
++		attr->maxplayrate = val;
++	} else {
++		attr->maxplayrate = 48000;
+ 	}
+ 
+-	size = ast_str_append(str, 0, "a=fmtp:%u ", payload);
++	if ((kvp = strstr(attributes, "sprop-maxcapturerate")) && sscanf(kvp, "sprop-maxcapturerate=%30u", &val) == 1) {
++		attr->spropmaxcapturerate = val;
++	} else {
++		attr->spropmaxcapturerate = 48000;
++	}
+ 
+-	if (CODEC_OPUS_DEFAULT_SAMPLE_RATE != attr->maxplayrate) {
+-		ast_str_append(str, 0, "%s=%d;",
+-			CODEC_OPUS_ATTR_MAX_PLAYBACK_RATE, attr->maxplayrate);
++	if ((kvp = strstr(attributes, "maxaveragebitrate")) && sscanf(kvp, "maxaveragebitrate=%30u", &val) == 1) {
++		attr->maxbitrate = val;
++	} else {
++		attr->maxbitrate = 510000;
+ 	}
+ 
+-	if (CODEC_OPUS_DEFAULT_SAMPLE_RATE != attr->spropmaxcapturerate) {
+-		ast_str_append(str, 0, "%s=%d;",
+-			CODEC_OPUS_ATTR_SPROP_MAX_CAPTURE_RATE, attr->spropmaxcapturerate);
++	if (!strncmp(attributes, "stereo=1", 8)) {
++		attr->stereo = 1;
++	} else if (strstr(attributes, " stereo=1")) {
++		attr->stereo = 1;
++	} else if (strstr(attributes, ";stereo=1")) {
++		attr->stereo = 1;
++	} else {
++		attr->stereo = 0;
+ 	}
+ 
+-	if (CODEC_OPUS_DEFAULT_BITRATE != attr->maxbitrate || attr->maxbitrate > 0) {
+-		ast_str_append(str, 0, "%s=%d;",
+-			CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE, attr->maxbitrate);
++	if (strstr(attributes, "sprop-stereo=1")) {
++		attr->spropstereo = 1;
++	} else {
++		attr->spropstereo = 0;
+ 	}
+ 
+-	if (CODEC_OPUS_DEFAULT_STEREO != attr->stereo) {
+-		ast_str_append(str, 0, "%s=%d;",
+-			CODEC_OPUS_ATTR_STEREO, attr->stereo);
++	if (strstr(attributes, "cbr=1")) {
++		attr->cbr = 1;
++	} else {
++		attr->cbr = 0;
+ 	}
+ 
+-	if (CODEC_OPUS_DEFAULT_STEREO != attr->spropstereo) {
+-		ast_str_append(str, 0, "%s=%d;",
+-			CODEC_OPUS_ATTR_SPROP_STEREO, attr->spropstereo);
++	if (strstr(attributes, "useinbandfec=1")) {
++		attr->fec = 1;
++	} else {
++		attr->fec = 0;
+ 	}
+ 
+-	if (CODEC_OPUS_DEFAULT_CBR != attr->cbr) {
+-		ast_str_append(str, 0, "%s=%d;",
+-			CODEC_OPUS_ATTR_CBR, attr->cbr);
++	if (strstr(attributes, "usedtx=1")) {
++		attr->dtx = 1;
++	} else {
++		attr->dtx = 0;
+ 	}
+ 
+-	if (CODEC_OPUS_DEFAULT_FEC!= attr->fec) {
+-		ast_str_append(str, 0, "%s=%d;",
+-		       CODEC_OPUS_ATTR_FEC, attr->fec);
++	return cloned;
++}
++
++static void opus_generate_sdp_fmtp(const struct ast_format *format, unsigned int payload, struct ast_str **str)
++{
++	struct opus_attr *attr = ast_format_get_attribute_data(format);
++	int added = 0;
++
++	if (!attr) {
++		/*
++		 * (Only) cached formats do not have attribute data assigned because
++		 * they were created before this attribute module was registered.
++		 * Therefore, we assume the default attribute values here.
++		 */
++		attr = &default_opus_attr;
+ 	}
+ 
+-	if (CODEC_OPUS_DEFAULT_DTX != attr->dtx) {
+-		ast_str_append(str, 0, "%s=%d;",
+-			CODEC_OPUS_ATTR_DTX, attr->dtx);
++	if (48000 != attr->maxplayrate) {
++		if (added) {
++			ast_str_append(str, 0, ";");
++		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
++			added = 1;
++		}
++		ast_str_append(str, 0, "maxplaybackrate=%u", attr->maxplayrate);
++	}
++
++	if (48000 != attr->spropmaxcapturerate) {
++		if (added) {
++			ast_str_append(str, 0, ";");
++		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
++			added = 1;
++		}
++		ast_str_append(str, 0, "sprop-maxcapturerate=%u", attr->spropmaxcapturerate);
++	}
++
++	if (510000 != attr->maxbitrate) {
++		if (added) {
++			ast_str_append(str, 0, ";");
++		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
++			added = 1;
++		}
++		ast_str_append(str, 0, "maxaveragebitrate=%u", attr->maxbitrate);
++	}
++
++	if (0 != attr->stereo) {
++		if (added) {
++			ast_str_append(str, 0, ";");
++		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
++			added = 1;
++		}
++		ast_str_append(str, 0, "stereo=%u", attr->stereo);
++	}
++
++	if (0 != attr->spropstereo) {
++		if (added) {
++			ast_str_append(str, 0, ";");
++		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
++			added = 1;
++		}
++		ast_str_append(str, 0, "sprop-stereo=%u", attr->spropstereo);
++	}
++
++	if (0 != attr->cbr) {
++		if (added) {
++			ast_str_append(str, 0, ";");
++		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
++			added = 1;
++		}
++		ast_str_append(str, 0, "cbr=%u", attr->cbr);
++	}
++
++	if (0 != attr->fec) {
++		if (added) {
++			ast_str_append(str, 0, ";");
++		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
++			added = 1;
++		}
++		ast_str_append(str, 0, "useinbandfec=%u", attr->fec);
++	}
++
++	if (0 != attr->dtx) {
++		if (added) {
++			ast_str_append(str, 0, ";");
++		} else if (0 < ast_str_append(str, 0, "a=fmtp:%u ", payload)) {
++			added = 1;
++		}
++		ast_str_append(str, 0, "usedtx=%u", attr->dtx);
+ 	}
+ 
+-	if (size == ast_str_strlen(*str)) {
+-		ast_str_reset(*str);
+-	} else {
+-		ast_str_truncate(*str, -1);
++	if (added) {
+ 		ast_str_append(str, 0, "\r\n");
+ 	}
+ }
+@@ -253,68 +290,49 @@
+ 	 * to receive stereo signals, it may be a waste of bandwidth. */
+ 	attr_res->stereo = attr1->stereo && attr2->stereo ? 1 : 0;
+ 
+-	if (attr1->maxbitrate < 0) {
+-		attr_res->maxbitrate = attr2->maxbitrate;
+-	} else if (attr2->maxbitrate < 0) {
+-		attr_res->maxbitrate = attr1->maxbitrate;
+-	} else {
+-		attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate);
+-	}
+-
++	attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate);
+ 	attr_res->spropmaxcapturerate = MIN(attr1->spropmaxcapturerate, attr2->spropmaxcapturerate);
+ 	attr_res->maxplayrate = MIN(attr1->maxplayrate, attr2->maxplayrate);
+ 
+ 	return jointformat;
+ }
+ 
+-static struct ast_format *opus_set(const struct ast_format *format,
+-	const char *name, const char *value)
++static struct ast_format *opus_set(const struct ast_format *format, const char *name, const char *value)
+ {
+ 	struct ast_format *cloned;
+ 	struct opus_attr *attr;
+-	int val;
++	unsigned int val;
+ 
+-	if (!(cloned = ast_format_clone(format))) {
++	if (sscanf(value, "%30u", &val) != 1) {
++		ast_log(LOG_WARNING, "Unknown value '%s' for attribute type '%s'\n",
++			value, name);
+ 		return NULL;
+ 	}
+ 
+-	attr = ast_format_get_attribute_data(cloned);
+-
+-	if (!strcmp(name, CODEC_OPUS_ATTR_DATA)) {
+-		ao2_cleanup(attr->data);
+-		attr->data = ao2_bump((void*)value);
+-		return cloned;
+-	}
+-
+-	if (sscanf(value, "%30d", &val) != 1) {
+-		ast_log(LOG_WARNING, "Unknown value '%s' for attribute type '%s'\n",
+-			value, name);
+-		ao2_ref(cloned, -1);
++	cloned = ast_format_clone(format);
++	if (!cloned) {
+ 		return NULL;
+ 	}
++	attr = ast_format_get_attribute_data(cloned);
+ 
+-	if (!strcasecmp(name, CODEC_OPUS_ATTR_MAX_PLAYBACK_RATE)) {
+-		attr->maxplayrate = val;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_MAX_CODED_AUDIO_BANDWIDTH)) {
+-		attr->maxplayrate = val;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_SPROP_MAX_CAPTURE_RATE)) {
+-		attr->spropmaxcapturerate = val;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_MAX_PTIME)) {
+-		attr->maxptime = val;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_PTIME)) {
+-		attr->ptime = val;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE)) {
++	if (!strcasecmp(name, "max_bitrate")) {
+ 		attr->maxbitrate = val;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_STEREO)) {
++	} else if (!strcasecmp(name, "max_playrate")) {
++		attr->maxplayrate = val;
++	} else if (!strcasecmp(name, "minptime")) {
++		attr->unused = val;
++	} else if (!strcasecmp(name, "stereo")) {
+ 		attr->stereo = val;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_SPROP_STEREO)) {
+-		attr->spropstereo = val;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_CBR)) {
++	} else if (!strcasecmp(name, "cbr")) {
+ 		attr->cbr = val;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_FEC)) {
++	} else if (!strcasecmp(name, "fec")) {
+ 		attr->fec = val;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_DTX)) {
++	} else if (!strcasecmp(name, "dtx")) {
+ 		attr->dtx = val;
++	} else if (!strcasecmp(name, "sprop_capture_rate")) {
++		attr->spropmaxcapturerate = val;
++	} else if (!strcasecmp(name, "sprop_stereo")) {
++		attr->spropstereo = val;
+ 	} else {
+ 		ast_log(LOG_WARNING, "unknown attribute type %s\n", name);
+ 	}
+@@ -322,44 +340,6 @@
+ 	return cloned;
+ }
+ 
+-static const void *opus_get(const struct ast_format *format, const char *name)
+-{
+-	struct opus_attr *attr = ast_format_get_attribute_data(format);
+-	int *val = NULL;
+-
+-	if (!attr) {
+-		return NULL;
+-	}
+-
+-	if (!strcasecmp(name, CODEC_OPUS_ATTR_DATA)) {
+-		return ao2_bump(attr->data);
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_MAX_PLAYBACK_RATE)) {
+-		val = &attr->maxplayrate;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_SPROP_MAX_CAPTURE_RATE)) {
+-		val = &attr->spropmaxcapturerate;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_MAX_PTIME)) {
+-		val = &attr->maxptime;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_PTIME)) {
+-		val = &attr->ptime;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_MAX_AVERAGE_BITRATE)) {
+-		val = &attr->maxbitrate;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_STEREO)) {
+-		val = &attr->stereo;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_SPROP_STEREO)) {
+-		val = &attr->spropstereo;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_CBR)) {
+-		val = &attr->cbr;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_FEC)) {
+-		val = &attr->fec;
+-	} else if (!strcasecmp(name, CODEC_OPUS_ATTR_DTX)) {
+-		val = &attr->dtx;
+-	} else {
+-		ast_log(LOG_WARNING, "unknown attribute type %s\n", name);
+-	}
+-
+-	return val;
+-}
+-
+ static struct ast_format_interface opus_interface = {
+ 	.format_destroy = opus_destroy,
+ 	.format_clone = opus_clone,
+@@ -367,12 +347,11 @@
+ 	.format_attribute_set = opus_set,
+ 	.format_parse_sdp_fmtp = opus_parse_sdp_fmtp,
+ 	.format_generate_sdp_fmtp = opus_generate_sdp_fmtp,
+-	.format_attribute_get = opus_get
+ };
+ 
+ static int load_module(void)
+ {
+-	if (__ast_format_interface_register("opus", &opus_interface, ast_module_info->self)) {
++	if (ast_format_interface_register("opus", &opus_interface)) {
+ 		return AST_MODULE_LOAD_DECLINE;
+ 	}
+ 
+@@ -384,9 +363,9 @@
+ 	return 0;
+ }
+ 
+-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Opus Format Attribute Module",
++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Opus Format Attribute Module",
+ 	.support_level = AST_MODULE_SUPPORT_CORE,
+ 	.load = load_module,
+ 	.unload = unload_module,
+-	.load_pri = AST_MODPRI_REALTIME_DRIVER /* Needs to load before codec_opus */
++	.load_pri = AST_MODPRI_CHANNEL_DEPEND,
+ );
+--- a/build_tools/menuselect-deps.in
++++ b/build_tools/menuselect-deps.in
+@@ -44,6 +44,7 @@
+ OGG=@PBX_OGG@
+ OPENH323=@PBX_OPENH323@
+ OPUS=@PBX_OPUS@
++OPUSFILE=@PBX_OPUSFILE@
+ OSPTK=@PBX_OSPTK@
+ OSS=@PBX_OSS@
+ PGSQL=@PBX_PGSQL@
+--- a/configure.ac
++++ b/configure.ac
+@@ -495,6 +495,7 @@
+ AST_EXT_LIB_SETUP([OGG], [OGG], [ogg])
+ AST_EXT_LIB_SETUP([OPENR2], [MFR2], [openr2])
+ AST_EXT_LIB_SETUP([OPUS], [Opus], [opus])
++AST_EXT_LIB_SETUP([OPUSFILE], [Opusfile], [opusfile])
+ AST_EXT_LIB_SETUP([OSPTK], [OSP Toolkit], [osptk])
+ AST_EXT_LIB_SETUP([OSS], [Open Sound System], [oss])
+ AST_EXT_LIB_SETUP([PGSQL], [PostgreSQL], [postgres])
+@@ -2277,6 +2278,13 @@
+ AST_EXT_LIB_CHECK([OPENR2], [openr2], [openr2_chan_new], [openr2.h])
+ 
+ AST_EXT_LIB_CHECK([OPUS], [opus], [opus_encoder_create], [opus/opus.h])
++# opusfile.h includes <opus_multistream.h> so we need to make sure that
++# either $OPUS_INCLUDE or /usr/include/opus is added to the search path.
++__opus_include=${OPUS_INCLUDE}
++if test -z "$__opus_include" -o x"$__opus_include" = x" " ; then
++	__opus_include=-I/usr/include/opus
++fi
++AST_EXT_LIB_CHECK([OPUSFILE], [opusfile], [op_open_callbacks], [opus/opusfile.h], [], [$__opus_include])
+ 
+ if test "${USE_PWLIB}" != "no"; then
+ 	if test -n "${PWLIB_DIR}"; then
+--- /dev/null
++++ b/formats/format_ogg_opus_open_source.c
+@@ -0,0 +1,236 @@
++/*
++ * Asterisk -- An open source telephony toolkit.
++ *
++ * Copyright (C) 2016, Digium, Inc.
++ *
++ * Mark Michelson <mmichelson at digium.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.
++ */
++
++/*** MODULEINFO
++	<depend>opusfile</depend>
++	<conflict>format_ogg_opus</conflict>
++	<defaultenabled>yes</defaultenabled>
++ ***/
++
++#include "asterisk.h"
++
++#if defined(ASTERISK_REGISTER_FILE)
++ASTERISK_REGISTER_FILE()
++#elif defined(ASTERISK_FILE_VERSION)
++ASTERISK_FILE_VERSION(__FILE__, "$Revision: $")
++#endif
++
++#include <opus/opus.h>
++#include <opus/opusfile.h>
++#include "asterisk/mod_format.h"
++#include "asterisk/utils.h"
++#include "asterisk/module.h"
++#include "asterisk/format_cache.h"
++
++/* 120ms of 48KHz audio */
++#define SAMPLES_MAX 5760
++#define BUF_SIZE (2 * SAMPLES_MAX)
++
++struct ogg_opus_desc {
++	OggOpusFile *of;
++};
++
++static int fread_wrapper(void *_stream, unsigned char *_ptr, int _nbytes)
++{
++	FILE *stream = _stream;
++	size_t bytes_read;
++
++	if (!stream || _nbytes < 0) {
++		return -1;
++	}
++
++	bytes_read = fread(_ptr, 1, _nbytes, stream);
++
++	return bytes_read > 0 || feof(stream) ? (int) bytes_read : OP_EREAD;
++}
++
++static int fseek_wrapper(void *_stream, opus_int64 _offset, int _whence)
++{
++	FILE *stream = _stream;
++
++	return fseeko(stream, (off_t) _offset, _whence);
++}
++
++static opus_int64 ftell_wrapper(void *_stream)
++{
++	FILE *stream = _stream;
++
++	return ftello(stream);
++}
++
++static int ogg_opus_open(struct ast_filestream *s)
++{
++	struct ogg_opus_desc *desc = (struct ogg_opus_desc *) s->_private;
++	OpusFileCallbacks cb = {
++		.read = fread_wrapper,
++		.seek = fseek_wrapper,
++		.tell = ftell_wrapper,
++		.close = NULL,
++	};
++
++	memset(desc, 0, sizeof(*desc));
++	desc->of = op_open_callbacks(s->f, &cb, NULL, 0, NULL);
++	if (!desc->of) {
++		return -1;
++	}
++
++	return 0;
++}
++
++static int ogg_opus_rewrite(struct ast_filestream *s, const char *comment)
++{
++	/* XXX Unimplemented. We currently only can read from OGG/Opus streams */
++	ast_log(LOG_ERROR, "Cannot write OGG/Opus streams. Sorry :(\n");
++	return -1;
++}
++
++static int ogg_opus_write(struct ast_filestream *fs, struct ast_frame *f)
++{
++	/* XXX Unimplemented. We currently only can read from OGG/Opus streams */
++	ast_log(LOG_ERROR, "Cannot write OGG/Opus streams. Sorry :(\n");
++	return -1;
++}
++
++static int ogg_opus_seek(struct ast_filestream *fs, off_t sample_offset, int whence)
++{
++	int seek_result = -1;
++	off_t relative_pcm_pos;
++	struct ogg_opus_desc *desc = fs->_private;
++
++	switch (whence) {
++	case SEEK_SET:
++		seek_result = op_pcm_seek(desc->of, sample_offset);
++		break;
++	case SEEK_CUR:
++		if ((relative_pcm_pos = op_pcm_tell(desc->of)) < 0) {
++			seek_result = -1;
++			break;
++		}
++		seek_result = op_pcm_seek(desc->of, relative_pcm_pos + sample_offset);
++		break;
++	case SEEK_END:
++		if ((relative_pcm_pos = op_pcm_total(desc->of, -1)) < 0) {
++			seek_result = -1;
++			break;
++		}
++		seek_result = op_pcm_seek(desc->of, relative_pcm_pos - sample_offset);
++		break;
++	default:
++		ast_log(LOG_WARNING, "Unknown *whence* to seek on OGG/Opus streams!\n");
++		break;
++	}
++
++	/* normalize error value to -1,0 */
++	return (seek_result == 0) ? 0 : -1;
++}
++
++static int ogg_opus_trunc(struct ast_filestream *fs)
++{
++	/* XXX Unimplemented. This is only used when recording, and we don't support that right now. */
++	ast_log(LOG_ERROR, "Truncation is not supported on OGG/Opus streams!\n");
++	return -1;
++}
++
++static off_t ogg_opus_tell(struct ast_filestream *fs)
++{
++	struct ogg_opus_desc *desc = fs->_private;
++	off_t pos;
++
++	pos = (off_t) op_pcm_tell(desc->of);
++	if (pos < 0) {
++		return -1;
++	}
++	return pos;
++}
++
++static struct ast_frame *ogg_opus_read(struct ast_filestream *fs, int *whennext)
++{
++	struct ogg_opus_desc *desc = fs->_private;
++	int hole = 1;
++	int samples_read;
++	opus_int16 *out_buf;
++
++	AST_FRAME_SET_BUFFER(&fs->fr, fs->buf, AST_FRIENDLY_OFFSET, BUF_SIZE);
++
++	out_buf = (opus_int16 *) fs->fr.data.ptr;
++
++	while (hole) {
++		samples_read = op_read(
++			desc->of,
++			out_buf,
++			SAMPLES_MAX,
++			NULL);
++
++		if (samples_read != OP_HOLE) {
++			hole = 0;
++		}
++	}
++
++	if (samples_read <= 0) {
++		return NULL;
++	}
++
++	fs->fr.datalen = samples_read * 2;
++	fs->fr.samples = samples_read;
++	*whennext = fs->fr.samples;
++
++	return &fs->fr;
++}
++
++static void ogg_opus_close(struct ast_filestream *fs)
++{
++	struct ogg_opus_desc *desc = fs->_private;
++
++	op_free(desc->of);
++}
++
++static struct ast_format_def opus_f = {
++	.name = "ogg_opus",
++	.exts = "opus",
++	.open = ogg_opus_open,
++	.rewrite = ogg_opus_rewrite,
++	.write = ogg_opus_write,
++	.seek = ogg_opus_seek,
++	.trunc = ogg_opus_trunc,
++	.tell = ogg_opus_tell,
++	.read = ogg_opus_read,
++	.close = ogg_opus_close,
++	.buf_size = BUF_SIZE + AST_FRIENDLY_OFFSET,
++	.desc_size = sizeof(struct ogg_opus_desc),
++};
++
++static int load_module(void)
++{
++	opus_f.format = ast_format_slin48;
++	if (ast_format_def_register(&opus_f)) {
++		return AST_MODULE_LOAD_FAILURE;
++	}
++	return AST_MODULE_LOAD_SUCCESS;
++}
++
++static int unload_module(void)
++{
++	return ast_format_def_unregister(opus_f.name);
++}
++
++AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "OGG/Opus audio",
++	.support_level = AST_MODULE_SUPPORT_CORE,
++	.load = load_module,
++	.unload = unload_module,
++	.load_pri = AST_MODPRI_APP_DEPEND
++);
+--- a/makeopts.in
++++ b/makeopts.in
+@@ -225,6 +225,9 @@
+ OPUS_INCLUDE=@OPUS_INCLUDE@
+ OPUS_LIB=@OPUS_LIB@
+ 
++OPUSFILE_INCLUDE=@OPUSFILE_INCLUDE@
++OPUSFILE_LIB=@OPUSFILE_LIB@
++
+ OSPTK_INCLUDE=@OSPTK_INCLUDE@
+ OSPTK_LIB=@OSPTK_LIB@
+ 
diff --git a/debian/rules b/debian/rules
index e3a211e..a0adc2a 100755
--- a/debian/rules
+++ b/debian/rules
@@ -92,6 +92,7 @@ override_dh_auto_configure:
 
 override_dh_auto_build:
 	$(MAKE) menuselect.makeopts BUILD_CFLAGS="$(CFLAGS) $(CPPFLAGS)" BUILD_LDFLAGS="$(LDFLAGS)"
+	menuselect/menuselect --enable codec_opus_open_source menuselect.makeopts
 	@if [ "x${ENABLE_DEBUG}" != "x" ] ; then \
 		menuselect/menuselect --enable BETTER_BACKTRACES menuselect.makeopts ; \
 		menuselect/menuselect --enable DEBUG_THREADS menuselect.makeopts ; \

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-voip/asterisk.git



More information about the Pkg-voip-commits mailing list