mirror of
https://github.com/asterisk/asterisk.git
synced 2025-10-12 15:45:18 +00:00
res_aeap & res_speech_aeap: Add Asterisk External Application Protocol
Add framework to connect to, and read and write protocol based messages from and to an external application using an Asterisk External Application Protocol (AEAP). This has been divided into several abstractions: 1. transport - base communication layer (currently websocket only) 2. message - AEAP description and data (currently JSON only) 3. transaction - links/binds requests and responses 4. aeap - transport, message, and transaction handler/manager This patch also adds an AEAP implementation for speech to text. Existing speech API callbacks for speech to text have been completed making it possible for Asterisk to connect to a configured external translator service and provide audio for STT. Results can also be received from the external translator, and made available as speech results in Asterisk. Unit tests have also been created that test the AEAP framework, and also the speech to text implementation. ASTERISK-29726 #close Change-Id: Iaa4b259f84aa63501e5fd2a6fb107f900b4d4ed2
This commit is contained in:
committed by
Friendly Automation
parent
53a3af6321
commit
272bac70dd
198
res/res_aeap.c
198
res/res_aeap.c
@@ -17,29 +17,37 @@
|
||||
*/
|
||||
|
||||
/*** MODULEINFO
|
||||
<depend>res_http_websocket</depend>
|
||||
<support_level>core</support_level>
|
||||
***/
|
||||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/astobj2.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/sorcery.h"
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/format.h"
|
||||
#include "asterisk/format_cap.h"
|
||||
#include "asterisk/res_aeap.h"
|
||||
|
||||
#include "res_aeap/general.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<configInfo name="res_aeap" language="en_US">
|
||||
<synopsis>Asterisk External Application Protocol (AEAP) module for Asterisk</synopsis>
|
||||
<configFile name="aeap.conf">
|
||||
<configObject name="server">
|
||||
<synopsis>AEAP server options</synopsis>
|
||||
<configObject name="client">
|
||||
<synopsis>AEAP client options</synopsis>
|
||||
<configOption name="type">
|
||||
<synopsis>Must be of type 'server'.</synopsis>
|
||||
<synopsis>Must be of type 'client'.</synopsis>
|
||||
</configOption>
|
||||
<configOption name="server_url">
|
||||
<configOption name="url">
|
||||
<synopsis>The URL of the server to connect to.</synopsis>
|
||||
</configOption>
|
||||
<configOption name="protocol">
|
||||
<synopsis>The application protocol.</synopsis>
|
||||
</configOption>
|
||||
<configOption name="codecs">
|
||||
<synopsis>Optional media codec(s)</synopsis>
|
||||
<description><para>
|
||||
@@ -56,30 +64,36 @@
|
||||
/* Asterisk External Application Protocol sorcery object */
|
||||
static struct ast_sorcery *aeap_sorcery;
|
||||
|
||||
struct aeap_server
|
||||
struct ast_sorcery *ast_aeap_sorcery(void) {
|
||||
return aeap_sorcery;
|
||||
}
|
||||
|
||||
struct ast_aeap_client_config
|
||||
{
|
||||
SORCERY_OBJECT(details);
|
||||
AST_DECLARE_STRING_FIELDS(
|
||||
/*! The URL of the server to connect to */
|
||||
AST_STRING_FIELD(server_url);
|
||||
AST_STRING_FIELD(url);
|
||||
/*! The application protocol */
|
||||
AST_STRING_FIELD(protocol);
|
||||
);
|
||||
/*! An optional list of codecs that will be used if provided */
|
||||
struct ast_format_cap *codecs;
|
||||
};
|
||||
|
||||
static void aeap_server_destructor(void *obj)
|
||||
static void client_config_destructor(void *obj)
|
||||
{
|
||||
struct aeap_server *cfg = obj;
|
||||
struct ast_aeap_client_config *cfg = obj;
|
||||
|
||||
ast_string_field_free_memory(cfg);
|
||||
ao2_cleanup(cfg->codecs);
|
||||
}
|
||||
|
||||
static void *aeap_server_alloc(const char *name)
|
||||
static void *client_config_alloc(const char *name)
|
||||
{
|
||||
struct aeap_server *cfg;
|
||||
struct ast_aeap_client_config *cfg;
|
||||
|
||||
cfg = ast_sorcery_generic_alloc(sizeof(*cfg), aeap_server_destructor);
|
||||
cfg = ast_sorcery_generic_alloc(sizeof(*cfg), client_config_destructor);
|
||||
if (!cfg) {
|
||||
return NULL;
|
||||
}
|
||||
@@ -97,32 +111,52 @@ static void *aeap_server_alloc(const char *name)
|
||||
return cfg;
|
||||
}
|
||||
|
||||
static int aeap_server_apply(const struct ast_sorcery *sorcery, void *obj)
|
||||
static int client_config_apply(const struct ast_sorcery *sorcery, void *obj)
|
||||
{
|
||||
struct aeap_server *cfg = obj;
|
||||
struct ast_aeap_client_config *cfg = obj;
|
||||
|
||||
if (ast_strlen_zero(cfg->server_url)) {
|
||||
ast_log(LOG_ERROR, "AEAP - Server URL must be present for server '%s'\n", ast_sorcery_object_get_id(cfg));
|
||||
if (ast_strlen_zero(cfg->url)) {
|
||||
ast_log(LOG_ERROR, "AEAP - URL must be present for '%s'\n", ast_sorcery_object_get_id(cfg));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ast_begins_with(cfg->server_url, "ws")) {
|
||||
ast_log(LOG_ERROR, "AEAP - Server URL must be ws or wss for server '%s'\n", ast_sorcery_object_get_id(cfg));
|
||||
if (!ast_begins_with(cfg->url, "ws")) {
|
||||
ast_log(LOG_ERROR, "AEAP - URL must be ws or wss for '%s'\n", ast_sorcery_object_get_id(cfg));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct aeap_server *aeap_server_get(const char *id)
|
||||
const struct ast_format_cap *ast_aeap_client_config_codecs(const struct ast_aeap_client_config *cfg)
|
||||
{
|
||||
return ast_sorcery_retrieve_by_id(aeap_sorcery, "server", id);
|
||||
return cfg->codecs;
|
||||
}
|
||||
|
||||
static struct ao2_container *aeap_server_get_all(void)
|
||||
int ast_aeap_client_config_has_protocol(const struct ast_aeap_client_config *cfg,
|
||||
const char *protocol)
|
||||
{
|
||||
return ast_sorcery_retrieve_by_fields(aeap_sorcery, "server",
|
||||
AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, NULL);
|
||||
return !strcmp(protocol, cfg->protocol);
|
||||
}
|
||||
|
||||
struct ao2_container *ast_aeap_client_configs_get(const char *protocol)
|
||||
{
|
||||
struct ao2_container *container;
|
||||
struct ast_variable *var;
|
||||
|
||||
var = protocol ? ast_variable_new("protocol ==", protocol, "") : NULL;
|
||||
|
||||
container = ast_sorcery_retrieve_by_fields(aeap_sorcery,
|
||||
AEAP_CONFIG_CLIENT, AST_RETRIEVE_FLAG_MULTIPLE | AST_RETRIEVE_FLAG_ALL, var);
|
||||
|
||||
ast_variables_destroy(var);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
static struct ast_aeap_client_config *client_config_get(const char *id)
|
||||
{
|
||||
return ast_sorcery_retrieve_by_id(aeap_sorcery, AEAP_CONFIG_CLIENT, id);
|
||||
}
|
||||
|
||||
static char *aeap_tab_complete_name(const char *word, struct ao2_container *container)
|
||||
@@ -145,6 +179,8 @@ static char *aeap_tab_complete_name(const char *word, struct ao2_container *cont
|
||||
}
|
||||
ao2_iterator_destroy(&it);
|
||||
|
||||
ao2_ref(container, -1);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@@ -159,8 +195,7 @@ static int aeap_cli_show(void *obj, void *arg, int flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
options = ast_variable_list_sort(ast_sorcery_objectset_create2(
|
||||
aeap_sorcery, obj, AST_HANDLER_ONLY_STRING));
|
||||
options = ast_variable_list_sort(ast_sorcery_objectset_create(aeap_sorcery, obj));
|
||||
if (!options) {
|
||||
return 0;
|
||||
}
|
||||
@@ -179,20 +214,20 @@ static int aeap_cli_show(void *obj, void *arg, int flags)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *aeap_server_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
static char *client_config_show(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
struct aeap_server *cfg;
|
||||
struct ast_aeap_client_config *cfg;
|
||||
|
||||
switch(cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "aeap show server";
|
||||
e->command = "aeap show client";
|
||||
e->usage =
|
||||
"Usage: aeap show server <id>\n"
|
||||
" Show the AEAP settings for a given server\n";
|
||||
"Usage: aeap show client <id>\n"
|
||||
" Show the AEAP settings for a given client\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
if (a->pos == 3) {
|
||||
return aeap_tab_complete_name(a->word, aeap_server_get_all());
|
||||
return aeap_tab_complete_name(a->word, ast_aeap_client_configs_get(NULL));
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
@@ -202,23 +237,23 @@ static char *aeap_server_show(struct ast_cli_entry *e, int cmd, struct ast_cli_a
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
cfg = aeap_server_get(a->argv[3]);
|
||||
cfg = client_config_get(a->argv[3]);
|
||||
aeap_cli_show(cfg, a, 0);
|
||||
ao2_cleanup(cfg);
|
||||
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
static char *aeap_server_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
static char *client_config_show_all(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
struct ao2_container *container;
|
||||
|
||||
switch(cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "aeap show servers";
|
||||
e->command = "aeap show clients";
|
||||
e->usage =
|
||||
"Usage: aeap show servers\n"
|
||||
" Show all configured AEAP servers\n";
|
||||
"Usage: aeap show clients\n"
|
||||
" Show all configured AEAP clients\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
return NULL;
|
||||
@@ -228,9 +263,9 @@ static char *aeap_server_show_all(struct ast_cli_entry *e, int cmd, struct ast_c
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
container = aeap_server_get_all();
|
||||
container = ast_aeap_client_configs_get(NULL);
|
||||
if (!container || ao2_container_count(container) == 0) {
|
||||
ast_cli(a->fd, "No AEAP servers found\n");
|
||||
ast_cli(a->fd, "No AEAP clients found\n");
|
||||
ao2_cleanup(container);
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
@@ -242,12 +277,75 @@ static char *aeap_server_show_all(struct ast_cli_entry *e, int cmd, struct ast_c
|
||||
}
|
||||
|
||||
static struct ast_cli_entry aeap_cli[] = {
|
||||
AST_CLI_DEFINE(aeap_server_show, "Show AEAP server configuration by id"),
|
||||
AST_CLI_DEFINE(aeap_server_show_all, "Show all AEAP server configurations"),
|
||||
AST_CLI_DEFINE(client_config_show, "Show AEAP client configuration by id"),
|
||||
AST_CLI_DEFINE(client_config_show_all, "Show all AEAP client configurations"),
|
||||
};
|
||||
|
||||
static struct ast_aeap *aeap_create(const char *id, const struct ast_aeap_params *params,
|
||||
int connect, int timeout)
|
||||
{
|
||||
struct ast_aeap_client_config *cfg;
|
||||
struct ast_aeap *aeap;
|
||||
const char *url = NULL;
|
||||
const char *protocol = NULL;
|
||||
|
||||
cfg = client_config_get(id);
|
||||
if (cfg) {
|
||||
url = cfg->url;
|
||||
protocol = cfg->protocol;
|
||||
}
|
||||
|
||||
#ifdef TEST_FRAMEWORK
|
||||
else if (ast_begins_with(id, "_aeap_test_")) {
|
||||
url = "ws://127.0.0.1:8088/ws";
|
||||
protocol = id;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!url && !protocol) {
|
||||
ast_log(LOG_ERROR, "AEAP: unable to get configuration for '%s'\n", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
aeap = connect ? ast_aeap_create_and_connect(url, params, url, protocol, timeout) :
|
||||
ast_aeap_create(url, params);
|
||||
|
||||
ao2_cleanup(cfg);
|
||||
return aeap;
|
||||
}
|
||||
|
||||
struct ast_aeap *ast_aeap_create_by_id(const char *id, const struct ast_aeap_params *params)
|
||||
{
|
||||
return aeap_create(id, params, 0, 0);
|
||||
}
|
||||
|
||||
struct ast_aeap *ast_aeap_create_and_connect_by_id(const char *id,
|
||||
const struct ast_aeap_params *params, int timeout)
|
||||
{
|
||||
return aeap_create(id, params, 1, timeout);
|
||||
}
|
||||
|
||||
struct ast_variable *ast_aeap_custom_fields_get(const char *id)
|
||||
{
|
||||
struct ast_aeap_client_config *cfg;
|
||||
struct ast_variable *vars;
|
||||
|
||||
cfg = client_config_get(id);
|
||||
if (!cfg) {
|
||||
ast_log(LOG_WARNING, "AEAP: no client configuration '%s' to get fields\n", id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
vars = ast_sorcery_objectset_create(aeap_sorcery, cfg);
|
||||
|
||||
ao2_ref(cfg, -1);
|
||||
return vars;
|
||||
}
|
||||
|
||||
static int reload_module(void)
|
||||
{
|
||||
ast_sorcery_reload(aeap_sorcery);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -258,28 +356,35 @@ static int unload_module(void)
|
||||
|
||||
ast_cli_unregister_multiple(aeap_cli, ARRAY_LEN(aeap_cli));
|
||||
|
||||
aeap_general_finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_module(void)
|
||||
{
|
||||
if (aeap_general_initialize()) {
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
if (!(aeap_sorcery = ast_sorcery_open()))
|
||||
{
|
||||
ast_log(LOG_ERROR, "AEAP - failed to open sorcery\n");
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
ast_sorcery_apply_default(aeap_sorcery, "server", "config", "aeap.conf,criteria=type=server");
|
||||
ast_sorcery_apply_default(aeap_sorcery, AEAP_CONFIG_CLIENT, "config", "aeap.conf,criteria=type=client");
|
||||
|
||||
if (ast_sorcery_object_register(aeap_sorcery, "server", aeap_server_alloc,
|
||||
NULL, aeap_server_apply)) {
|
||||
ast_log(LOG_ERROR, "AEAP - failed to register server sorcery object\n");
|
||||
if (ast_sorcery_object_register(aeap_sorcery, "client", client_config_alloc,
|
||||
NULL, client_config_apply)) {
|
||||
ast_log(LOG_ERROR, "AEAP - failed to register client sorcery object\n");
|
||||
return AST_MODULE_LOAD_DECLINE;
|
||||
}
|
||||
|
||||
ast_sorcery_object_field_register(aeap_sorcery, "server", "type", "", OPT_NOOP_T, 0, 0);
|
||||
ast_sorcery_object_field_register(aeap_sorcery, "server", "server_url", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct aeap_server, server_url));
|
||||
ast_sorcery_object_field_register(aeap_sorcery, "server", "codecs", "", OPT_CODEC_T, 1, FLDSET(struct aeap_server, codecs));
|
||||
ast_sorcery_object_field_register(aeap_sorcery, AEAP_CONFIG_CLIENT, "type", "", OPT_NOOP_T, 0, 0);
|
||||
ast_sorcery_object_field_register(aeap_sorcery, AEAP_CONFIG_CLIENT, "url", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_aeap_client_config, url));
|
||||
ast_sorcery_object_field_register(aeap_sorcery, AEAP_CONFIG_CLIENT, "protocol", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_aeap_client_config, protocol));
|
||||
ast_sorcery_object_field_register(aeap_sorcery, AEAP_CONFIG_CLIENT, "codecs", "", OPT_CODEC_T, 1, FLDSET(struct ast_aeap_client_config, codecs));
|
||||
|
||||
ast_sorcery_load(aeap_sorcery);
|
||||
|
||||
@@ -295,4 +400,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_
|
||||
.unload = unload_module,
|
||||
.reload = reload_module,
|
||||
.load_pri = AST_MODPRI_CHANNEL_DEPEND,
|
||||
.requires = "res_http_websocket",
|
||||
);
|
||||
|
Reference in New Issue
Block a user