summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Michelson <mmichelson@digium.com>2016-06-30 15:58:53 -0500
committerMark Michelson <mmichelson@digium.com>2016-07-14 15:54:21 -0500
commit28501051b47e6bb8968bb016abf0b3493c05fa21 (patch)
tree3d7f0e5fbe9f46e37e3033f7dbc44736e42a32ac
parent43a78100c0e51117e7e5c91ed2800c772f767f8a (diff)
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
-rw-r--r--include/asterisk/format_cache.h8
-rw-r--r--main/codec_builtin.c63
-rw-r--r--main/format_cache.c20
-rw-r--r--main/rtp_engine.c10
-rw-r--r--res/res_format_attr_silk.c64
5 files changed, 134 insertions, 31 deletions
diff --git a/include/asterisk/format_cache.h b/include/asterisk/format_cache.h
index 9f4e06a23..ff03bb4aa 100644
--- a/include/asterisk/format_cache.h
+++ b/include/asterisk/format_cache.h
@@ -224,6 +224,14 @@ extern struct ast_format *ast_format_t140_red;
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 d3f65174c..1d329bc3b 100644
--- a/main/codec_builtin.c
+++ b/main/codec_builtin.c
@@ -772,6 +772,65 @@ static struct ast_codec t140 = {
.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 @@ int ast_codec_builtin_init(void)
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 6638a78c0..74ebfe8d5 100644
--- a/main/format_cache.c
+++ b/main/format_cache.c
@@ -232,6 +232,14 @@ struct ast_format *ast_format_t140_red;
*/
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 @@ static void format_cache_shutdown(void)
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 @@ static void set_cached_format(const char *name, struct ast_format *format)
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 462d4c530..8d46bfdcc 100644
--- a/main/rtp_engine.c
+++ b/main/rtp_engine.c
@@ -2198,6 +2198,11 @@ int ast_rtp_engine_init(void)
/* 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);
@@ -2243,6 +2248,11 @@ int ast_rtp_engine_init(void)
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 dcbbe4c1c..d52ec7410 100644
--- a/res/res_format_attr_silk.c
+++ b/res/res_format_attr_silk.c
@@ -40,7 +40,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
* \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 @@ static void silk_destroy(struct ast_format *format)
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 @@ static int silk_clone(const struct ast_format *src, struct ast_format *dst)
if (original) {
*attr = *original;
+ } else {
+ attr_init(attr);
}
ast_format_set_attribute_data(dst, attr);
@@ -109,17 +115,17 @@ static void silk_generate_sdp_fmtp(const struct ast_format *format, unsigned int
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 @@ static struct ast_format *silk_getjoint(const struct ast_format *format1, const
{
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 @@ static struct ast_format *silk_getjoint(const struct ast_format *format1, const
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 @@ static struct ast_format *silk_set(const struct ast_format *format, const char *
}
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 @@ static const void *silk_get(const struct ast_format *format, const char *name)
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;