diff --git a/include/asterisk/format_cache.h b/include/asterisk/format_cache.h index 64e53b9c5feb1072162f40494c5ac6704a458828..3894ad21bcc2c401020e83bae923efadf6a19cef 100644 --- a/include/asterisk/format_cache.h +++ b/include/asterisk/format_cache.h @@ -223,6 +223,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. * diff --git a/main/codec_builtin.c b/main/codec_builtin.c index 9131643287d90f4ddb34695d4e6b79f4f121e5b9..50fbf555c03c9bdd2808dd864781e349c59d594b 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 def795c9d4028c948d8a98eb608110dafb78e4a9..b4d426092510cdb1ede8d567a58b2b25dc782e8f 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 11e94c699a9c9dc5c4899796f438324201d4d428..feadda3d379c2ba4ff153015948f6a72b3d23fbd 100644 --- a/main/rtp_engine.c +++ b/main/rtp_engine.c @@ -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; } diff --git a/res/res_format_attr_silk.c b/res/res_format_attr_silk.c index f3f43821c8d031aab33227a72c246efa36b188c1..e69e3f43c1f63b7a2f383fd7ea51f116d0fe268b 100644 --- a/res/res_format_attr_silk.c +++ b/res/res_format_attr_silk.c @@ -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;