[Asterisk-code-review] Update support for SILK format. (asterisk[master])

Joshua Colp asteriskteam at digium.com
Thu Jul 14 17:22:19 CDT 2016


Joshua Colp has submitted this change and it was merged.

Change subject: Update support for SILK format.
......................................................................


Update support for SILK format.

This commit adds scaffolding in order to support the SILK audio format
on calls. Roughly, this is what is added:

* Cached silk formats. One for each possible sample rate.
* ast_codec structures for each possible sample rate.
* RTP payload mappings for "SILK".

In addition, this change overhauls the res_format_attr_silk file in the
following ways:

* The "samplerate" attribute is scrapped. That's native to the format.
* There are far more checks to ensure that attributes have been
  allocated before attempting to reference them.
* We do not SDP fmtp lines for attributes set to 0.

These changes make way to be able to install a codec_silk module and
have it actually work. It also should allow for passthrough silk calls
in Asterisk.

Change-Id: Ieeb39c95a9fecc9246bcfd3c45a6c9b51c59380e
---
M include/asterisk/format_cache.h
M main/codec_builtin.c
M main/format_cache.c
M main/rtp_engine.c
M res/res_format_attr_silk.c
5 files changed, 134 insertions(+), 31 deletions(-)

Approvals:
  Richard Mudgett: Looks good to me, but someone else must approve
  Joshua Colp: Looks good to me, approved; Verified



diff --git a/include/asterisk/format_cache.h b/include/asterisk/format_cache.h
index 64e53b9..3894ad2 100644
--- a/include/asterisk/format_cache.h
+++ b/include/asterisk/format_cache.h
@@ -224,6 +224,14 @@
 extern struct ast_format *ast_format_none;
 
 /*!
+ * \brief Built-in SILK format.
+ */
+extern struct ast_format *ast_format_silk8;
+extern struct ast_format *ast_format_silk12;
+extern struct ast_format *ast_format_silk16;
+extern struct ast_format *ast_format_silk24;
+
+/*!
  * \brief Initialize format cache support within the core.
  *
  * \retval 0 success
diff --git a/main/codec_builtin.c b/main/codec_builtin.c
index 9131643..50fbf55 100644
--- a/main/codec_builtin.c
+++ b/main/codec_builtin.c
@@ -772,6 +772,65 @@
 	.type = AST_MEDIA_TYPE_TEXT,
 };
 
+static int silk_samples(struct ast_frame *frame)
+{
+	/* XXX This is likely not at all what's intended from this callback. However,
+	 * since SILK is variable bit rate, I have no idea how to take a frame of data
+	 * and determine the number of samples present. Instead, we base this on the
+	 * sample rate of the codec and the expected number of samples to receive in 20ms.
+	 * In testing, this has worked just fine.
+	 */
+	return ast_format_get_sample_rate(frame->subclass.format) / 50;
+}
+
+static struct ast_codec silk8 = {
+	.name = "silk",
+	.description = "SILK Codec (8 KHz)",
+	.type = AST_MEDIA_TYPE_AUDIO,
+	.sample_rate = 8000,
+	.minimum_ms = 20,
+	.maximum_ms = 100,
+	.default_ms = 20,
+	.minimum_bytes = 160,
+	.samples_count = silk_samples
+};
+
+static struct ast_codec silk12 = {
+	.name = "silk",
+	.description = "SILK Codec (12 KHz)",
+	.type = AST_MEDIA_TYPE_AUDIO,
+	.sample_rate = 12000,
+	.minimum_ms = 20,
+	.maximum_ms = 100,
+	.default_ms = 20,
+	.minimum_bytes = 240,
+	.samples_count = silk_samples
+};
+
+static struct ast_codec silk16 = {
+	.name = "silk",
+	.description = "SILK Codec (16 KHz)",
+	.type = AST_MEDIA_TYPE_AUDIO,
+	.sample_rate = 16000,
+	.minimum_ms = 20,
+	.maximum_ms = 100,
+	.default_ms = 20,
+	.minimum_bytes = 320,
+	.samples_count = silk_samples
+};
+
+static struct ast_codec silk24 = {
+	.name = "silk",
+	.description = "SILK Codec (24 KHz)",
+	.type = AST_MEDIA_TYPE_AUDIO,
+	.sample_rate = 24000,
+	.minimum_ms = 20,
+	.maximum_ms = 100,
+	.default_ms = 20,
+	.minimum_bytes = 480,
+	.samples_count = silk_samples
+};
+
 #define CODEC_REGISTER_AND_CACHE(codec) \
 	({ \
 		int __res_ ## __LINE__ = 0; \
@@ -843,6 +902,10 @@
 	res |= CODEC_REGISTER_AND_CACHE(t140red);
 	res |= CODEC_REGISTER_AND_CACHE(t140);
 	res |= CODEC_REGISTER_AND_CACHE(none);
+	res |= CODEC_REGISTER_AND_CACHE_NAMED("silk8", silk8);
+	res |= CODEC_REGISTER_AND_CACHE_NAMED("silk12", silk12);
+	res |= CODEC_REGISTER_AND_CACHE_NAMED("silk16", silk16);
+	res |= CODEC_REGISTER_AND_CACHE_NAMED("silk24", silk24);
 
 	return res;
 }
diff --git a/main/format_cache.c b/main/format_cache.c
index def795c..b4d4260 100644
--- a/main/format_cache.c
+++ b/main/format_cache.c
@@ -232,6 +232,14 @@
  */
 struct ast_format *ast_format_none;
 
+/*!
+ * \brief Built-in "silk" format
+ */
+struct ast_format *ast_format_silk8;
+struct ast_format *ast_format_silk12;
+struct ast_format *ast_format_silk16;
+struct ast_format *ast_format_silk24;
+
 /*! \brief Number of buckets to use for the media format cache (should be prime for performance reasons) */
 #define CACHE_BUCKETS 53
 
@@ -331,6 +339,10 @@
 	ao2_replace(ast_format_t140_red, NULL);
 	ao2_replace(ast_format_t140, NULL);
 	ao2_replace(ast_format_none, NULL);
+	ao2_replace(ast_format_silk8, NULL);
+	ao2_replace(ast_format_silk12, NULL);
+	ao2_replace(ast_format_silk16, NULL);
+	ao2_replace(ast_format_silk24, NULL);
 }
 
 int ast_format_cache_init(void)
@@ -426,6 +438,14 @@
 		ao2_replace(ast_format_t140, format);
 	} else if (!strcmp(name, "none")) {
 		ao2_replace(ast_format_none, format);
+	} else if (!strcmp(name, "silk8")) {
+		ao2_replace(ast_format_silk8, format);
+	} else if (!strcmp(name, "silk12")) {
+		ao2_replace(ast_format_silk12, format);
+	} else if (!strcmp(name, "silk16")) {
+		ao2_replace(ast_format_silk16, format);
+	} else if (!strcmp(name, "silk24")) {
+		ao2_replace(ast_format_silk24, format);
 	}
 }
 
diff --git a/main/rtp_engine.c b/main/rtp_engine.c
index 11e94c6..feadda3 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -2690,6 +2690,11 @@
 	/* Opus and VP8 */
 	set_next_mime_type(ast_format_opus, 0,  "audio", "opus", 48000);
 	set_next_mime_type(ast_format_vp8, 0,  "video", "VP8", 90000);
+	/* DA SILK */
+	set_next_mime_type(ast_format_silk8, 0, "audio", "silk", 8000);
+	set_next_mime_type(ast_format_silk12, 0, "audio", "silk", 12000);
+	set_next_mime_type(ast_format_silk16, 0, "audio", "silk", 16000);
+	set_next_mime_type(ast_format_silk24, 0, "audio", "silk", 24000);
 
 	/* Define the static rtp payload mappings */
 	add_static_payload(0, ast_format_ulaw, 0);
@@ -2743,6 +2748,11 @@
 	add_static_payload(100, ast_format_vp8, 0);
 	add_static_payload(107, ast_format_opus, 0);
 
+	add_static_payload(108, ast_format_silk8, 0);
+	add_static_payload(109, ast_format_silk12, 0);
+	add_static_payload(113, ast_format_silk16, 0);
+	add_static_payload(114, ast_format_silk24, 0);
+
 	return 0;
 }
 
diff --git a/res/res_format_attr_silk.c b/res/res_format_attr_silk.c
index f3f4382..e69e3f4 100644
--- a/res/res_format_attr_silk.c
+++ b/res/res_format_attr_silk.c
@@ -40,7 +40,6 @@
  * \note The only attribute that affects compatibility here is the sample rate.
  */
 struct silk_attr {
-	unsigned int samplerate;
 	unsigned int maxbitrate;
 	unsigned int dtx;
 	unsigned int fec;
@@ -54,10 +53,15 @@
 	ast_free(attr);
 }
 
+static void attr_init(struct silk_attr *attr)
+{
+	memset(attr, 0, sizeof(*attr));
+}
+
 static int silk_clone(const struct ast_format *src, struct ast_format *dst)
 {
 	struct silk_attr *original = ast_format_get_attribute_data(src);
-	struct silk_attr *attr = ast_calloc(1, sizeof(*attr));
+	struct silk_attr *attr = ast_malloc(sizeof(*attr));
 
 	if (!attr) {
 		return -1;
@@ -65,6 +69,8 @@
 
 	if (original) {
 		*attr = *original;
+	} else {
+		attr_init(attr);
 	}
 
 	ast_format_set_attribute_data(dst, attr);
@@ -109,17 +115,17 @@
 		ast_str_append(str, 0, "a=fmtp:%u maxaveragebitrate=%u\r\n", payload, attr->maxbitrate);
 	}
 
-	ast_str_append(str, 0, "a=fmtp:%u usedtx=%u\r\n", payload, attr->dtx);
-	ast_str_append(str, 0, "a=fmtp:%u useinbandfec=%u\r\n", payload, attr->fec);
+	if (attr->dtx) {
+		ast_str_append(str, 0, "a=fmtp:%u usedtx=%u\r\n", payload, attr->dtx);
+	}
+	if (attr->fec) {
+		ast_str_append(str, 0, "a=fmtp:%u useinbandfec=%u\r\n", payload, attr->fec);
+	}
 }
 
 static enum ast_format_cmp_res silk_cmp(const struct ast_format *format1, const struct ast_format *format2)
 {
-	struct silk_attr *attr1 = ast_format_get_attribute_data(format1);
-	struct silk_attr *attr2 = ast_format_get_attribute_data(format2);
-
-	if (((!attr1 || !attr1->samplerate) && (!attr2 || !attr2->samplerate)) ||
-		(attr1->samplerate == attr2->samplerate)) {
+	if (ast_format_get_sample_rate(format1) == ast_format_get_sample_rate(format2)) {
 		return AST_FORMAT_CMP_EQUAL;
 	}
 
@@ -130,13 +136,10 @@
 {
 	struct silk_attr *attr1 = ast_format_get_attribute_data(format1);
 	struct silk_attr *attr2 = ast_format_get_attribute_data(format2);
-	unsigned int samplerate;
 	struct ast_format *jointformat;
 	struct silk_attr *attr_res;
 
-	samplerate = attr1->samplerate & attr2->samplerate;
-	/* sample rate is the only attribute that has any bearing on if joint capabilities exist or not */
-	if (samplerate) {
+	if (ast_format_get_sample_rate(format1) != ast_format_get_sample_rate(format2)) {
 		return NULL;
 	}
 
@@ -145,22 +148,25 @@
 		return NULL;
 	}
 	attr_res = ast_format_get_attribute_data(jointformat);
-	attr_res->samplerate = samplerate;
 
-	/* Take the lowest max bitrate */
-	attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate);
+	if (!attr1 || !attr2) {
+		attr_init(attr_res);
+	} else {
+		/* Take the lowest max bitrate */
+		attr_res->maxbitrate = MIN(attr1->maxbitrate, attr2->maxbitrate);
 
-	/* Only do dtx if both sides want it. DTX is a trade off between
-	 * computational complexity and bandwidth. */
-	attr_res->dtx = attr1->dtx && attr2->dtx ? 1 : 0;
+		/* Only do dtx if both sides want it. DTX is a trade off between
+		 * computational complexity and bandwidth. */
+		attr_res->dtx = attr1->dtx && attr2->dtx ? 1 : 0;
 
-	/* Only do FEC if both sides want it.  If a peer specifically requests not
-	 * to receive with FEC, it may be a waste of bandwidth. */
-	attr_res->fec = attr1->fec && attr2->fec ? 1 : 0;
+		/* Only do FEC if both sides want it.  If a peer specifically requests not
+		 * to receive with FEC, it may be a waste of bandwidth. */
+		attr_res->fec = attr1->fec && attr2->fec ? 1 : 0;
 
-	/* Use the maximum packetloss percentage between the two attributes. This affects how
-	 * much redundancy is used in the FEC. */
-	attr_res->packetloss_percentage = MAX(attr1->packetloss_percentage, attr2->packetloss_percentage);
+		/* Use the maximum packetloss percentage between the two attributes. This affects how
+		 * much redundancy is used in the FEC. */
+		attr_res->packetloss_percentage = MAX(attr1->packetloss_percentage, attr2->packetloss_percentage);
+	}
 
 	return jointformat;
 }
@@ -183,9 +189,7 @@
 	}
 	attr = ast_format_get_attribute_data(cloned);
 
-	if (!strcasecmp(name, "sample_rate")) {
-		attr->samplerate = val;
-	} else if (!strcasecmp(name, "max_bitrate")) {
+	if (!strcasecmp(name, "max_bitrate")) {
 		attr->maxbitrate = val;
 	} else if (!strcasecmp(name, "dtx")) {
 		attr->dtx = val;
@@ -205,9 +209,7 @@
 	struct silk_attr *attr = ast_format_get_attribute_data(format);
 	unsigned int *val;
 
-	if (!strcasecmp(name, "sample_rate")) {
-		val = &attr->samplerate;
-	} else if (!strcasecmp(name, "max_bitrate")) {
+	if (!strcasecmp(name, "max_bitrate")) {
 		val = &attr->maxbitrate;
 	} else if (!strcasecmp(name, "dtx")) {
 		val = &attr->dtx;

-- 
To view, visit https://gerrit.asterisk.org/3137
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ieeb39c95a9fecc9246bcfd3c45a6c9b51c59380e
Gerrit-PatchSet: 3
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Mark Michelson <mmichelson at digium.com>
Gerrit-Reviewer: Anonymous Coward #1000019
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Kevin Harwell <kharwell at digium.com>
Gerrit-Reviewer: Richard Mudgett <rmudgett at digium.com>



More information about the asterisk-code-review mailing list