<p>Friendly Automation <strong>submitted</strong> this change.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/11579">View Change</a></p><div style="white-space:pre-wrap">Approvals:
Kevin Harwell: Looks good to me, but someone else must approve
Joshua Colp: Looks good to me, approved
Friendly Automation: Approved for Submit
</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">feat: AudioSocket channel, application, and ARI support.<br><br>This commit adds support for<br>[AudioSocket](<br>https://wiki.asterisk.org/wiki/display/AST/AudioSocket),<br>a very simple bidirectional audio streaming protocol. There are both<br>channel 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>ARI support has also been added via the existing "externalMedia" ARI<br>functionality. The UUID is specified using the arbitrary "data" field.<br><br>ASTERISK-28484 #close<br><br>Change-Id: Ie866e6c4fa13178ec76f2a6971ad3590a3a588b5<br>---<br>A apps/app_audiosocket.c<br>A channels/chan_audiosocket.c<br>A doc/CHANGES-staging/feat_audiosocket.txt<br>A include/asterisk/res_audiosocket.h<br>M res/ari/resource_channels.c<br>M res/ari/resource_channels.h<br>M res/res_ari_channels.c<br>A res/res_audiosocket.c<br>A res/res_audiosocket.exports.in<br>M rest-api/api-docs/channels.json<br>10 files changed, 1,060 insertions(+), 2 deletions(-)<br><br></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..45d3396</span><br><span>--- /dev/null</span><br><span>+++ b/apps/app_audiosocket.c</span><br><span>@@ -0,0 +1,240 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</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%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</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>extended</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/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 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%);">+</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%);">+ <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://wiki.asterisk.org/wiki/display/AST/AudioSocket</para></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>This application does not automatically answer and should generally be 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_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%);">+ struct ast_format *readFormat, *writeFormat;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *chanName;</span><br><span style="color: hsl(120, 100%, 40%);">+ int res;</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%);">+ uuid_t uu;</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%);">+ chanName = ast_channel_name(chan);</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 (uuid_parse(args.idStr, uu)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to parse UUID '%s'\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 = ast_audiosocket_connect(args.server, chan)) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* The res module will already output a log message, so another is not needed */</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%);">+ writeFormat = ao2_bump(ast_channel_writeformat(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ readFormat = ao2_bump(ast_channel_readformat(chan));</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 for channel %s\n", chanName);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(writeFormat, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(readFormat, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ close(s);</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 for channel %s\n", chanName);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Attempt to restore previous write format even though it is likely to</span><br><span style="color: hsl(120, 100%, 40%);">+ * fail, since setting the read format did.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_set_write_format(chan, writeFormat)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to restore write format for channel %s\n", chanName);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(writeFormat, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(readFormat, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ close(s);</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%);">+ res = audiosocket_run(chan, args.idStr, s);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* On non-zero return, report failure */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (res) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Restore previous formats and close the connection */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_set_write_format(chan, writeFormat)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to restore write format for channel %s\n", chanName);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_set_read_format(chan, readFormat)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to restore read format for channel %s\n", chanName);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(writeFormat, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(readFormat, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ close(s);</span><br><span style="color: hsl(120, 100%, 40%);">+ return res;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</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%);">+ if (ast_set_write_format(chan, writeFormat)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to restore write format for channel %s\n", chanName);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_set_read_format(chan, readFormat)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to restore read format for channel %s\n", chanName);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(writeFormat, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(readFormat, -1);</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, int svc)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *chanName;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *targetChan;</span><br><span style="color: hsl(120, 100%, 40%);">+ int ms = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int outfd = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_frame *f;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!chan || ast_channel_state(chan) != AST_STATE_UP) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Channel is %s\n", chan ? "not answered" : "missing");</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 (ast_audiosocket_init(svc, id)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to intialize AudioSocket\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%);">+ chanName = ast_channel_name(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ while (1) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ targetChan = ast_waitfor_nandfds(&chan, 1, &svc, 1, NULL, &outfd, &ms);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (targetChan) {</span><br><span style="color: hsl(120, 100%, 40%);">+ f = ast_read(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!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%);">+ if (f->frametype == AST_FRAME_VOICE) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Send audio frame to audiosocket */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_audiosocket_send_frame(svc, f)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to forward channel frame from %s to AudioSocket\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ chanName);</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%);">+ ast_frfree(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%);">+ if (outfd >= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ f = ast_audiosocket_receive_frame(svc);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!f) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to receive frame from AudioSocket message for"</span><br><span style="color: hsl(120, 100%, 40%);">+ "channel %s\n", chanName);</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 %s\n", chanName);</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%);">+ ast_frfree(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%);">+ 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(</span><br><span style="color: hsl(120, 100%, 40%);">+ ASTERISK_GPL_KEY,</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_MODFLAG_LOAD_ORDER,</span><br><span style="color: hsl(120, 100%, 40%);">+ "AudioSocket Application",</span><br><span style="color: hsl(120, 100%, 40%);">+ .support_level = AST_MODULE_SUPPORT_EXTENDED,</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..823a978</span><br><span>--- /dev/null</span><br><span>+++ b/channels/chan_audiosocket.c</span><br><span>@@ -0,0 +1,302 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</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%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</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>extended</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/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%);">+#define FD_OUTPUT 1 /* A fd of -1 means an error, 0 is stdin */</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; /* The file descriptor for the AudioSocket instance */</span><br><span style="color: hsl(120, 100%, 40%);">+ char id[38]; /* The UUID identifying this AudioSocket instance */</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,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids,</span><br><span style="color: hsl(120, 100%, 40%);">+ 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;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* The channel should always be present from the API */</span><br><span style="color: hsl(120, 100%, 40%);">+ instance = ast_channel_tech_pvt(ast);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (instance == NULL || instance->svc < FD_OUTPUT) {</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 ast_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;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* The channel should always be present from the API */</span><br><span style="color: hsl(120, 100%, 40%);">+ instance = ast_channel_tech_pvt(ast);</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 ast_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%);">+ return ast_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;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* The channel should always be present from the API */</span><br><span style="color: hsl(120, 100%, 40%);">+ instance = ast_channel_tech_pvt(ast);</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%);">+ ast_free(instance);</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%);">+enum {</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_AUDIOSOCKET_CODEC = (1 << 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%);">+enum {</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_ARG_AUDIOSOCKET_CODEC = (1 << 0),</span><br><span style="color: hsl(120, 100%, 40%);">+ OPT_ARG_ARRAY_SIZE</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_APP_OPTIONS(audiosocket_options, BEGIN_OPTIONS</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_OPTION_ARG('c', OPT_AUDIOSOCKET_CODEC, OPT_ARG_AUDIOSOCKET_CODEC),</span><br><span style="color: hsl(120, 100%, 40%);">+END_OPTIONS );</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,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids,</span><br><span style="color: hsl(120, 100%, 40%);">+ 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 = NULL;</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_format_cap *caps = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_format *fmt = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ uuid_t uu;</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%);">+ AST_APP_ARG(options);</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_flags opts = { 0, };</span><br><span style="color: hsl(120, 100%, 40%);">+ char *opt_args[OPT_ARG_ARRAY_SIZE];</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</span><br><span style="color: hsl(120, 100%, 40%);">+ (&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 (uuid_parse(args.idStr, uu)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to parse UUID '%s'\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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(args.options)</span><br><span style="color: hsl(120, 100%, 40%);">+ && ast_app_parse_options(audiosocket_options, &opts, opt_args,</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_strdupa(args.options))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "'AudioSocket' channel options '%s' parse error\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ args.options);</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_test_flag(&opts, OPT_AUDIOSOCKET_CODEC)</span><br><span style="color: hsl(120, 100%, 40%);">+ && !ast_strlen_zero(opt_args[OPT_ARG_AUDIOSOCKET_CODEC])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ fmt = ast_format_cache_get(opt_args[OPT_ARG_AUDIOSOCKET_CODEC]);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!fmt) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Codec '%s' not found for AudioSocket connection to '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ opt_args[OPT_ARG_AUDIOSOCKET_CODEC], 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%);">+ } else {</span><br><span style="color: hsl(120, 100%, 40%);">+ fmt = ast_format_cap_get_format(cap, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!fmt) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "No codec available for AudioSocket connection to '%s'\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ 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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!caps) {</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%);">+ instance = ast_calloc(1, sizeof(*instance));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!instance) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to allocate AudioSocket channel pvt\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%);">+ 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%);">+ if ((fd = ast_audiosocket_connect(args.destination, NULL)) < 0) {</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_format_cap_append(caps, fmt, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_nativeformats_set(chan, caps);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_set_writeformat(chan, fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_set_rawwriteformat(chan, fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_set_readformat(chan, fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_set_rawreadformat(chan, fmt);</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%);">+ ao2_ref(fmt, -1);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_ref(caps, -1);</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%);">+ ao2_cleanup(fmt);</span><br><span style="color: hsl(120, 100%, 40%);">+ ao2_cleanup(caps);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (instance != NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(instance);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (fd >= 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ close(fd);</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 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_by_type(audiosocket_channel_tech.capabilities, AST_MEDIA_TYPE_UNKNOWN);</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%);">+</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,</span><br><span style="color: hsl(120, 100%, 40%);">+ "AudioSocket Channel",</span><br><span style="color: hsl(120, 100%, 40%);">+ .support_level = AST_MODULE_SUPPORT_EXTENDED,</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/doc/CHANGES-staging/feat_audiosocket.txt b/doc/CHANGES-staging/feat_audiosocket.txt</span><br><span>new file mode 100644</span><br><span>index 0000000..cc7b352</span><br><span>--- /dev/null</span><br><span>+++ b/doc/CHANGES-staging/feat_audiosocket.txt</span><br><span>@@ -0,0 +1,14 @@</span><br><span style="color: hsl(120, 100%, 40%);">+Subject: Features</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Adds support for AudioSocket, a very simple bidirectional audio streaming</span><br><span style="color: hsl(120, 100%, 40%);">+protocol. There are both channel and application interfaces.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+A description of the protocol can be found on the referenced wiki page. A</span><br><span style="color: hsl(120, 100%, 40%);">+short talk about the reasons and implementation can be found on YouTube at</span><br><span style="color: hsl(120, 100%, 40%);">+the link provided.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ARI support has also been added via the existing "externalMedia" ARI</span><br><span style="color: hsl(120, 100%, 40%);">+functionality. The UUID is specified using the arbitrary "data" field.</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+Wiki: https://wiki.asterisk.org/wiki/display/AST/AudioSocket</span><br><span style="color: hsl(120, 100%, 40%);">+YouTube: https://www.youtube.com/watch?v=tjduXbZZEgI</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..0357bcd</span><br><span>--- /dev/null</span><br><span>+++ b/include/asterisk/res_audiosocket.h</span><br><span>@@ -0,0 +1,87 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</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%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</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%);">+ * \param chan An optional channel which will be put into autoservice during</span><br><span style="color: hsl(120, 100%, 40%);">+ * the connection period. If there is no channel to be autoserviced, pass NULL</span><br><span style="color: hsl(120, 100%, 40%);">+ * instead.</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 ast_audiosocket_connect(const char *server, struct ast_channel *chan);</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 ast_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 ast_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 a pointer to an Asterisk frame which must be</span><br><span style="color: hsl(120, 100%, 40%);">+ * manually freed by the caller.</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%);">+ *</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 *ast_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/ari/resource_channels.c b/res/ari/resource_channels.c</span><br><span>index 81a902c..549883d 100644</span><br><span>--- a/res/ari/resource_channels.c</span><br><span>+++ b/res/ari/resource_channels.c</span><br><span>@@ -2101,6 +2101,51 @@</span><br><span> ast_channel_unref(chan);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void external_media_audiosocket_tcp(struct ast_ari_channels_external_media_args *args,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_variable *variables,</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_ari_response *response)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ size_t endpoint_len;</span><br><span style="color: hsl(120, 100%, 40%);">+ char *endpoint;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct varshead *vars;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ endpoint_len = strlen("AudioSocket/") + strlen(args->external_host) + 1 + strlen(args->data) + 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ endpoint = ast_alloca(endpoint_len);</span><br><span style="color: hsl(120, 100%, 40%);">+ /* The UUID is stored in the arbitrary data field */</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(endpoint, endpoint_len, "AudioSocket/%s/%s", args->external_host, args->data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ chan = ari_channels_handle_originate_with_id(</span><br><span style="color: hsl(120, 100%, 40%);">+ endpoint,</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ args->app,</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ 0,</span><br><span style="color: hsl(120, 100%, 40%);">+ variables,</span><br><span style="color: hsl(120, 100%, 40%);">+ args->channel_id,</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+ args->format,</span><br><span style="color: hsl(120, 100%, 40%);">+ response);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_variables_destroy(variables);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!chan) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</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_lock(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ vars = ast_channel_varshead(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (vars && !AST_LIST_EMPTY(vars)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_json_object_set(response->message, "channelvars", ast_json_channel_vars(vars));</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%);">+ ast_channel_unref(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #include "asterisk/config.h"</span><br><span> #include "asterisk/netsock2.h"</span><br><span> </span><br><span>@@ -2161,6 +2206,8 @@</span><br><span> </span><br><span> if (strcasecmp(args->encapsulation, "rtp") == 0 && strcasecmp(args->transport, "udp") == 0) {</span><br><span> external_media_rtp_udp(args, variables, response);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else if (strcasecmp(args->encapsulation, "audiosocket") == 0 && strcasecmp(args->transport, "tcp") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ external_media_audiosocket_tcp(args, variables, response);</span><br><span> } else {</span><br><span> ast_ari_response_error(</span><br><span> response, 501, "Not Implemented",</span><br><span>diff --git a/res/ari/resource_channels.h b/res/ari/resource_channels.h</span><br><span>index 49a3882..d06e30b 100644</span><br><span>--- a/res/ari/resource_channels.h</span><br><span>+++ b/res/ari/resource_channels.h</span><br><span>@@ -844,6 +844,8 @@</span><br><span> const char *format;</span><br><span> /*! External media direction */</span><br><span> const char *direction;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! An arbitrary data field */</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *data;</span><br><span> };</span><br><span> /*!</span><br><span> * \brief Body parsing function for /channels/externalMedia.</span><br><span>diff --git a/res/res_ari_channels.c b/res/res_ari_channels.c</span><br><span>index f938e14..72e47f1 100644</span><br><span>--- a/res/res_ari_channels.c</span><br><span>+++ b/res/res_ari_channels.c</span><br><span>@@ -2852,6 +2852,10 @@</span><br><span> if (field) {</span><br><span> args->direction = ast_json_string_get(field);</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+ field = ast_json_object_get(body, "data");</span><br><span style="color: hsl(120, 100%, 40%);">+ if (field) {</span><br><span style="color: hsl(120, 100%, 40%);">+ args->data = ast_json_string_get(field);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span>@@ -2899,6 +2903,9 @@</span><br><span> if (strcmp(i->name, "direction") == 0) {</span><br><span> args.direction = (i->value);</span><br><span> } else</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcmp(i->name, "data") == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ args.data = (i->value);</span><br><span style="color: hsl(120, 100%, 40%);">+ } else</span><br><span> {}</span><br><span> }</span><br><span> args.variables = body;</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..6dc64e7</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_audiosocket.c</span><br><span>@@ -0,0 +1,345 @@</span><br><span style="color: hsl(120, 100%, 40%);">+/*</span><br><span style="color: hsl(120, 100%, 40%);">+ * Asterisk -- An open source telephony toolkit.</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%);">+ * See http://www.asterisk.org for more information about</span><br><span style="color: hsl(120, 100%, 40%);">+ * the Asterisk project. Please do not directly contact</span><br><span style="color: hsl(120, 100%, 40%);">+ * any of the maintainers of this project for assistance;</span><br><span style="color: hsl(120, 100%, 40%);">+ * the project provides a web site, mailing lists and IRC</span><br><span style="color: hsl(120, 100%, 40%);">+ * channels for your use.</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>extended</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%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \internal</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Attempt to complete the audiosocket connection.</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,</span><br><span style="color: hsl(120, 100%, 40%);">+ 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"</span><br><span style="color: hsl(120, 100%, 40%);">+ "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,</span><br><span style="color: hsl(120, 100%, 40%);">+ 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 ast_audiosocket_connect(const char *server, struct ast_channel *chan)</span><br><span style="color: hsl(120, 100%, 40%);">+{</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 (chan && ast_autoservice_start(chan) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Failed to start autoservice for channel "</span><br><span style="color: hsl(120, 100%, 40%);">+ "%s\n", ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ goto end;</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(server)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "No AudioSocket server provided\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ goto end;</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 (!(num_addrs = ast_sockaddr_resolve(&addrs, server, PARSE_PORT_REQUIRE,</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_AF_UNSPEC))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to resolve AudioSocket service using %s - "</span><br><span style="color: hsl(120, 100%, 40%);">+ "requires a valid hostname and port\n", server);</span><br><span style="color: hsl(120, 100%, 40%);">+ goto end;</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_sockaddr_port(&addrs[i])) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* If there's no port, other addresses should have the</span><br><span style="color: hsl(120, 100%, 40%);">+ * same problem. Stop here.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "No port provided for %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_sockaddr_stringify(&addrs[i]));</span><br><span style="color: hsl(120, 100%, 40%);">+ s = -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ goto end;</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 = ast_socket_nonblock(addrs[i].ss.ss_family, SOCK_STREAM,</span><br><span style="color: hsl(120, 100%, 40%);">+ 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_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%);">+ close(s);</span><br><span style="color: hsl(120, 100%, 40%);">+ s = -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%);">+ break;</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%);">+end:</span><br><span style="color: hsl(120, 100%, 40%);">+ if (addrs) {</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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (chan && ast_autoservice_stop(chan) < 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Failed to stop autoservice for channel %s\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ close(s);</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 (i == num_addrs) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to connect to AudioSocket service\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ close(s);</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%);">+const int ast_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%);">+ uuid_t uu;</span><br><span style="color: hsl(120, 100%, 40%);">+ int ret = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t buf[3 + 16];</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\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 (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%);">+ 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\n");</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%);">+ 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 ast_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 *p;</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t buf[3 + f->datalen];</span><br><span style="color: hsl(120, 100%, 40%);">+</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\n");</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%);">+ 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 *ast_audiosocket_receive_frame(const int 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%);">+ int i = 0, n = 0, ret = 0, not_audio = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_frame f = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .frametype = AST_FRAME_VOICE,</span><br><span style="color: hsl(120, 100%, 40%);">+ .subclass.format = ast_format_slin,</span><br><span style="color: hsl(120, 100%, 40%);">+ .src = "AudioSocket",</span><br><span style="color: hsl(120, 100%, 40%);">+ .mallocd = AST_MALLOCD_DATA,</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%);">+ if (!data) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Failed to allocate for data 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%);">+</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%);">+ ast_free(data);</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%);">+ ast_free(data);</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.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%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* The frame steals data, so it doesn't need to be freed here */</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_frisolate(&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,</span><br><span style="color: hsl(120, 100%, 40%);">+ "AudioSocket support",</span><br><span style="color: hsl(120, 100%, 40%);">+ .support_level = AST_MODULE_SUPPORT_EXTENDED,</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..c89c9a9</span><br><span>--- /dev/null</span><br><span>+++ b/res/res_audiosocket.exports.in</span><br><span>@@ -0,0 +1,4 @@</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_PREFIX*;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span>diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json</span><br><span>index 94afb27..a20e9f2 100644</span><br><span>--- a/rest-api/api-docs/channels.json</span><br><span>+++ b/rest-api/api-docs/channels.json</span><br><span>@@ -1810,7 +1810,8 @@</span><br><span> "allowableValues": {</span><br><span> "valueType": "LIST",</span><br><span> "values": [</span><br><span style="color: hsl(0, 100%, 40%);">- "rtp"</span><br><span style="color: hsl(120, 100%, 40%);">+ "rtp",</span><br><span style="color: hsl(120, 100%, 40%);">+ "audiosocket"</span><br><span> ]</span><br><span> }</span><br><span> },</span><br><span>@@ -1825,7 +1826,8 @@</span><br><span> "allowableValues": {</span><br><span> "valueType": "LIST",</span><br><span> "values": [</span><br><span style="color: hsl(0, 100%, 40%);">- "udp"</span><br><span style="color: hsl(120, 100%, 40%);">+ "udp",</span><br><span style="color: hsl(120, 100%, 40%);">+ "tcp"</span><br><span> ]</span><br><span> }</span><br><span> },</span><br><span>@@ -1866,6 +1868,14 @@</span><br><span> "both"</span><br><span> ]</span><br><span> }</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%);">+ "name": "data",</span><br><span style="color: hsl(120, 100%, 40%);">+ "description": "An arbitrary data field",</span><br><span style="color: hsl(120, 100%, 40%);">+ "paramType": "query",</span><br><span style="color: hsl(120, 100%, 40%);">+ "required": false,</span><br><span style="color: hsl(120, 100%, 40%);">+ "allowMultiple": false,</span><br><span style="color: hsl(120, 100%, 40%);">+ "dataType": "string"</span><br><span> }</span><br><span> ],</span><br><span> "errorResponses": [</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: 34 </div>
<div style="display:none"> Gerrit-Owner: Seán C. McCord <ulexus@gmail.com> </div>
<div style="display:none"> Gerrit-Reviewer: Benjamin Keith Ford <bford@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Friendly Automation </div>
<div style="display:none"> Gerrit-Reviewer: George Joseph <gjoseph@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Joshua Colp <jcolp@sangoma.com> </div>
<div style="display:none"> Gerrit-Reviewer: Kevin Harwell <kharwell@digium.com> </div>
<div style="display:none"> Gerrit-Reviewer: Seán C. McCord <ulexus@gmail.com> </div>
<div style="display:none"> Gerrit-MessageType: merged </div>