Asterisk - The Open Source Telephony Project GIT-master-c753fe4
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
Macros | Functions | Variables
res_audiosocket.c File Reference

AudioSocket support for Asterisk. More...

#include "asterisk.h"
#include "errno.h"
#include <uuid/uuid.h>
#include <arpa/inet.h>
#include "asterisk/file.h"
#include "asterisk/res_audiosocket.h"
#include "asterisk/channel.h"
#include "asterisk/module.h"
#include "asterisk/uuid.h"
#include "asterisk/format_cache.h"
Include dependency graph for res_audiosocket.c:

Go to the source code of this file.

Macros

#define MAX_CONNECT_TIMEOUT_MSEC   2000
 
#define MODULE_DESCRIPTION   "AudioSocket support functions for Asterisk"
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
const int ast_audiosocket_connect (const char *server, struct ast_channel *chan)
 Send the initial message to an AudioSocket server. More...
 
const int ast_audiosocket_init (const int svc, const char *id)
 Send the initial message to an AudioSocket server. More...
 
struct ast_frameast_audiosocket_receive_frame (const int svc)
 Receive an Asterisk frame from an AudioSocket server. More...
 
struct ast_frameast_audiosocket_receive_frame_with_hangup (const int svc, int *const hangup)
 Receive an Asterisk frame from an AudioSocket server. More...
 
const int ast_audiosocket_send_frame (const int svc, const struct ast_frame *f)
 Send an Asterisk audio frame to an AudioSocket server. More...
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static int handle_audiosocket_connection (const char *server, const struct ast_sockaddr addr, const int netsockfd)
 
static int load_module (void)
 
static int unload_module (void)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "AudioSocket support" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_CHANNEL_DEPEND, }
 
static const struct ast_module_infoast_module_info = &__mod_info
 

Detailed Description

AudioSocket support for Asterisk.

Author
Seán C McCord scm@c.nosp@m.ycor.nosp@m.esys..nosp@m.com

Definition in file res_audiosocket.c.

Macro Definition Documentation

◆ MAX_CONNECT_TIMEOUT_MSEC

#define MAX_CONNECT_TIMEOUT_MSEC   2000

Definition at line 45 of file res_audiosocket.c.

◆ MODULE_DESCRIPTION

#define MODULE_DESCRIPTION   "AudioSocket support functions for Asterisk"

Definition at line 43 of file res_audiosocket.c.

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 372 of file res_audiosocket.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 372 of file res_audiosocket.c.

◆ ast_audiosocket_connect()

const int ast_audiosocket_connect ( const char *  server,
struct ast_channel chan 
)

Send the initial message to an AudioSocket server.

Parameters
serverThe server address, including port.
chanAn optional channel which will be put into autoservice during the connection period. If there is no channel to be autoserviced, pass NULL instead.
Return values
socketfile descriptor for AudioSocket on success
-1on error

Definition at line 100 of file res_audiosocket.c.

101{
102 int s = -1;
103 struct ast_sockaddr *addrs = NULL;
104 int num_addrs = 0, i = 0;
105
106 if (chan && ast_autoservice_start(chan) < 0) {
107 ast_log(LOG_WARNING, "Failed to start autoservice for channel "
108 "'%s'\n", ast_channel_name(chan));
109 goto end;
110 }
111
112 if (ast_strlen_zero(server)) {
113 ast_log(LOG_ERROR, "No AudioSocket server provided\n");
114 goto end;
115 }
116
117 if (!(num_addrs = ast_sockaddr_resolve(&addrs, server, PARSE_PORT_REQUIRE,
118 AST_AF_UNSPEC))) {
119 ast_log(LOG_ERROR, "Failed to resolve AudioSocket service using '%s' - "
120 "requires a valid hostname and port\n", server);
121 goto end;
122 }
123
124 /* Connect to AudioSocket service */
125 for (i = 0; i < num_addrs; i++) {
126
127 if (!ast_sockaddr_port(&addrs[i])) {
128 /* If there's no port, other addresses should have the
129 * same problem. Stop here.
130 */
131 ast_log(LOG_ERROR, "No port provided for '%s'\n",
132 ast_sockaddr_stringify(&addrs[i]));
133 s = -1;
134 goto end;
135 }
136
137 if ((s = ast_socket_nonblock(addrs[i].ss.ss_family, SOCK_STREAM,
138 IPPROTO_TCP)) < 0) {
139 ast_log(LOG_WARNING, "Unable to create socket: '%s'\n", strerror(errno));
140 continue;
141 }
142
143 /*
144 * Disable Nagle's algorithm by setting the TCP_NODELAY socket option.
145 * This reduces latency by preventing delays caused by packet buffering.
146 */
147 if (setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &(int){1}, sizeof(int)) < 0) {
148 ast_log(LOG_ERROR, "Failed to set TCP_NODELAY on AudioSocket: %s\n", strerror(errno));
149 }
150
151 if (ast_connect(s, &addrs[i]) && errno == EINPROGRESS) {
152
153 if (handle_audiosocket_connection(server, addrs[i], s)) {
154 close(s);
155 continue;
156 }
157
158 } else {
159 ast_log(LOG_ERROR, "Connection to '%s' failed with unexpected error: %s\n",
160 ast_sockaddr_stringify(&addrs[i]), strerror(errno));
161 close(s);
162 s = -1;
163 }
164
165 break;
166 }
167
168end:
169 if (addrs) {
170 ast_free(addrs);
171 }
172
173 if (chan && ast_autoservice_stop(chan) < 0) {
174 ast_log(LOG_WARNING, "Failed to stop autoservice for channel '%s'\n",
175 ast_channel_name(chan));
176 close(s);
177 return -1;
178 }
179
180 if (i == num_addrs) {
181 ast_log(LOG_ERROR, "Failed to connect to AudioSocket service\n");
182 close(s);
183 return -1;
184 }
185
186 return s;
187}
#define ast_free(a)
Definition: astmm.h:180
#define ast_log
Definition: astobj2.c:42
const char * ast_channel_name(const struct ast_channel *chan)
int ast_autoservice_stop(struct ast_channel *chan)
Stop servicing a channel for us...
Definition: autoservice.c:266
int ast_autoservice_start(struct ast_channel *chan)
Automatically service a channel for us...
Definition: autoservice.c:200
char * end
Definition: eagi_proxy.c:73
@ PARSE_PORT_REQUIRE
#define LOG_ERROR
#define LOG_WARNING
int errno
int ast_connect(int sockfd, const struct ast_sockaddr *addr)
Wrapper around connect(2) that uses struct ast_sockaddr.
Definition: netsock2.c:595
static char * ast_sockaddr_stringify(const struct ast_sockaddr *addr)
Wrapper around ast_sockaddr_stringify_fmt() with default format.
Definition: netsock2.h:256
#define ast_sockaddr_port(addr)
Get the port number of a socket address.
Definition: netsock2.h:517
int ast_sockaddr_resolve(struct ast_sockaddr **addrs, const char *str, int flags, int family)
Parses a string with an IPv4 or IPv6 address and place results into an array.
Definition: netsock2.c:280
@ AST_AF_UNSPEC
Definition: netsock2.h:54
static int handle_audiosocket_connection(const char *server, const struct ast_sockaddr addr, const int netsockfd)
#define NULL
Definition: resample.c:96
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
Socket address structure.
Definition: netsock2.h:97
struct sockaddr_storage ss
Definition: netsock2.h:98
#define ast_socket_nonblock(domain, type, protocol)
Create a non-blocking socket.
Definition: utils.h:1073

References AST_AF_UNSPEC, ast_autoservice_start(), ast_autoservice_stop(), ast_channel_name(), ast_connect(), ast_free, ast_log, ast_sockaddr_port, ast_sockaddr_resolve(), ast_sockaddr_stringify(), ast_socket_nonblock, ast_strlen_zero(), end, errno, handle_audiosocket_connection(), LOG_ERROR, LOG_WARNING, NULL, PARSE_PORT_REQUIRE, and ast_sockaddr::ss.

Referenced by audiosocket_exec(), and audiosocket_request().

◆ ast_audiosocket_init()

const int ast_audiosocket_init ( const int  svc,
const char *  id 
)

Send the initial message to an AudioSocket server.

Parameters
svcThe file descriptor of the network socket to the AudioSocket server.
idThe UUID to send to the AudioSocket server to uniquely identify this connection.
Return values
0on success
-1on error

Definition at line 189 of file res_audiosocket.c.

190{
191 uuid_t uu;
192 int ret = 0;
193 uint8_t buf[3 + 16];
194
195 if (ast_strlen_zero(id)) {
196 ast_log(LOG_ERROR, "No UUID for AudioSocket\n");
197 return -1;
198 }
199
200 if (uuid_parse(id, uu)) {
201 ast_log(LOG_ERROR, "Failed to parse UUID '%s'\n", id);
202 return -1;
203 }
204
206 buf[1] = 0x00;
207 buf[2] = 0x10;
208 memcpy(buf + 3, uu, 16);
209
210 if (write(svc, buf, 3 + 16) != 3 + 16) {
211 ast_log(LOG_WARNING, "Failed to write data to AudioSocket because: %s\n", strerror(errno));
212 ret = -1;
213 }
214
215 return ret;
216}
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
@ AST_AUDIOSOCKET_KIND_UUID
Message contains the connection's UUID, direction: Received only.

References AST_AUDIOSOCKET_KIND_UUID, ast_log, ast_strlen_zero(), buf, errno, LOG_ERROR, and LOG_WARNING.

Referenced by audiosocket_call(), and audiosocket_run().

◆ ast_audiosocket_receive_frame()

struct ast_frame * ast_audiosocket_receive_frame ( const int  svc)

Receive an Asterisk frame from an AudioSocket server.

This returned object is a pointer to an Asterisk frame which must be manually freed by the caller.

Parameters
svcThe file descriptor of the network socket to the AudioSocket server.
Return values
Aast_frame on success
NULLon error

Definition at line 256 of file res_audiosocket.c.

257{
259}
struct ast_frame * ast_audiosocket_receive_frame_with_hangup(const int svc, int *const hangup)
Receive an Asterisk frame from an AudioSocket server.

References ast_audiosocket_receive_frame_with_hangup(), and NULL.

◆ ast_audiosocket_receive_frame_with_hangup()

struct ast_frame * ast_audiosocket_receive_frame_with_hangup ( const int  svc,
int *const  hangup 
)

Receive an Asterisk frame from an AudioSocket server.

This returned object is a pointer to an Asterisk frame which must be manually freed by the caller.

Parameters
svcThe file descriptor of the network socket to the AudioSocket server.
hangupWill be true if the AudioSocket server requested the channel be hung up, otherwise false. Used as an out-parameter only, pass NULL if not needed. The function return value will always be NULL when true.
Return values
Aast_frame on success
NULLon error or when the hungup parameter is true.

Definition at line 261 of file res_audiosocket.c.

263{
264 int i = 0, n = 0, ret = 0;
265 struct ast_frame f = {
267 .subclass.format = ast_format_slin,
268 .src = "AudioSocket",
269 .mallocd = AST_MALLOCD_DATA,
270 };
271 uint8_t header[3];
272 uint8_t *kind = &header[0];
273 uint16_t *length = (uint16_t *) &header[1];
274 uint8_t *data;
275
276 if (hangup) {
277 *hangup = 0;
278 }
279
280 n = read(svc, &header, 3);
281 if (n == -1) {
282 ast_log(LOG_WARNING, "Failed to read header from AudioSocket because: %s\n", strerror(errno));
283 return NULL;
284 }
285
286 if (n == 0 || *kind == AST_AUDIOSOCKET_KIND_HANGUP) {
287 /* Socket closure or requested hangup. */
288 if (hangup) {
289 *hangup = 1;
290 }
291 return NULL;
292 }
293
294 if (*kind != AST_AUDIOSOCKET_KIND_AUDIO) {
295 ast_log(LOG_ERROR, "Received AudioSocket message other than hangup or audio, refer to protocol specification for valid message types\n");
296 return NULL;
297 }
298
299 /* Swap endianess of length if needed. */
300 *length = ntohs(*length);
301 if (*length < 1) {
302 ast_log(LOG_ERROR, "Invalid message length received from AudioSocket server. \n");
303 return NULL;
304 }
305
306 data = ast_malloc(*length);
307 if (!data) {
308 ast_log(LOG_ERROR, "Failed to allocate for data from AudioSocket\n");
309 return NULL;
310 }
311
312 ret = 0;
313 n = 0;
314 i = 0;
315 while (i < *length) {
316 n = read(svc, data + i, *length - i);
317 if (n == -1) {
318 if (errno == EAGAIN || errno == EWOULDBLOCK) {
319 int poll_result = ast_wait_for_input(svc, 5);
320
321 if (poll_result == 1) {
322 continue;
323 } else if (poll_result == 0) {
324 ast_log(LOG_WARNING, "Poll timed out while waiting for data\n");
325 } else {
326 ast_log(LOG_WARNING, "Poll error: %s\n", strerror(errno));
327 }
328 }
329
330 ast_log(LOG_ERROR, "Failed to read payload from AudioSocket: %s\n", strerror(errno));
331 ret = -1;
332 break;
333 }
334 if (n == 0) {
335 ast_log(LOG_ERROR, "Insufficient payload read from AudioSocket\n");
336 ret = -1;
337 break;
338 }
339 i += n;
340 }
341
342 if (ret != 0) {
343 ast_free(data);
344 return NULL;
345 }
346
347 f.data.ptr = data;
348 f.datalen = *length;
349 f.samples = *length / 2;
350
351 /* The frame steals data, so it doesn't need to be freed here */
352 return ast_frisolate(&f);
353}
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
static int hangup(void *data)
Definition: chan_pjsip.c:2526
struct ast_format * ast_format_slin
Built-in cached signed linear 8kHz format.
Definition: format_cache.c:41
#define ast_frisolate(fr)
Makes a frame independent of any static storage.
#define AST_MALLOCD_DATA
@ AST_FRAME_VOICE
@ AST_AUDIOSOCKET_KIND_AUDIO
Messages contains audio data, direction: Sent and received.
@ AST_AUDIOSOCKET_KIND_HANGUP
Message indicates the channel should be hung up, direction: Sent only.
Data structure associated with a single frame of data.
union ast_frame::@228 data
enum ast_frame_type frametype
int ast_wait_for_input(int fd, int ms)
Definition: utils.c:1698

References AST_AUDIOSOCKET_KIND_AUDIO, AST_AUDIOSOCKET_KIND_HANGUP, ast_format_slin, AST_FRAME_VOICE, ast_free, ast_frisolate, ast_log, ast_malloc, AST_MALLOCD_DATA, ast_wait_for_input(), ast_frame::data, ast_frame::datalen, errno, ast_frame::frametype, hangup(), LOG_ERROR, LOG_WARNING, NULL, ast_frame::ptr, and ast_frame::samples.

Referenced by ast_audiosocket_receive_frame(), audiosocket_read(), and audiosocket_run().

◆ ast_audiosocket_send_frame()

const int ast_audiosocket_send_frame ( const int  svc,
const struct ast_frame f 
)

Send an Asterisk audio frame to an AudioSocket server.

Parameters
svcThe file descriptor of the network socket to the AudioSocket server.
fThe Asterisk audio frame to send.
Return values
0on success
-1on error

Definition at line 218 of file res_audiosocket.c.

219{
220 int datalen = f->datalen;
221 if (f->frametype == AST_FRAME_DTMF) {
222 datalen = 1;
223 }
224
225 {
226 uint8_t buf[3 + datalen];
227 uint16_t *length = (uint16_t *) &buf[1];
228
229 /* Audio format is 16-bit, 8kHz signed linear mono for dialplan app,
230 depends on agreed upon audio codec for channel driver interface. */
231 switch (f->frametype) {
232 case AST_FRAME_VOICE:
234 *length = htons(datalen);
235 memcpy(&buf[3], f->data.ptr, datalen);
236 break;
237 case AST_FRAME_DTMF:
239 buf[3] = (uint8_t) f->subclass.integer;
240 *length = htons(1);
241 break;
242 default:
243 ast_log(LOG_ERROR, "Unsupported frame type %d for AudioSocket\n", f->frametype);
244 return -1;
245 }
246
247 if (write(svc, buf, 3 + datalen) != 3 + datalen) {
248 ast_log(LOG_WARNING, "Failed to write data to AudioSocket because: %s\n", strerror(errno));
249 return -1;
250 }
251 }
252
253 return 0;
254}
#define AST_FRAME_DTMF
@ AST_AUDIOSOCKET_KIND_DTMF
Message contains a DTMF digit, direction: Received only.
struct ast_frame_subclass subclass

References AST_AUDIOSOCKET_KIND_AUDIO, AST_AUDIOSOCKET_KIND_DTMF, AST_FRAME_DTMF, AST_FRAME_VOICE, ast_log, buf, ast_frame::data, ast_frame::datalen, errno, ast_frame::frametype, ast_frame_subclass::integer, LOG_ERROR, LOG_WARNING, ast_frame::ptr, and ast_frame::subclass.

Referenced by audiosocket_run(), audiosocket_send_dtmf(), and audiosocket_write().

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 372 of file res_audiosocket.c.

◆ handle_audiosocket_connection()

static int handle_audiosocket_connection ( const char *  server,
const struct ast_sockaddr  addr,
const int  netsockfd 
)
static

Definition at line 58 of file res_audiosocket.c.

60{
61 struct pollfd pfds[1];
62 int res, conresult;
63 socklen_t reslen;
64
65 reslen = sizeof(conresult);
66
67 pfds[0].fd = netsockfd;
68 pfds[0].events = POLLOUT;
69
70 while ((res = ast_poll(pfds, 1, MAX_CONNECT_TIMEOUT_MSEC)) != 1) {
71 if (errno != EINTR) {
72 if (!res) {
73 ast_log(LOG_WARNING, "AudioSocket connection to '%s' timed"
74 "out after MAX_CONNECT_TIMEOUT_MSEC (%d) milliseconds.\n",
76 } else {
77 ast_log(LOG_WARNING, "Connect to '%s' failed: %s\n", server,
78 strerror(errno));
79 }
80
81 return -1;
82 }
83 }
84
85 if (getsockopt(pfds[0].fd, SOL_SOCKET, SO_ERROR, &conresult, &reslen) < 0) {
86 ast_log(LOG_WARNING, "Connection to '%s' failed with error: %s\n",
87 ast_sockaddr_stringify(&addr), strerror(errno));
88 return -1;
89 }
90
91 if (conresult) {
92 ast_log(LOG_WARNING, "Connecting to '%s' failed for url '%s': %s\n",
93 ast_sockaddr_stringify(&addr), server, strerror(conresult));
94 return -1;
95 }
96
97 return 0;
98}
#define ast_poll(a, b, c)
Definition: poll-compat.h:88
#define MAX_CONNECT_TIMEOUT_MSEC

References ast_log, ast_poll, ast_sockaddr_stringify(), errno, LOG_WARNING, and MAX_CONNECT_TIMEOUT_MSEC.

Referenced by ast_audiosocket_connect().

◆ load_module()

static int load_module ( void  )
static

Definition at line 355 of file res_audiosocket.c.

356{
357 ast_verb(5, "Loading AudioSocket Support module\n");
359}
#define ast_verb(level,...)
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70

References AST_MODULE_LOAD_SUCCESS, and ast_verb.

◆ unload_module()

static int unload_module ( void  )
static

Definition at line 361 of file res_audiosocket.c.

362{
363 ast_verb(5, "Unloading AudioSocket Support module\n");
365}

References AST_MODULE_LOAD_SUCCESS, and ast_verb.

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER , .description = "AudioSocket support" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_EXTENDED, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_CHANNEL_DEPEND, }
static

Definition at line 372 of file res_audiosocket.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 372 of file res_audiosocket.c.