<p>Seán C. McCord has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/11579">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">feat: AudioSocket channel and application<br><br>This commit adds support for<br>[AudioSocket](https://github.com/CyCoreSystems/audiosocket), a very<br>simple bidirectional audio streaming protocol. There are both channel<br>and application interfaces.<br><br>A description of the protocol can be found on the above referenced<br>GitHub page. A short talk about the reasons and implementation can be<br>found on [YouTube](https://www.youtube.com/watch?v=tjduXbZZEgI), from<br>CommCon 2019.<br><br>ASTERISK-28484 #close<br><br>Change-Id: Ie866e6c4fa13178ec76f2a6971ad3590a3a588b5<br>Signed-off-by: Seán C McCord <ulexus@gmail.com><br>---<br>A apps/app_audiosocket.c<br>A channels/chan_audiosocket.c<br>A include/asterisk/res_audiosocket.h<br>A res/res_audiosocket.c<br>A res/res_audiosocket.exports.in<br>5 files changed, 812 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/79/11579/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/apps/app_audiosocket.c b/apps/app_audiosocket.c</span><br><span>new file mode 100644</span><br><span>index 0000000..99916b8</span><br><span>--- /dev/null</span><br><span>+++ b/apps/app_audiosocket.c</span><br><span>@@ -0,0 +1,180 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * app_audiosocket</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2018, CyCore Systems, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Seán C McCord <scm@cycoresys.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief AudioSocket application -- transmit and receive audio through a TCP socket</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \author Seán C McCord <scm@cycoresys.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \ingroup applications</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+ <depend>res_audiosocket</depend></span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>core</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "errno.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include <uuid/uuid.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/file.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/channel.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/app.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/uuid.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_audiosocket.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/utils.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/format_cache.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define AST_MODULE "app_audiosocket"</span><br><span style="color: hsl(120, 100%, 40%);">+//#define AST_MODULE_SELF "app_audiosocket"</span><br><span style="color: hsl(120, 100%, 40%);">+#define AUDIOSOCKET_CONFIG "audiosocket.conf"</span><br><span style="color: hsl(120, 100%, 40%);">+#define MAX_CONNECT_TIMEOUT_MSEC 2000</span><br><span style="color: hsl(120, 100%, 40%);">+#define CHANNEL_INPUT_TIMEOUT_MS 5000</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** DOCUMENTATION</span><br><span style="color: hsl(120, 100%, 40%);">+ <application name="AudioSocket" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Transmit and receive audio between channel and TCP socket</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="uuid" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>UUID is the universally-unique identifier of the call for the audio socket service. This ID must conform to the string form of a standard UUID.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="service" required="true"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Service is the name or IP address and port number of the audio socket service to which this call should be connected. This should be in the form host:port, such as myserver:9019 </para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Connects to the given TCP service, then transmits channel audio over that socket. In turn, audio is received from the socket and sent to the channel. Only audio frames will be transmitted.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Protocol is specified at https://github.com/CyCoreSystems/audiosocket/.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>This application does not automatically answer and should generally be</span><br><span style="color: hsl(120, 100%, 40%);">+ preceeded by an application such as Answer() or Progress().</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </application></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const char app[] = "AudioSocket";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int audiosocket_exec(struct ast_channel *chan, const char *data);</span><br><span style="color: hsl(120, 100%, 40%);">+static int audiosocket_run(struct ast_channel *chan, const char *id, const int svc);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int audiosocket_exec(struct ast_channel *chan, const char *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char *parse;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(idStr);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(server);</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ int s = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_uuid *id = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Parse and validate arguments */</span><br><span style="color: hsl(120, 100%, 40%);">+ parse = ast_strdupa(data);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(args, parse);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(args.idStr)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "UUID is required\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if ( (id = ast_str_to_uuid(args.idStr)) == NULL ) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "UUID '%s' could not be parsed\n", args.idStr);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if( (s = audiosocket_connect(args.server)) < 0 ) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "failed to connect to AudioSocket\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ audiosocket_run(chan, args.idStr, s);</span><br><span style="color: hsl(120, 100%, 40%);">+ close(s);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int audiosocket_run(struct ast_channel *chan, const char *id, const int svc) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_set_write_format(chan, ast_format_slin)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to set write format to SLINEAR\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_set_read_format(chan, ast_format_slin)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to set read format to SLINEAR\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (audiosocket_init(svc, id)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ while (ast_waitfor(chan, CHANNEL_INPUT_TIMEOUT_MS) > -1) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ // Check channel state</span><br><span style="color: hsl(120, 100%, 40%);">+ if( ast_channel_state(chan) != AST_STATE_UP ) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_frame *f = ast_read(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if(!f) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "No frame received\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ f->delivery.tv_sec = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ f->delivery.tv_usec = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (f->frametype == AST_FRAME_VOICE) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ // Send audio frame to audiosocket</span><br><span style="color: hsl(120, 100%, 40%);">+ if(audiosocket_send_frame(svc, f)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to forward channel frame to audiosocket\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_frfree(f);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_frfree(f);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ // Send audiosocket data to channel</span><br><span style="color: hsl(120, 100%, 40%);">+ if(!(f = audiosocket_receive_frame(svc))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to receive frame from audiosocket message\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if(ast_write(chan, f)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Failed to forward frame to channel\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_unregister_application(app);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_register_application_xml(app, audiosocket_exec);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "AudioSocket Application",</span><br><span style="color: hsl(120, 100%, 40%);">+ .support_level = AST_MODULE_SUPPORT_CORE,</span><br><span style="color: hsl(120, 100%, 40%);">+ .load = load_module,</span><br><span style="color: hsl(120, 100%, 40%);">+ .unload = unload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+ .load_pri = AST_MODPRI_CHANNEL_DRIVER,</span><br><span style="color: hsl(120, 100%, 40%);">+ .requires = "res_audiosocket",</span><br><span style="color: hsl(120, 100%, 40%);">+);</span><br><span>diff --git a/channels/chan_audiosocket.c b/channels/chan_audiosocket.c</span><br><span>new file mode 100644</span><br><span>index 0000000..9f6340e</span><br><span>--- /dev/null</span><br><span>+++ b/channels/chan_audiosocket.c</span><br><span>@@ -0,0 +1,237 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2019, CyCore Systems, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Seán C McCord <scm@cycoresys.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \author Seán C McCord <scm@cycoresys.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief AudioSocket Channel</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \ingroup channel_drivers</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+ <depend>res_audiosocket</depend></span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>core</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include <uuid/uuid.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/channel.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/uuid.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_audiosocket.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/pbx.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/acl.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/app.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/causes.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/format_cache.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct audiosocket_instance {</span><br><span style="color: hsl(120, 100%, 40%);">+ int svc;</span><br><span style="color: hsl(120, 100%, 40%);">+ char id[38];</span><br><span style="color: hsl(120, 100%, 40%);">+} audiosocket_instance;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Forward declarations */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_channel *audiosocket_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause);</span><br><span style="color: hsl(120, 100%, 40%);">+static int audiosocket_call(struct ast_channel *ast, const char *dest, int timeout);</span><br><span style="color: hsl(120, 100%, 40%);">+static int audiosocket_hangup(struct ast_channel *ast);</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_frame *audiosocket_read(struct ast_channel *ast);</span><br><span style="color: hsl(120, 100%, 40%);">+static int audiosocket_write(struct ast_channel *ast, struct ast_frame *f);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* AudioSocket channel driver declaration */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_channel_tech audiosocket_channel_tech = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .type = "AudioSocket",</span><br><span style="color: hsl(120, 100%, 40%);">+ .description = "AudioSocket Channel Driver",</span><br><span style="color: hsl(120, 100%, 40%);">+ .requester = audiosocket_request,</span><br><span style="color: hsl(120, 100%, 40%);">+ .call = audiosocket_call,</span><br><span style="color: hsl(120, 100%, 40%);">+ .hangup = audiosocket_hangup,</span><br><span style="color: hsl(120, 100%, 40%);">+ .read = audiosocket_read,</span><br><span style="color: hsl(120, 100%, 40%);">+ .write = audiosocket_write,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Function called when we should read a frame from the channel */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_frame *audiosocket_read(struct ast_channel *ast)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct audiosocket_instance *instance = ast_channel_tech_pvt(ast);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (instance == NULL || instance->svc < 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return audiosocket_receive_frame(instance->svc);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Function called when we should write a frame to the channel */</span><br><span style="color: hsl(120, 100%, 40%);">+static int audiosocket_write(struct ast_channel *ast, struct ast_frame *f)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct audiosocket_instance *instance = ast_channel_tech_pvt(ast);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (instance == NULL || instance->svc < 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return audiosocket_send_frame(instance->svc, f);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Function called when we should actually call the destination */</span><br><span style="color: hsl(120, 100%, 40%);">+static int audiosocket_call(struct ast_channel *ast, const char *dest, int timeout)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct audiosocket_instance *instance = ast_channel_tech_pvt(ast);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_queue_control(ast, AST_CONTROL_ANSWER);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_set_write_format(ast, ast_format_slin)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to set write format to SLINEAR\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_set_read_format(ast, ast_format_slin)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to set read format to SLINEAR\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return audiosocket_init(instance->svc, instance->id);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Function called when we should hang the channel up */</span><br><span style="color: hsl(120, 100%, 40%);">+static int audiosocket_hangup(struct ast_channel *ast)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct audiosocket_instance *instance = ast_channel_tech_pvt(ast);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if(instance != NULL && instance->svc > 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ close(instance->svc);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_tech_pvt_set(ast, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Function called when we should prepare to call the unicast destination */</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_channel *audiosocket_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char *parse;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct audiosocket_instance *instance;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sockaddr address;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_uuid *id = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ int fd;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(destination);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(idStr);</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(data)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Destination is required for the 'AudioSocket' channel\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ goto failure;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ parse = ast_strdupa(data);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_NONSTANDARD_APP_ARGS(args, parse, '/');</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(args.destination)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Destination is required for the 'AudioSocket' channel\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ goto failure;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_sockaddr_resolve_first_af(&address, args.destination, PARSE_PORT_REQUIRE, AST_AF_UNSPEC)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Destination '%s' could not be parsed\n", args.destination);</span><br><span style="color: hsl(120, 100%, 40%);">+ goto failure;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(args.idStr)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "UUID is required for the 'AudioSocket' channel\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ goto failure;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if ( (id = ast_str_to_uuid(args.idStr)) == NULL ) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "UUID '%s' could not be parsed\n", args.idStr);</span><br><span style="color: hsl(120, 100%, 40%);">+ goto failure;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ instance = ast_calloc(1, sizeof(*instance));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(instance->id, args.idStr, sizeof(instance->id));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ //instance.id = args.idStr;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if(ast_format_cap_iscompatible_format(cap, ast_format_slin) == AST_FORMAT_CMP_NOT_EQUAL) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *cap_buf = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_NOTICE, "Asked to get a channel of unsupported format '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_get_names(cap, &cap_buf));</span><br><span style="color: hsl(120, 100%, 40%);">+ goto failure;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if( (fd = audiosocket_connect(args.destination)) < 0 ) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to connect to AudioSocket server at '%s'\n", args.destination);</span><br><span style="color: hsl(120, 100%, 40%);">+ goto failure;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ instance->svc = fd;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", assignedids,</span><br><span style="color: hsl(120, 100%, 40%);">+ requestor, 0, "AudioSocket/%s-%s", args.destination, args.idStr);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!chan) {</span><br><span style="color: hsl(120, 100%, 40%);">+ goto failure;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_set_fd(chan, 0, fd);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_tech_set(chan, &audiosocket_channel_tech);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_nativeformats_set(chan, audiosocket_channel_tech.capabilities);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_set_writeformat(chan, ast_format_slin);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_set_rawwriteformat(chan, ast_format_slin);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_set_readformat(chan, ast_format_slin);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_set_rawreadformat(chan, ast_format_slin);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_tech_pvt_set(chan, instance);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "AUDIOSOCKET_UUID", args.idStr);</span><br><span style="color: hsl(120, 100%, 40%);">+ pbx_builtin_setvar_helper(chan, "AUDIOSOCKET_SERVICE", args.destination);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unlock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return chan;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+failure:</span><br><span style="color: hsl(120, 100%, 40%);">+ *cause = AST_CAUSE_FAILURE;</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Function called when our module is unloaded */</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unregister(&audiosocket_channel_tech);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(audiosocket_channel_tech.capabilities);</span><br><span style="color: hsl(120, 100%, 40%);">+ audiosocket_channel_tech.capabilities = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Function called when our module is loaded */</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(audiosocket_channel_tech.capabilities = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_format_cap_append(audiosocket_channel_tech.capabilities, ast_format_slin, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_channel_register(&audiosocket_channel_tech)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Unable to register channel class AudioSocket");</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(audiosocket_channel_tech.capabilities, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ audiosocket_channel_tech.capabilities = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_MODULE_LOAD_DECLINE;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_MODULE_LOAD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "AudioSocket Channel",</span><br><span style="color: hsl(120, 100%, 40%);">+ .support_level = AST_MODULE_SUPPORT_CORE,</span><br><span style="color: hsl(120, 100%, 40%);">+ .load = load_module,</span><br><span style="color: hsl(120, 100%, 40%);">+ .unload = unload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+ .load_pri = AST_MODPRI_CHANNEL_DRIVER,</span><br><span style="color: hsl(120, 100%, 40%);">+ .requires = "res_audiosocket",</span><br><span style="color: hsl(120, 100%, 40%);">+);</span><br><span>diff --git a/include/asterisk/res_audiosocket.h b/include/asterisk/res_audiosocket.h</span><br><span>new file mode 100644</span><br><span>index 0000000..0590489</span><br><span>--- /dev/null</span><br><span>+++ b/include/asterisk/res_audiosocket.h</span><br><span>@@ -0,0 +1,81 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2019, CyCore Systems, Inc.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Seán C McCord <scm@cycoresys.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \file</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief AudioSocket support functions</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \author Seán C McCord <scm@cycoresys.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#ifndef _ASTERISK_RES_AUDIOSOCKET_H</span><br><span style="color: hsl(120, 100%, 40%);">+#define _ASTERISK_RES_AUDIOSOCKET_H</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#if defined(__cplusplus) || defined(c_plusplus)</span><br><span style="color: hsl(120, 100%, 40%);">+extern "C" {</span><br><span style="color: hsl(120, 100%, 40%);">+#endif</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <uuid/uuid.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/frame.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/uuid.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Send the initial message to an AudioSocket server</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param server The server address, including port.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval socket file descriptor for AudioSocket on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const int audiosocket_connect(const char *server);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Send the initial message to an AudioSocket server</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param svc The file descriptor of the network socket to the AudioSocket server.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param id The UUID to send to the AudioSocket server to uniquely identify this connection.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const int audiosocket_init(const int svc, const char *id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Send an Asterisk audio frame to an AudioSocket server</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param svc The file descriptor of the network socket to the AudioSocket server.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param f The Asterisk audio frame to send.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval -1 on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const int audiosocket_send_frame(const int svc, const struct ast_frame *f);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Receive an Asterisk frame from an AudioSocket server</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This returned object is an ao2 reference counted object.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Any attribute in the returned \ref hepv3_capture_info that is a</span><br><span style="color: hsl(120, 100%, 40%);">+ * pointer should point to something that is allocated on the heap,</span><br><span style="color: hsl(120, 100%, 40%);">+ * as it will be free'd when the \ref hepv3_capture_info object is</span><br><span style="color: hsl(120, 100%, 40%);">+ * reclaimed.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param payload The payload to send to the HEP capture node</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param len Length of \ref payload</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval A \ref ast_frame on success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL on error</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_frame *audiosocket_receive_frame(const int svc);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#endif /* _ASTERISK_RES_AUDIOSOCKET_H */</span><br><span>diff --git a/res/res_audiosocket.c b/res/res_audiosocket.c</span><br><span>new file mode 100644</span><br><span>index 0000000..c8f6e53</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_audiosocket.c</span><br><span>@@ -0,0 +1,305 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Copyright (C) 2019, CyCore Systems, Inc</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Seán C McCord <scm@cycoresys.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * This program is free software, distributed under the terms of</span><br><span style="color: hsl(120, 100%, 40%);">+ * the GNU General Public License Version 2. See the LICENSE file</span><br><span style="color: hsl(120, 100%, 40%);">+ * at the top of the source tree.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \file</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief AudioSocket support for Asterisk</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \author Seán C McCord <scm@cycoresys.com></span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*** MODULEINFO</span><br><span style="color: hsl(120, 100%, 40%);">+ <support_level>core</support_level></span><br><span style="color: hsl(120, 100%, 40%);">+ ***/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "errno.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include <uuid/uuid.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/file.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/res_audiosocket.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/channel.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/module.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/uuid.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/format_cache.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define MODULE_DESCRIPTION "AudioSocket support functions for Asterisk"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define MAX_CONNECT_TIMEOUT_MSEC 2000</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_audiosocket_connection(const char *server, const struct ast_sockaddr addr, const int netsockfd);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const int audiosocket_connect(const char *server) {</span><br><span style="color: hsl(120, 100%, 40%);">+ int s = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_sockaddr *addrs;</span><br><span style="color: hsl(120, 100%, 40%);">+ int num_addrs = 0, i = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(server)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "no AudioSocket server provided");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(num_addrs = ast_sockaddr_resolve(&addrs, server, PARSE_PORT_REQUIRE, AST_AF_UNSPEC))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "failed to resolve AudioSocket service");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Connect to AudioSocket service */</span><br><span style="color: hsl(120, 100%, 40%);">+ for (i = 0; i < num_addrs; i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_sockaddr_port(&addrs[i])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "no port provided");</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((s = socket(addrs[i].ss.ss_family, SOCK_STREAM, IPPROTO_TCP)) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "unable to create socket: %s\n", strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_fd_set_flags(s, O_NONBLOCK)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ close(s);</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_connect(s, &addrs[i]) && errno == EINPROGRESS) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (handle_audiosocket_connection(server, addrs[i], s)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ close(s);</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "connection to %s failed with unexpected error: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sockaddr_stringify(&addrs[i]), strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(addrs);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (i == num_addrs) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "failed to connect to AudioSocket service");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return s;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Handle the connection that was started by launch_netscript.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param server Url that we are trying to connect to.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param addr Address that host was resolved to.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param netsockfd File descriptor of socket.</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 when connection is succesful.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 1 when there is an error.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static int handle_audiosocket_connection(const char *server, const struct ast_sockaddr addr, const int netsockfd)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct pollfd pfds[1];</span><br><span style="color: hsl(120, 100%, 40%);">+ int res, conresult;</span><br><span style="color: hsl(120, 100%, 40%);">+ socklen_t reslen;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ reslen = sizeof(conresult);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ pfds[0].fd = netsockfd;</span><br><span style="color: hsl(120, 100%, 40%);">+ pfds[0].events = POLLOUT;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((res = ast_poll(pfds, 1, MAX_CONNECT_TIMEOUT_MSEC)) != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (errno != EINTR) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!res) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "AudioSocket connection to '%s' timed out after MAX_CONNECT_TIMEOUT_MSEC (%d) milliseconds.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ server, MAX_CONNECT_TIMEOUT_MSEC);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", server, strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (getsockopt(pfds[0].fd, SOL_SOCKET, SO_ERROR, &conresult, &reslen) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Connection to %s failed with error: %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sockaddr_stringify(&addr), strerror(errno));</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (conresult) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Connecting to '%s' failed for url '%s': %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sockaddr_stringify(&addr), server, strerror(conresult));</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const int audiosocket_init(const int svc, const char *id) {</span><br><span style="color: hsl(120, 100%, 40%);">+ uuid_t uu;</span><br><span style="color: hsl(120, 100%, 40%);">+ //char idBuf[AST_UUID_STR_LEN+1];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(id)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "No UUID for AudioSocket");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (uuid_parse(id, uu)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to parse UUID '%s'\n", id);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ usleep(100);</span><br><span style="color: hsl(120, 100%, 40%);">+ int ret = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = ast_malloc(3+16);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ buf[0] = 0x01;</span><br><span style="color: hsl(120, 100%, 40%);">+ buf[1] = 0x00;</span><br><span style="color: hsl(120, 100%, 40%);">+ buf[2] = 0x10;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(buf+3, uu, 16);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (write(svc, buf, 3+16) != 3+16) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Failed to write data to audiosocket");</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ return ret;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const int audiosocket_send_frame(const int svc, const struct ast_frame *f) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ int ret = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t kind = 0x10; // always 16-bit, 8kHz signed linear mono, for now</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf, *p;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ buf = ast_malloc(3 + f->datalen);</span><br><span style="color: hsl(120, 100%, 40%);">+ p = buf;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ *(p++) = kind;</span><br><span style="color: hsl(120, 100%, 40%);">+ *(p++) = f->datalen >> 8;</span><br><span style="color: hsl(120, 100%, 40%);">+ *(p++) = f->datalen & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(p, f->data.ptr, f->datalen);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if(write(svc, buf, 3+f->datalen) != 3+f->datalen) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Failed to write data to audiosocket");</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(buf);</span><br><span style="color: hsl(120, 100%, 40%);">+ return ret;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_frame *audiosocket_receive_frame(const int svc) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ int i = 0, n = 0, ret = 0;;</span><br><span style="color: hsl(120, 100%, 40%);">+ int not_audio = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ static struct ast_frame f;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t kind;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t len_high;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t len_low;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint16_t len = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t* data;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ n = read(svc, &kind, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (n < 0 && errno == EAGAIN) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return &ast_null_frame;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (n == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return &ast_null_frame;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (n != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Failed to read type header from audiosocket\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (kind == 0x00) {</span><br><span style="color: hsl(120, 100%, 40%);">+ // AudioSocket ended by remote</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (kind != 0x10) {</span><br><span style="color: hsl(120, 100%, 40%);">+ // read but ignore non-audio message</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Received non-audio audiosocket message\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ not_audio = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ n = read(svc, &len_high, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (n != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Failed to read data length from audiosocket\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ len += len_high * 256;</span><br><span style="color: hsl(120, 100%, 40%);">+ n = read(svc, &len_low, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (n != 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Failed to read data length from audiosocket\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ len += len_low;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (len < 1) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return &ast_null_frame;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ data = ast_malloc(len);</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ n = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ i = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ while (i < len) {</span><br><span style="color: hsl(120, 100%, 40%);">+ n = read(svc, data+i, len-i);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (n < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to read data from audiosocket\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = n;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (n == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Insufficient data read from audiosocket\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ ret = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ i += n;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ret != 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if(not_audio) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return &ast_null_frame;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ f.frametype = AST_FRAME_VOICE;</span><br><span style="color: hsl(120, 100%, 40%);">+ f.subclass.format = ast_format_slin;</span><br><span style="color: hsl(120, 100%, 40%);">+ f.src = "AudioSocket";</span><br><span style="color: hsl(120, 100%, 40%);">+ f.data.ptr = data;</span><br><span style="color: hsl(120, 100%, 40%);">+ f.datalen = len;</span><br><span style="color: hsl(120, 100%, 40%);">+ f.samples = len/2;</span><br><span style="color: hsl(120, 100%, 40%);">+ f.mallocd = AST_MALLOCD_DATA;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return &f;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int load_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(1, "Loading AudioSocket Support module\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_MODULE_LOAD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int unload_module(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verb(1, "Unloading AudioSocket Support module\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_MODULE_LOAD_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "AudioSocket support",</span><br><span style="color: hsl(120, 100%, 40%);">+ .support_level = AST_MODULE_SUPPORT_CORE,</span><br><span style="color: hsl(120, 100%, 40%);">+ .load = load_module,</span><br><span style="color: hsl(120, 100%, 40%);">+ .unload = unload_module,</span><br><span style="color: hsl(120, 100%, 40%);">+ .load_pri = AST_MODPRI_CHANNEL_DEPEND,</span><br><span style="color: hsl(120, 100%, 40%);">+);</span><br><span>diff --git a/res/res_audiosocket.exports.in b/res/res_audiosocket.exports.in</span><br><span>new file mode 100644</span><br><span>index 0000000..dfe0e4a</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_audiosocket.exports.in</span><br><span>@@ -0,0 +1,9 @@</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ global:</span><br><span style="color: hsl(120, 100%, 40%);">+ LINKER_SYMBOL_PREFIXaudiosocket_connect;</span><br><span style="color: hsl(120, 100%, 40%);">+ LINKER_SYMBOL_PREFIXaudiosocket_init;</span><br><span style="color: hsl(120, 100%, 40%);">+ LINKER_SYMBOL_PREFIXaudiosocket_send_frame;</span><br><span style="color: hsl(120, 100%, 40%);">+ LINKER_SYMBOL_PREFIX*audiosocket_receive_frame;</span><br><span style="color: hsl(120, 100%, 40%);">+ local:</span><br><span style="color: hsl(120, 100%, 40%);">+ *;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/11579">change 11579</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/11579"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Ie866e6c4fa13178ec76f2a6971ad3590a3a588b5 </div>
<div style="display:none"> Gerrit-Change-Number: 11579 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Seán C. McCord <ulexus@gmail.com> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>