Update support for SILK format.
authorMark Michelson <mmichelson@digium.com>
Thu, 30 Jun 2016 20:58:53 +0000 (15:58 -0500)
committerMark Michelson <mmichelson@digium.com>
Thu, 14 Jul 2016 20:59:49 +0000 (15:59 -0500)
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

include/asterisk/format_cache.h
main/codec_builtin.c
main/format_cache.c
main/rtp_engine.c
res/res_format_attr_silk.c

index 64e53b9..3894ad2 100644 (file)
@@ -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
index 9131643..50fbf55 100644 (file)
@@ -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;
 }
index def795c..b4d4260 100644 (file)
@@ -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);
        }
 }
 
index 11e94c6..feadda3 100644 (file)
@@ -2690,6 +2690,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);
@@ -2743,6 +2748,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;
 }
 
index f3f4382..e69e3f4 100644 (file)
@@ -40,7 +40,6 @@ ASTERISK_REGISTER_FILE()
  * \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;