Asterisk - The Open Source Telephony Project GIT-master-27fb039
Loading...
Searching...
No Matches
Data Structures | Macros | Enumerations | Functions | Variables
chan_websocket.c File Reference

Websocket Media Channel. More...

#include "asterisk.h"
#include "asterisk/app.h"
#include "asterisk/causes.h"
#include "asterisk/channel.h"
#include "asterisk/codec.h"
#include "asterisk/http_websocket.h"
#include "asterisk/format_cache.h"
#include "asterisk/frame.h"
#include "asterisk/lock.h"
#include "asterisk/mod_format.h"
#include "asterisk/module.h"
#include "asterisk/pbx.h"
#include "asterisk/uuid.h"
#include "asterisk/timing.h"
#include "asterisk/translate.h"
#include "asterisk/websocket_client.h"
Include dependency graph for chan_websocket.c:

Go to the source code of this file.

Data Structures

struct  instance_proxy
 
struct  websocket_pvt
 

Macros

#define ANSWER_CHANNEL   "ANSWER"
 
#define CONTINUE_MEDIA   "CONTINUE_MEDIA"
 
#define DRIVER_STATUS   "STATUS"
 
#define DTMF_END   "DTMF_END"
 
#define FLUSH_MEDIA   "FLUSH_MEDIA"
 
#define GET_DRIVER_STATUS   "GET_STATUS"
 
#define HANGUP_CHANNEL   "HANGUP"
 
#define INCOMING_CONNECTION_ID   "INCOMING"
 
#define MAX_TEXT_MESSAGE_LEN   MIN(128, (AST_WEBSOCKET_MAX_RX_PAYLOAD_SIZE - 1))
 
#define MEDIA_BUFFERING_COMPLETED   "MEDIA_BUFFERING_COMPLETED"
 
#define MEDIA_START   "MEDIA_START"
 
#define MEDIA_WEBSOCKET_CONNECTION_ID   "MEDIA_WEBSOCKET_CONNECTION_ID"
 
#define MEDIA_WEBSOCKET_OPTIMAL_FRAME_SIZE   "MEDIA_WEBSOCKET_OPTIMAL_FRAME_SIZE"
 
#define MEDIA_XOFF   "MEDIA_XOFF"
 
#define MEDIA_XON   "MEDIA_XON"
 
#define PAUSE_MEDIA   "PAUSE_MEDIA"
 
#define QUEUE_DRAINED   "QUEUE_DRAINED"
 
#define QUEUE_LENGTH_MAX   1000
 
#define QUEUE_LENGTH_XOFF_LEVEL   900
 
#define QUEUE_LENGTH_XON_LEVEL   800
 
#define REPORT_QUEUE_DRAINED   "REPORT_QUEUE_DRAINED"
 
#define START_MEDIA_BUFFERING   "START_MEDIA_BUFFERING"
 
#define STOP_MEDIA_BUFFERING   "STOP_MEDIA_BUFFERING"
 

Enumerations

enum  { OPT_WS_CODEC = (1 << 0) , OPT_WS_NO_AUTO_ANSWER = (1 << 1) , OPT_WS_URI_PARAM = (1 << 2) , OPT_WS_PASSTHROUGH = (1 << 3) }
 
enum  {
  OPT_ARG_WS_CODEC , OPT_ARG_WS_NO_AUTO_ANSWER , OPT_ARG_WS_URI_PARAM , OPT_ARG_WS_PASSTHROUGH ,
  OPT_ARG_ARRAY_SIZE
}
 

Functions

static void __reg_module (void)
 
static void __unreg_module (void)
 
struct ast_moduleAST_MODULE_SELF_SYM (void)
 
static struct ast_framedequeue_frame (struct websocket_pvt *instance)
 
static void incoming_ws_established_cb (struct ast_websocket *ast_ws_session, struct ast_variable *get_params, struct ast_variable *upgrade_headers)
 
static int incoming_ws_http_callback (struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)
 
static void instance_proxy_cb (void *weakproxy, void *data)
 
static int load_module (void)
 Function called when our module is loaded.
 
static int process_binary_message (struct websocket_pvt *instance, char *payload, uint64_t payload_len)
 
static int process_text_message (struct websocket_pvt *instance, char *payload, uint64_t payload_len)
 
static int queue_frame_from_buffer (struct websocket_pvt *instance, char *buffer, size_t len)
 
static int queue_option_frame (struct websocket_pvt *instance, char *buffer)
 
static int read_from_ws_and_queue (struct websocket_pvt *instance)
 
static void * read_thread_handler (void *obj)
 
static void set_channel_format (struct websocket_pvt *instance, struct ast_format *fmt)
 
static int set_channel_timer (struct websocket_pvt *instance)
 
static int set_channel_variables (struct websocket_pvt *instance)
 
static int set_instance_silence_frame (struct websocket_pvt *instance)
 
static int set_instance_translator (struct websocket_pvt *instance)
 
static int unload_module (void)
 Function called when our module is unloaded.
 
static int validate_uri_parameters (const char *uri_params)
 
static int webchan_call (struct ast_channel *ast, const char *dest, int timeout)
 
static int webchan_hangup (struct ast_channel *ast)
 
static struct ast_framewebchan_read (struct ast_channel *ast)
 
static struct ast_channelwebchan_request (const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)
 
static int webchan_send_dtmf_text (struct ast_channel *ast, char digit, unsigned int duration)
 
static int webchan_write (struct ast_channel *ast, struct ast_frame *f)
 Function called when we should write a frame to the channel.
 
static void websocket_destructor (void *data)
 
static struct websocket_pvtwebsocket_new (const char *chan_name, const char *connection_id, struct ast_format *fmt)
 

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Websocket Media Channel" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_CHANNEL_DRIVER, .requires = "res_http_websocket,res_websocket_client", }
 
static const struct ast_module_infoast_module_info = &__mod_info
 
static struct ast_websocket_serverast_ws_server
 
static struct ast_http_uri http_uri
 
static struct ao2_containerinstances = NULL
 
static const struct ast_app_option websocket_options [128] = { [ 'c' ] = { .flag = OPT_WS_CODEC , .arg_index = OPT_ARG_WS_CODEC + 1 }, [ 'n' ] = { .flag = OPT_WS_NO_AUTO_ANSWER }, [ 'v' ] = { .flag = OPT_WS_URI_PARAM , .arg_index = OPT_ARG_WS_URI_PARAM + 1 }, [ 'p' ] = { .flag = OPT_WS_PASSTHROUGH }, }
 
static struct ast_channel_tech websocket_tech
 

Detailed Description

Websocket Media Channel.

Author
George Joseph gjose.nosp@m.ph@s.nosp@m.angom.nosp@m.a.co.nosp@m.m

Definition in file chan_websocket.c.

Macro Definition Documentation

◆ ANSWER_CHANNEL

#define ANSWER_CHANNEL   "ANSWER"

Definition at line 159 of file chan_websocket.c.

◆ CONTINUE_MEDIA

#define CONTINUE_MEDIA   "CONTINUE_MEDIA"

Definition at line 167 of file chan_websocket.c.

◆ DRIVER_STATUS

#define DRIVER_STATUS   "STATUS"

Definition at line 173 of file chan_websocket.c.

◆ DTMF_END

#define DTMF_END   "DTMF_END"

Definition at line 175 of file chan_websocket.c.

◆ FLUSH_MEDIA

#define FLUSH_MEDIA   "FLUSH_MEDIA"

Definition at line 163 of file chan_websocket.c.

◆ GET_DRIVER_STATUS

#define GET_DRIVER_STATUS   "GET_STATUS"

Definition at line 164 of file chan_websocket.c.

◆ HANGUP_CHANNEL

#define HANGUP_CHANNEL   "HANGUP"

Definition at line 160 of file chan_websocket.c.

◆ INCOMING_CONNECTION_ID

#define INCOMING_CONNECTION_ID   "INCOMING"

Definition at line 157 of file chan_websocket.c.

◆ MAX_TEXT_MESSAGE_LEN

#define MAX_TEXT_MESSAGE_LEN   MIN(128, (AST_WEBSOCKET_MAX_RX_PAYLOAD_SIZE - 1))

Definition at line 180 of file chan_websocket.c.

◆ MEDIA_BUFFERING_COMPLETED

#define MEDIA_BUFFERING_COMPLETED   "MEDIA_BUFFERING_COMPLETED"

Definition at line 174 of file chan_websocket.c.

◆ MEDIA_START

#define MEDIA_START   "MEDIA_START"

Definition at line 169 of file chan_websocket.c.

◆ MEDIA_WEBSOCKET_CONNECTION_ID

#define MEDIA_WEBSOCKET_CONNECTION_ID   "MEDIA_WEBSOCKET_CONNECTION_ID"

Definition at line 156 of file chan_websocket.c.

◆ MEDIA_WEBSOCKET_OPTIMAL_FRAME_SIZE

#define MEDIA_WEBSOCKET_OPTIMAL_FRAME_SIZE   "MEDIA_WEBSOCKET_OPTIMAL_FRAME_SIZE"

Definition at line 155 of file chan_websocket.c.

◆ MEDIA_XOFF

#define MEDIA_XOFF   "MEDIA_XOFF"

Definition at line 171 of file chan_websocket.c.

◆ MEDIA_XON

#define MEDIA_XON   "MEDIA_XON"

Definition at line 170 of file chan_websocket.c.

◆ PAUSE_MEDIA

#define PAUSE_MEDIA   "PAUSE_MEDIA"

Definition at line 166 of file chan_websocket.c.

◆ QUEUE_DRAINED

#define QUEUE_DRAINED   "QUEUE_DRAINED"

Definition at line 172 of file chan_websocket.c.

◆ QUEUE_LENGTH_MAX

#define QUEUE_LENGTH_MAX   1000

Definition at line 177 of file chan_websocket.c.

◆ QUEUE_LENGTH_XOFF_LEVEL

#define QUEUE_LENGTH_XOFF_LEVEL   900

Definition at line 178 of file chan_websocket.c.

◆ QUEUE_LENGTH_XON_LEVEL

#define QUEUE_LENGTH_XON_LEVEL   800

Definition at line 179 of file chan_websocket.c.

◆ REPORT_QUEUE_DRAINED

#define REPORT_QUEUE_DRAINED   "REPORT_QUEUE_DRAINED"

Definition at line 165 of file chan_websocket.c.

◆ START_MEDIA_BUFFERING

#define START_MEDIA_BUFFERING   "START_MEDIA_BUFFERING"

Definition at line 161 of file chan_websocket.c.

◆ STOP_MEDIA_BUFFERING

#define STOP_MEDIA_BUFFERING   "STOP_MEDIA_BUFFERING"

Definition at line 162 of file chan_websocket.c.

Enumeration Type Documentation

◆ anonymous enum

anonymous enum
Enumerator
OPT_WS_CODEC 
OPT_WS_NO_AUTO_ANSWER 
OPT_WS_URI_PARAM 
OPT_WS_PASSTHROUGH 

Definition at line 1275 of file chan_websocket.c.

1275 {
1276 OPT_WS_CODEC = (1 << 0),
1277 OPT_WS_NO_AUTO_ANSWER = (1 << 1),
1278 OPT_WS_URI_PARAM = (1 << 2),
1279 OPT_WS_PASSTHROUGH = (1 << 3),
1280};
@ OPT_WS_CODEC
@ OPT_WS_PASSTHROUGH
@ OPT_WS_URI_PARAM
@ OPT_WS_NO_AUTO_ANSWER

◆ anonymous enum

anonymous enum
Enumerator
OPT_ARG_WS_CODEC 
OPT_ARG_WS_NO_AUTO_ANSWER 
OPT_ARG_WS_URI_PARAM 
OPT_ARG_WS_PASSTHROUGH 
OPT_ARG_ARRAY_SIZE 

Definition at line 1282 of file chan_websocket.c.

1282 {
1288};
@ OPT_ARG_WS_PASSTHROUGH
@ OPT_ARG_WS_URI_PARAM
@ OPT_ARG_WS_CODEC
@ OPT_ARG_WS_NO_AUTO_ANSWER
@ OPT_ARG_ARRAY_SIZE

Function Documentation

◆ __reg_module()

static void __reg_module ( void  )
static

Definition at line 1763 of file chan_websocket.c.

◆ __unreg_module()

static void __unreg_module ( void  )
static

Definition at line 1763 of file chan_websocket.c.

◆ AST_MODULE_SELF_SYM()

struct ast_module * AST_MODULE_SELF_SYM ( void  )

Definition at line 1763 of file chan_websocket.c.

◆ dequeue_frame()

static struct ast_frame * dequeue_frame ( struct websocket_pvt instance)
static

Definition at line 218 of file chan_websocket.c.

219{
220 struct ast_frame *queued_frame = NULL;
221 SCOPED_LOCK(frame_queue_lock, &instance->frame_queue, AST_LIST_LOCK,
223
224 /*
225 * If the queue is paused, don't read a frame. Processing
226 * will continue down the function and a silence frame will
227 * be sent in its place.
228 */
229 if (instance->queue_paused) {
230 return NULL;
231 }
232
233 /*
234 * We need to check if we need to send an XON before anything
235 * else because there are multiple escape paths in this function
236 * and we don't want to accidentally keep the queue in a "full"
237 * state.
238 */
239 if (instance->queue_full && instance->frame_queue_length < QUEUE_LENGTH_XON_LEVEL) {
240 instance->queue_full = 0;
241 ast_debug(4, "%s: WebSocket sending MEDIA_XON\n",
242 ast_channel_name(instance->channel));
244 }
245
246 queued_frame = AST_LIST_REMOVE_HEAD(&instance->frame_queue, frame_list);
247
248 /*
249 * If there are no frames in the queue, we need to
250 * return NULL so we can send a silence frame. We also need
251 * to send the QUEUE_DRAINED notification if we were requested
252 * to do so.
253 */
254 if (!queued_frame) {
255 if (instance->report_queue_drained) {
256 instance->report_queue_drained = 0;
257 ast_debug(4, "%s: WebSocket sending QUEUE_DRAINED\n",
258 ast_channel_name(instance->channel));
260 }
261 return NULL;
262 }
263
264 /*
265 * The only way a control frame could be present here is as
266 * a result of us calling queue_option_frame() in response
267 * to an incoming TEXT command from the websocket.
268 * We'll be safe and make sure it's a AST_CONTROL_OPTION
269 * frame anyway.
270 *
271 * It's quite possible that there are multiple control frames
272 * in a row in the queue so we need to process consecutive ones
273 * immediately.
274 *
275 * In any case, processing a control frame MUST not use up
276 * a media timeslot so after all control frames have been
277 * processed, we need to read an audio frame and process it.
278 */
279 while (queued_frame && queued_frame->frametype == AST_FRAME_CONTROL) {
280 if (queued_frame->subclass.integer == AST_CONTROL_OPTION) {
281 /*
282 * We just need to send the data to the websocket.
283 * The data should already be NULL terminated.
284 */
286 queued_frame->data.ptr);
287 ast_debug(4, "%s: WebSocket sending %s\n",
288 ast_channel_name(instance->channel), (char *)queued_frame->data.ptr);
289 }
290 /*
291 * We do NOT send these to the core so we need to free
292 * the frame and grab the next one. If it's also a
293 * control frame, we need to process it otherwise
294 * continue down in the function.
295 */
296 ast_frame_free(queued_frame, 0);
297 queued_frame = AST_LIST_REMOVE_HEAD(&instance->frame_queue, frame_list);
298 /*
299 * Jut FYI... We didn't bump the queue length when we added the control
300 * frames so we don't need to decrement it here.
301 */
302 }
303
304 /*
305 * If, after reading all control frames, there are no frames
306 * left in the queue, we need to return NULL so we can send
307 * a silence frame.
308 */
309 if (!queued_frame) {
310 return NULL;
311 }
312
313 instance->frame_queue_length--;
314
315 return queued_frame;
316}
#define QUEUE_LENGTH_XON_LEVEL
#define QUEUE_DRAINED
#define MEDIA_XON
const char * ast_channel_name(const struct ast_channel *chan)
int AST_OPTIONAL_API_NAME() ast_websocket_write_string(struct ast_websocket *ws, const char *buf)
Construct and transmit a WebSocket frame containing string data.
void ast_frame_free(struct ast_frame *frame, int cache)
Frees a frame or list of frames.
Definition main/frame.c:176
@ AST_FRAME_CONTROL
@ AST_CONTROL_OPTION
#define ast_debug(level,...)
Log a DEBUG message.
#define AST_LIST_LOCK(head)
Locks a list.
Definition linkedlists.h:40
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
#define SCOPED_LOCK(varname, lock, lockfunc, unlockfunc)
Scoped Locks.
Definition lock.h:590
#define NULL
Definition resample.c:96
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
enum ast_frame_type frametype
union ast_frame::@239 data
struct websocket_pvt::@145 frame_queue
struct ast_channel * channel
struct ast_websocket * websocket

References ast_channel_name(), AST_CONTROL_OPTION, ast_debug, AST_FRAME_CONTROL, ast_frame_free(), AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_websocket_write_string(), websocket_pvt::channel, ast_frame::data, websocket_pvt::frame_queue, websocket_pvt::frame_queue_length, ast_frame::frametype, ast_frame_subclass::integer, MEDIA_XON, NULL, ast_frame::ptr, QUEUE_DRAINED, websocket_pvt::queue_full, QUEUE_LENGTH_XON_LEVEL, websocket_pvt::queue_paused, websocket_pvt::report_queue_drained, SCOPED_LOCK, ast_frame::subclass, and websocket_pvt::websocket.

Referenced by webchan_read().

◆ incoming_ws_established_cb()

static void incoming_ws_established_cb ( struct ast_websocket ast_ws_session,
struct ast_variable get_params,
struct ast_variable upgrade_headers 
)
static

Definition at line 1538 of file chan_websocket.c.

1540{
1541 RAII_VAR(struct ast_websocket *, s, ast_ws_session, ast_websocket_unref);
1542 struct ast_variable *v;
1543 const char *connection_id = NULL;
1544 struct websocket_pvt *instance = NULL;
1545 int nodelay = 1;
1546
1547 ast_debug(3, "WebSocket established\n");
1548
1549 for (v = upgrade_headers; v; v = v->next) {
1550 ast_debug(4, "Header-> %s: %s\n", v->name, v->value);
1551 }
1552 for (v = get_params; v; v = v->next) {
1553 ast_debug(4, " Param-> %s: %s\n", v->name, v->value);
1554 }
1555
1556 connection_id = ast_variable_find_in_list(get_params, "CONNECTION_ID");
1557 if (!connection_id) {
1558 /*
1559 * This can't really happen because websocket_http_callback won't
1560 * let it get this far if it can't add the connection_id to the
1561 * get_params.
1562 * Just in case though...
1563 */
1564 ast_log(LOG_WARNING, "WebSocket connection id not found\n");
1566 ast_websocket_close(ast_ws_session, 1000);
1567 return;
1568 }
1569
1571 if (!instance) {
1572 /*
1573 * This also can't really happen because websocket_http_callback won't
1574 * let it get this far if it can't find the instance.
1575 * Just in case though...
1576 */
1577 ast_log(LOG_WARNING, "%s: WebSocket instance not found\n", connection_id);
1579 ast_websocket_close(ast_ws_session, 1000);
1580 return;
1581 }
1582 instance->websocket = ao2_bump(ast_ws_session);
1583
1584 if (setsockopt(ast_websocket_fd(instance->websocket),
1585 IPPROTO_TCP, TCP_NODELAY, (char *) &nodelay, sizeof(nodelay)) < 0) {
1586 ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on manager connection: %s\n", strerror(errno));
1587 }
1588
1589 /* read_thread_handler cleans up the bump */
1590 read_thread_handler(ao2_bump(instance));
1591
1592 ao2_cleanup(instance);
1593 ast_debug(3, "WebSocket closed\n");
1594}
#define ast_log
Definition astobj2.c:42
#define ao2_cleanup(obj)
Definition astobj2.h:1934
#define ao2_weakproxy_find(c, arg, flags, tag)
Perform an ao2_find on a container with ao2_weakproxy objects, returning the real object.
Definition astobj2.h:1748
#define ao2_bump(obj)
Bump refcount on an AO2 object by one, returning the object.
Definition astobj2.h:480
@ OBJ_NOLOCK
Assume that the ao2_container is already locked.
Definition astobj2.h:1063
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition astobj2.h:1101
static void * read_thread_handler(void *obj)
static struct ao2_container * instances
int ast_queue_control(struct ast_channel *chan, enum ast_control_frame_type control)
Queue a control frame without payload.
Definition channel.c:1288
int AST_OPTIONAL_API_NAME() ast_websocket_fd(struct ast_websocket *session)
Get the file descriptor for a WebSocket session.
int AST_OPTIONAL_API_NAME() ast_websocket_close(struct ast_websocket *session, uint16_t reason)
Close a WebSocket session by sending a message with the CLOSE opcode and an optional code.
void AST_OPTIONAL_API_NAME() ast_websocket_unref(struct ast_websocket *session)
Decrease the reference count for a WebSocket session.
const char * ast_variable_find_in_list(const struct ast_variable *list, const char *variable)
Gets the value of a variable from a variable list by name.
@ AST_CONTROL_HANGUP
#define LOG_WARNING
int errno
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
Structure definition for session.
char connection_id[0]
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition utils.h:981

References ao2_bump, ao2_cleanup, ao2_weakproxy_find, AST_CONTROL_HANGUP, ast_debug, ast_log, ast_queue_control(), ast_variable_find_in_list(), ast_websocket_close(), ast_websocket_fd(), ast_websocket_unref(), websocket_pvt::channel, websocket_pvt::connection_id, errno, instances, LOG_WARNING, ast_variable::name, ast_variable::next, NULL, OBJ_NOLOCK, OBJ_SEARCH_KEY, RAII_VAR, read_thread_handler(), ast_variable::value, and websocket_pvt::websocket.

Referenced by load_module().

◆ incoming_ws_http_callback()

static int incoming_ws_http_callback ( struct ast_tcptls_session_instance ser,
const struct ast_http_uri urih,
const char *  uri,
enum ast_http_method  method,
struct ast_variable get_params,
struct ast_variable headers 
)
static

Definition at line 1606 of file chan_websocket.c.

1610{
1611 struct ast_http_uri fake_urih = {
1613 };
1614 int res = 0;
1615 /*
1616 * Normally the http server will destroy the get_params
1617 * when the session ends but if there weren't any initially
1618 * and we create some and add them to the list, the http server
1619 * won't know about it so we have to destroy it ourselves.
1620 */
1621 int destroy_get_params = (get_params == NULL);
1622 struct ast_variable *v = NULL;
1623 RAII_VAR(struct websocket_pvt *, instance, NULL, ao2_cleanup);
1624
1625 ast_debug(2, "URI: %s Starting\n", uri);
1626
1627 /*
1628 * The client will have issued the GET request with a URI of
1629 * /media/<connection_id>
1630 *
1631 * Since this callback is registered for the /media URI prefix the
1632 * http server will strip that off the front of the URI passing in
1633 * only the path components after that in the 'uri' parameter.
1634 * This should leave only the connection id without a leading '/'.
1635 */
1636 instance = ao2_weakproxy_find(instances, uri, OBJ_SEARCH_KEY | OBJ_NOLOCK, "");
1637 if (!instance) {
1638 ast_log(LOG_WARNING, "%s: WebSocket instance not found\n", uri);
1639 ast_http_error(ser, 404, "Not found", "WebSocket instance not found");
1640 return -1;
1641 }
1642
1643 /*
1644 * We don't allow additional connections using the same connection id.
1645 */
1646 if (instance->websocket) {
1647 ast_log(LOG_WARNING, "%s: Websocket already connected for channel '%s'\n",
1648 uri, instance->channel ? ast_channel_name(instance->channel) : "unknown");
1649 ast_http_error(ser, 409, "Conflict", "Another websocket connection exists for this connection id");
1650 return -1;
1651 }
1652
1653 v = ast_variable_new("CONNECTION_ID", uri, "");
1654 if (!v) {
1655 ast_http_error(ser, 500, "Server error", "");
1656 return -1;
1657 }
1658 ast_variable_list_append(&get_params, v);
1659
1660 for (v = get_params; v; v = v->next) {
1661 ast_debug(4, " Param-> %s: %s\n", v->name, v->value);
1662 }
1663
1664 /*
1665 * This will ultimately call internal_ws_established_cb() so
1666 * this function will block until the websocket is closed and
1667 * internal_ws_established_cb() returns;
1668 */
1669 res = ast_websocket_uri_cb(ser, &fake_urih, uri, method,
1670 get_params, headers);
1671 if (destroy_get_params) {
1672 ast_variables_destroy(get_params);
1673 }
1674
1675 ast_debug(2, "URI: %s DONE\n", uri);
1676
1677 return res;
1678}
static struct ast_websocket_server * ast_ws_server
void ast_http_error(struct ast_tcptls_session_instance *ser, int status, const char *title, const char *text)
Send HTTP error message and close socket.
Definition http.c:664
int AST_OPTIONAL_API_NAME() ast_websocket_uri_cb(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_vars, struct ast_variable *headers)
Callback suitable for use with a ast_http_uri.
#define ast_variable_new(name, value, filename)
#define ast_variable_list_append(head, new_var)
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition extconf.c:1260
const char * method
Definition res_pjsip.c:1279
Definition of a URI handler.
Definition http.h:102
void * data
Definition http.h:116

References ao2_cleanup, ao2_weakproxy_find, ast_channel_name(), ast_debug, ast_http_error(), ast_log, ast_variable_list_append, ast_variable_new, ast_variables_destroy(), ast_websocket_uri_cb(), ast_ws_server, ast_http_uri::data, instances, LOG_WARNING, method, ast_variable::name, ast_variable::next, NULL, OBJ_NOLOCK, OBJ_SEARCH_KEY, RAII_VAR, and ast_variable::value.

◆ instance_proxy_cb()

static void instance_proxy_cb ( void *  weakproxy,
void *  data 
)
static

Definition at line 1045 of file chan_websocket.c.

1046{
1047 struct instance_proxy *proxy = weakproxy;
1048 ast_debug(3, "%s: WebSocket instance removed from instances\n", proxy->connection_id);
1049 ao2_unlink(instances, weakproxy);
1050}
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition astobj2.h:1578
char connection_id[0]
The name of the module owning this sorcery instance.

References ao2_unlink, ast_debug, instance_proxy::connection_id, and instances.

Referenced by websocket_new().

◆ load_module()

static int load_module ( void  )
static

Function called when our module is loaded.

Definition at line 1712 of file chan_websocket.c.

1713{
1714 int res = 0;
1715 struct ast_websocket_protocol *protocol;
1716
1719 }
1720
1723 ast_log(LOG_ERROR, "Unable to register channel class 'WebSocket'\n");
1724 unload_module();
1726 }
1727
1729 AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, 17, instance_proxy_hash_fn,
1730 instance_proxy_sort_fn, instance_proxy_cmp_fn);
1731 if (!instances) {
1733 "Failed to allocate the chan_websocket instance registry\n");
1734 unload_module();
1736 }
1737
1739 if (!ast_ws_server) {
1740 unload_module();
1742 }
1743
1744 protocol = ast_websocket_sub_protocol_alloc("media");
1745 if (!protocol) {
1746 unload_module();
1748 }
1751
1753
1755}
@ AO2_ALLOC_OPT_LOCK_RWLOCK
Definition astobj2.h:365
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition astobj2.h:1303
@ AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE
Replace objects with duplicate keys in container.
Definition astobj2.h:1211
static struct ast_channel_tech websocket_tech
static struct ast_http_uri http_uri
static int unload_module(void)
Function called when our module is unloaded.
static void incoming_ws_established_cb(struct ast_websocket *ast_ws_session, struct ast_variable *get_params, struct ast_variable *upgrade_headers)
int ast_channel_register(const struct ast_channel_tech *tech)
Register a channel technology (a new channel driver) Called by a channel module to register the kind ...
Definition channel.c:539
@ AST_MEDIA_TYPE_UNKNOWN
Definition codec.h:31
int ast_format_cap_append_by_type(struct ast_format_cap *cap, enum ast_media_type type)
Add all codecs Asterisk knows about for a specific type to the capabilities structure.
Definition format_cap.c:216
@ AST_FORMAT_CAP_FLAG_DEFAULT
Definition format_cap.h:38
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition format_cap.h:49
int ast_http_uri_link(struct ast_http_uri *urihandler)
Register a URI handler.
Definition http.c:689
int AST_OPTIONAL_API_NAME() ast_websocket_server_add_protocol2(struct ast_websocket_server *server, struct ast_websocket_protocol *protocol)
Add a sub-protocol handler to the given server.
struct ast_websocket_protocol *AST_OPTIONAL_API_NAME() ast_websocket_sub_protocol_alloc(const char *name)
Allocate a websocket sub-protocol instance.
struct ast_websocket_server *AST_OPTIONAL_API_NAME() ast_websocket_server_create(void)
Creates a ast_websocket_server.
#define LOG_ERROR
@ AST_MODULE_LOAD_SUCCESS
Definition module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition module.h:78
struct ast_format_cap * capabilities
Definition channel.h:652
A websocket protocol implementation.
ast_websocket_callback session_established
Callback called when a new session is established. Mandatory.

References AO2_ALLOC_OPT_LOCK_RWLOCK, ao2_container_alloc_hash, AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, ast_channel_register(), ast_format_cap_alloc, ast_format_cap_append_by_type(), AST_FORMAT_CAP_FLAG_DEFAULT, ast_http_uri_link(), ast_log, AST_MEDIA_TYPE_UNKNOWN, AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_websocket_server_add_protocol2(), ast_websocket_server_create(), ast_websocket_sub_protocol_alloc(), ast_ws_server, ast_channel_tech::capabilities, http_uri, incoming_ws_established_cb(), instances, LOG_ERROR, LOG_WARNING, ast_websocket_protocol::session_established, unload_module(), and websocket_tech.

◆ process_binary_message()

static int process_binary_message ( struct websocket_pvt instance,
char *  payload,
uint64_t  payload_len 
)
static

Definition at line 641 of file chan_websocket.c.

643{
644 char *next_frame_ptr = NULL;
645 size_t bytes_read = 0;
646 int res = 0;
647 size_t bytes_left = 0;
648
649 {
650 SCOPED_LOCK(frame_queue_lock, &instance->frame_queue, AST_LIST_LOCK,
652 if (instance->frame_queue_length >= QUEUE_LENGTH_MAX) {
653 ast_debug(4, "%s: WebSocket queue is full. Ignoring incoming binary message.\n",
654 ast_channel_name(instance->channel));
655 return 0;
656 }
657 }
658
659 next_frame_ptr = payload;
660 instance->bytes_read += payload_len;
661
662 if (instance->passthrough) {
663 res = queue_frame_from_buffer(instance, payload, payload_len);
664 return res;
665 }
666
667 if (instance->bulk_media_in_progress && instance->leftover_len > 0) {
668 /*
669 * We have leftover data from a previous websocket message.
670 * Try to make a complete frame by appending data from
671 * the current message to the leftover data.
672 */
673 char *append_ptr = instance->leftover_data + instance->leftover_len;
674 size_t bytes_needed_for_frame = instance->optimal_frame_size - instance->leftover_len;
675 /*
676 * It's possible that even the current message doesn't have enough
677 * data to make a complete frame.
678 */
679 size_t bytes_avail_to_copy = MIN(bytes_needed_for_frame, payload_len);
680
681 /*
682 * Append whatever we can to the end of the leftover data
683 * even if it's not enough to make a complete frame.
684 */
685 memcpy(append_ptr, payload, bytes_avail_to_copy);
686
687 /*
688 * If leftover data is still short, just return and wait for the
689 * next websocket message.
690 */
691 if (bytes_avail_to_copy < bytes_needed_for_frame) {
692 ast_debug(4, "%s: Leftover data %d bytes but only %d new bytes available of %d needed. Appending and waiting for next message.\n",
693 ast_channel_name(instance->channel), (int)instance->leftover_len, (int)bytes_avail_to_copy, (int)bytes_needed_for_frame);
694 instance->leftover_len += bytes_avail_to_copy;
695 return 0;
696 }
697
698 res = queue_frame_from_buffer(instance, instance->leftover_data, instance->optimal_frame_size);
699 if (res < 0) {
700 return -1;
701 }
702
703 /*
704 * We stole data from the current payload so decrement payload_len
705 * and set the next frame pointer after the data in payload
706 * we just copied.
707 */
708 payload_len -= bytes_avail_to_copy;
709 next_frame_ptr = payload + bytes_avail_to_copy;
710
711 ast_debug(5, "%s: --- BR: %4d FQ: %4d PL: %4d LOL: %3d P: %p NFP: %p OFF: %4d NPL: %4d BAC: %3d\n",
712 ast_channel_name(instance->channel),
713 instance->frame_queue_length,
714 (int)instance->bytes_read,
715 (int)(payload_len + bytes_avail_to_copy),
716 (int)instance->leftover_len,
717 payload,
718 next_frame_ptr,
719 (int)(next_frame_ptr - payload),
720 (int)payload_len,
721 (int)bytes_avail_to_copy
722 );
723
724
725 instance->leftover_len = 0;
726 }
727
728 if (!instance->bulk_media_in_progress && instance->leftover_len > 0) {
729 instance->leftover_len = 0;
730 }
731
732 bytes_left = payload_len;
733 while (bytes_read < payload_len && bytes_left >= instance->optimal_frame_size) {
734 res = queue_frame_from_buffer(instance, next_frame_ptr,
735 instance->optimal_frame_size);
736 if (res < 0) {
737 break;
738 }
739 bytes_read += instance->optimal_frame_size;
740 next_frame_ptr += instance->optimal_frame_size;
741 bytes_left -= instance->optimal_frame_size;
742 }
743
744 if (instance->bulk_media_in_progress && bytes_left > 0) {
745 /*
746 * We have a partial frame. Save the leftover data.
747 */
748 ast_debug(5, "%s: +++ BR: %4d FQ: %4d PL: %4d LOL: %3d P: %p NFP: %p OFF: %4d BL: %4d\n",
749 ast_channel_name(instance->channel),
750 (int)instance->bytes_read,
751 instance->frame_queue_length,
752 (int)payload_len,
753 (int)instance->leftover_len,
754 payload,
755 next_frame_ptr,
756 (int)(next_frame_ptr - payload),
757 (int)bytes_left
758 );
759 memcpy(instance->leftover_data, next_frame_ptr, bytes_left);
760 instance->leftover_len = bytes_left;
761 }
762
763 return 0;
764}
static int queue_frame_from_buffer(struct websocket_pvt *instance, char *buffer, size_t len)
#define QUEUE_LENGTH_MAX
#define MIN(a, b)
Definition utils.h:252

References ast_channel_name(), ast_debug, AST_LIST_LOCK, AST_LIST_UNLOCK, websocket_pvt::bulk_media_in_progress, websocket_pvt::bytes_read, websocket_pvt::channel, websocket_pvt::frame_queue, websocket_pvt::frame_queue_length, websocket_pvt::leftover_data, websocket_pvt::leftover_len, MIN, NULL, websocket_pvt::optimal_frame_size, websocket_pvt::passthrough, queue_frame_from_buffer(), QUEUE_LENGTH_MAX, and SCOPED_LOCK.

Referenced by read_from_ws_and_queue().

◆ process_text_message()

static int process_text_message ( struct websocket_pvt instance,
char *  payload,
uint64_t  payload_len 
)
static

Definition at line 487 of file chan_websocket.c.

489{
490 int res = 0;
491 char *command;
492
493 if (payload_len > MAX_TEXT_MESSAGE_LEN) {
494 ast_log(LOG_WARNING, "%s: WebSocket TEXT message of length %d exceeds maximum length of %d\n",
495 ast_channel_name(instance->channel), (int)payload_len, MAX_TEXT_MESSAGE_LEN);
496 return 0;
497 }
498
499 /*
500 * Unfortunately, payload is not NULL terminated even when it's
501 * a TEXT frame so we need to allocate a new buffer, copy
502 * the data into it, and NULL terminate it.
503 */
504 command = ast_alloca(payload_len + 1);
505 memcpy(command, payload, payload_len); /* Safe */
506 command[payload_len] = '\0';
507 command = ast_strip(command);
508
509 ast_debug(4, "%s: WebSocket %s command received\n",
510 ast_channel_name(instance->channel), command);
511
512 if (ast_strings_equal(command, ANSWER_CHANNEL)) {
514
515 } else if (ast_strings_equal(command, HANGUP_CHANNEL)) {
517
518 } else if (ast_strings_equal(command, START_MEDIA_BUFFERING)) {
519 if (instance->passthrough) {
520 ast_debug(4, "%s: WebSocket in passthrough mode. Ignoring %s command.\n",
521 ast_channel_name(instance->channel), command);
522 return 0;
523 }
524 AST_LIST_LOCK(&instance->frame_queue);
525 instance->bulk_media_in_progress = 1;
526 AST_LIST_UNLOCK(&instance->frame_queue);
527
528 } else if (ast_begins_with(command, STOP_MEDIA_BUFFERING)) {
529 char *id;
530 char *option;
531 SCOPED_LOCK(frame_queue_lock, &instance->frame_queue, AST_LIST_LOCK,
533
534 id = ast_strip(command + strlen(STOP_MEDIA_BUFFERING));
535
536 if (instance->passthrough) {
537 ast_debug(4, "%s: WebSocket in passthrough mode. Ignoring %s command.\n",
538 ast_channel_name(instance->channel), command);
539 return 0;
540 }
541
542 ast_debug(4, "%s: WebSocket %s '%s' with %d bytes in leftover_data.\n",
544 (int)instance->leftover_len);
545
546 instance->bulk_media_in_progress = 0;
547 if (instance->leftover_len > 0) {
548 res = queue_frame_from_buffer(instance, instance->leftover_data, instance->leftover_len);
549 if (res != 0) {
550 return res;
551 }
552 }
553 instance->leftover_len = 0;
554 res = ast_asprintf(&option, "%s%s%s", MEDIA_BUFFERING_COMPLETED,
555 S_COR(!ast_strlen_zero(id), " ", ""), S_OR(id, ""));
556 if (res <= 0 || !option) {
557 return res;
558 }
559 res = queue_option_frame(instance, option);
560 ast_free(option);
561
562 } else if (ast_strings_equal(command, FLUSH_MEDIA)) {
563 struct ast_frame *frame = NULL;
564
565 if (instance->passthrough) {
566 ast_debug(4, "%s: WebSocket in passthrough mode. Ignoring %s command.\n",
567 ast_channel_name(instance->channel), command);
568 return 0;
569 }
570
571 AST_LIST_LOCK(&instance->frame_queue);
572 while ((frame = AST_LIST_REMOVE_HEAD(&instance->frame_queue, frame_list))) {
573 ast_frfree(frame);
574 }
575 instance->frame_queue_length = 0;
576 instance->bulk_media_in_progress = 0;
577 instance->leftover_len = 0;
578 AST_LIST_UNLOCK(&instance->frame_queue);
579
580 } else if (ast_strings_equal(command, REPORT_QUEUE_DRAINED)) {
581 if (instance->passthrough) {
582 ast_debug(4, "%s: WebSocket in passthrough mode. Ignoring %s command.\n",
583 ast_channel_name(instance->channel), command);
584 return 0;
585 }
586
587 AST_LIST_LOCK(&instance->frame_queue);
588 instance->report_queue_drained = 1;
589 AST_LIST_UNLOCK(&instance->frame_queue);
590
591 } else if (ast_strings_equal(command, GET_DRIVER_STATUS)) {
592 char *status = NULL;
593
594 res = ast_asprintf(&status, "%s channel_id:%s queue_length:%d xon_level:%d xoff_level:%d queue_full:%s bulk_media:%s media_paused:%s",
596 ast_channel_uniqueid(instance->channel),
599 S_COR(instance->queue_full, "true", "false"),
600 S_COR(instance->bulk_media_in_progress, "true", "false"),
601 S_COR(instance->queue_paused, "true", "false")
602 );
603 if (res <= 0 || !status) {
605 res = -1;
606 } else {
607 ast_debug(4, "%s: WebSocket status: %s\n",
608 ast_channel_name(instance->channel), status);
611 }
612
613 } else if (ast_strings_equal(command, PAUSE_MEDIA)) {
614 if (instance->passthrough) {
615 ast_debug(4, "%s: WebSocket in passthrough mode. Ignoring %s command.\n",
616 ast_channel_name(instance->channel), command);
617 return 0;
618 }
619 AST_LIST_LOCK(&instance->frame_queue);
620 instance->queue_paused = 1;
621 AST_LIST_UNLOCK(&instance->frame_queue);
622
623 } else if (ast_strings_equal(command, CONTINUE_MEDIA)) {
624 if (instance->passthrough) {
625 ast_debug(4, "%s: WebSocket in passthrough mode. Ignoring %s command.\n",
626 ast_channel_name(instance->channel), command);
627 return 0;
628 }
629 AST_LIST_LOCK(&instance->frame_queue);
630 instance->queue_paused = 0;
631 AST_LIST_UNLOCK(&instance->frame_queue);
632
633 } else {
634 ast_log(LOG_WARNING, "%s: WebSocket %s command unknown\n",
635 ast_channel_name(instance->channel), command);
636 }
637
638 return res;
639}
jack_status_t status
Definition app_jack.c:149
enum queue_result id
Definition app_queue.c:1771
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition astmm.h:288
#define ast_free(a)
Definition astmm.h:180
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition astmm.h:267
#define FLUSH_MEDIA
#define MEDIA_BUFFERING_COMPLETED
#define ANSWER_CHANNEL
#define HANGUP_CHANNEL
#define DRIVER_STATUS
#define GET_DRIVER_STATUS
#define START_MEDIA_BUFFERING
#define QUEUE_LENGTH_XOFF_LEVEL
#define PAUSE_MEDIA
#define REPORT_QUEUE_DRAINED
#define CONTINUE_MEDIA
#define STOP_MEDIA_BUFFERING
#define MAX_TEXT_MESSAGE_LEN
static int queue_option_frame(struct websocket_pvt *instance, char *buffer)
const char * ast_channel_uniqueid(const struct ast_channel *chan)
#define ast_frfree(fr)
@ AST_CONTROL_ANSWER
int ast_strings_equal(const char *str1, const char *str2)
Compare strings for equality checking for NULL.
Definition strings.c:238
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition strings.h:80
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition strings.h:87
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix)
Checks whether a string begins with another.
Definition strings.h:97
char * ast_strip(char *s)
Strip leading/trailing whitespace from a string.
Definition strings.h:223

References ANSWER_CHANNEL, ast_alloca, ast_asprintf, ast_begins_with(), ast_channel_name(), ast_channel_uniqueid(), AST_CONTROL_ANSWER, AST_CONTROL_HANGUP, ast_debug, ast_free, ast_frfree, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_log, ast_queue_control(), ast_strings_equal(), ast_strip(), ast_strlen_zero(), ast_websocket_write_string(), websocket_pvt::bulk_media_in_progress, websocket_pvt::channel, CONTINUE_MEDIA, DRIVER_STATUS, FLUSH_MEDIA, websocket_pvt::frame_queue, websocket_pvt::frame_queue_length, GET_DRIVER_STATUS, HANGUP_CHANNEL, id, websocket_pvt::leftover_data, websocket_pvt::leftover_len, LOG_WARNING, MAX_TEXT_MESSAGE_LEN, MEDIA_BUFFERING_COMPLETED, NULL, websocket_pvt::passthrough, PAUSE_MEDIA, queue_frame_from_buffer(), websocket_pvt::queue_full, QUEUE_LENGTH_XOFF_LEVEL, QUEUE_LENGTH_XON_LEVEL, queue_option_frame(), websocket_pvt::queue_paused, websocket_pvt::report_queue_drained, REPORT_QUEUE_DRAINED, S_COR, S_OR, SCOPED_LOCK, START_MEDIA_BUFFERING, status, STOP_MEDIA_BUFFERING, and websocket_pvt::websocket.

Referenced by read_from_ws_and_queue().

◆ queue_frame_from_buffer()

static int queue_frame_from_buffer ( struct websocket_pvt instance,
char *  buffer,
size_t  len 
)
static

Definition at line 423 of file chan_websocket.c.

425{
426 struct ast_frame fr = { 0, };
427 struct ast_frame *duped_frame = NULL;
428
429 AST_FRAME_SET_BUFFER(&fr, buffer, 0, len);
431 fr.subclass.format = instance->native_format;
432 fr.samples = instance->native_codec->samples_count(&fr);
433
434 duped_frame = ast_frisolate(&fr);
435 if (!duped_frame) {
436 ast_log(LOG_WARNING, "%s: Failed to isolate frame\n",
437 ast_channel_name(instance->channel));
438 return -1;
439 }
440
441 {
442 SCOPED_LOCK(frame_queue_lock, &instance->frame_queue, AST_LIST_LOCK,
444 AST_LIST_INSERT_TAIL(&instance->frame_queue, duped_frame, frame_list);
445 instance->frame_queue_length++;
446 if (!instance->queue_full && instance->frame_queue_length >= QUEUE_LENGTH_XOFF_LEVEL) {
447 instance->queue_full = 1;
448 ast_debug(4, "%s: WebSocket sending %s\n",
451 }
452 }
453
454 ast_debug(5, "%s: Queued %d byte frame\n", ast_channel_name(instance->channel),
455 duped_frame->datalen);
456
457 return 0;
458}
#define MEDIA_XOFF
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define ast_frisolate(fr)
Makes a frame independent of any static storage.
#define AST_FRAME_SET_BUFFER(fr, _base, _ofs, _datalen)
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
int(* samples_count)(struct ast_frame *frame)
Retrieve the number of samples in a frame.
Definition codec.h:68
struct ast_format * format
struct ast_format * native_format
struct ast_codec * native_codec

References ast_channel_name(), ast_debug, AST_FRAME_SET_BUFFER, AST_FRAME_VOICE, ast_frisolate, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log, ast_websocket_write_string(), websocket_pvt::channel, ast_frame::datalen, ast_frame_subclass::format, websocket_pvt::frame_queue, websocket_pvt::frame_queue_length, ast_frame::frametype, len(), LOG_WARNING, MEDIA_XOFF, websocket_pvt::native_codec, websocket_pvt::native_format, NULL, websocket_pvt::queue_full, QUEUE_LENGTH_XOFF_LEVEL, ast_frame::samples, ast_codec::samples_count, SCOPED_LOCK, ast_frame::subclass, and websocket_pvt::websocket.

Referenced by process_binary_message(), and process_text_message().

◆ queue_option_frame()

static int queue_option_frame ( struct websocket_pvt instance,
char *  buffer 
)
static

Definition at line 460 of file chan_websocket.c.

462{
463 struct ast_frame fr = { 0, };
464 struct ast_frame *duped_frame = NULL;
465
466 AST_FRAME_SET_BUFFER(&fr, buffer, 0, strlen(buffer) + 1);
469
470 duped_frame = ast_frisolate(&fr);
471 if (!duped_frame) {
472 ast_log(LOG_WARNING, "%s: Failed to isolate frame\n",
473 ast_channel_name(instance->channel));
474 return -1;
475 }
476
477 AST_LIST_LOCK(&instance->frame_queue);
478 AST_LIST_INSERT_TAIL(&instance->frame_queue, duped_frame, frame_list);
479 AST_LIST_UNLOCK(&instance->frame_queue);
480
481 ast_debug(4, "%s: Queued '%s' option frame\n",
482 ast_channel_name(instance->channel), buffer);
483
484 return 0;
485}

References ast_channel_name(), AST_CONTROL_OPTION, ast_debug, AST_FRAME_CONTROL, AST_FRAME_SET_BUFFER, ast_frisolate, AST_LIST_INSERT_TAIL, AST_LIST_LOCK, AST_LIST_UNLOCK, ast_log, websocket_pvt::channel, websocket_pvt::frame_queue, ast_frame::frametype, ast_frame_subclass::integer, LOG_WARNING, NULL, and ast_frame::subclass.

Referenced by process_text_message().

◆ read_from_ws_and_queue()

static int read_from_ws_and_queue ( struct websocket_pvt instance)
static

Definition at line 766 of file chan_websocket.c.

767{
768 uint64_t payload_len = 0;
769 char *payload = NULL;
770 enum ast_websocket_opcode opcode;
771 int fragmented = 0;
772 int res = 0;
773
774 if (!instance || !instance->websocket) {
775 ast_log(LOG_WARNING, "%s: WebSocket instance not found\n",
776 ast_channel_name(instance->channel));
777 return -1;
778 }
779
780 ast_debug(9, "%s: Waiting for websocket to have data\n", ast_channel_name(instance->channel));
781 res = ast_wait_for_input(
782 ast_websocket_fd(instance->websocket), -1);
783 if (res <= 0) {
784 ast_log(LOG_WARNING, "%s: WebSocket read failed: %s\n",
785 ast_channel_name(instance->channel), strerror(errno));
786 return -1;
787 }
788
789 /*
790 * We need to lock here to prevent the websocket handle from
791 * being pulled out from under us if the core sends us a
792 * hangup request.
793 */
794 ao2_lock(instance);
795 if (!instance->websocket) {
796 ao2_unlock(instance);
797 return -1;
798 }
799
800 res = ast_websocket_read(instance->websocket, &payload, &payload_len,
801 &opcode, &fragmented);
802 ao2_unlock(instance);
803 if (res) {
804 return -1;
805 }
806 ast_debug(5, "%s: WebSocket read %d bytes\n", ast_channel_name(instance->channel),
807 (int)payload_len);
808
809 if (opcode == AST_WEBSOCKET_OPCODE_TEXT) {
810 return process_text_message(instance, payload, payload_len);
811 }
812
813 if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) {
814 ast_debug(5, "%s: WebSocket closed by remote\n",
815 ast_channel_name(instance->channel));
816 return -1;
817 }
818
819 if (opcode != AST_WEBSOCKET_OPCODE_BINARY) {
820 ast_debug(5, "%s: WebSocket frame type %d not supported. Ignoring.\n",
821 ast_channel_name(instance->channel), (int)opcode);
822 return 0;
823 }
824
825 return process_binary_message(instance, payload, payload_len);
826}
#define ao2_unlock(a)
Definition astobj2.h:729
#define ao2_lock(a)
Definition astobj2.h:717
static int process_text_message(struct websocket_pvt *instance, char *payload, uint64_t payload_len)
static int process_binary_message(struct websocket_pvt *instance, char *payload, uint64_t payload_len)
ast_websocket_opcode
WebSocket operation codes.
@ AST_WEBSOCKET_OPCODE_BINARY
@ AST_WEBSOCKET_OPCODE_CLOSE
@ AST_WEBSOCKET_OPCODE_TEXT
int AST_OPTIONAL_API_NAME() ast_websocket_read(struct ast_websocket *session, char **payload, uint64_t *payload_len, enum ast_websocket_opcode *opcode, int *fragmented)
Read a WebSocket frame and handle it.
int ast_wait_for_input(int fd, int ms)
Definition utils.c:1734

References ao2_lock, ao2_unlock, ast_channel_name(), ast_debug, ast_log, ast_wait_for_input(), ast_websocket_fd(), AST_WEBSOCKET_OPCODE_BINARY, AST_WEBSOCKET_OPCODE_CLOSE, AST_WEBSOCKET_OPCODE_TEXT, ast_websocket_read(), websocket_pvt::channel, errno, LOG_WARNING, NULL, process_binary_message(), process_text_message(), and websocket_pvt::websocket.

Referenced by read_thread_handler().

◆ read_thread_handler()

static void * read_thread_handler ( void *  obj)
static

Definition at line 838 of file chan_websocket.c.

839{
840 RAII_VAR(struct websocket_pvt *, instance, obj, ao2_cleanup);
841 RAII_VAR(char *, command, NULL, ast_free);
842 int res = 0;
843
844 ast_debug(3, "%s: Read thread started\n", ast_channel_name(instance->channel));
845
846 /*
847 * We need to tell the remote app what channel this media is for.
848 * This is especially important for outbound connections otherwise
849 * the app won't know who the media is for.
850 */
851 res = ast_asprintf(&command, "%s connection_id:%s channel:%s channel_id:%s format:%s optimal_frame_size:%d ptime:%d", MEDIA_START,
852 instance->connection_id, ast_channel_name(instance->channel),
853 ast_channel_uniqueid(instance->channel),
854 ast_format_get_name(instance->native_format),
855 instance->optimal_frame_size, instance->native_codec->default_ms);
856 if (res <= 0 || !command) {
857 ast_queue_control(instance->channel, AST_CONTROL_HANGUP);
858 ast_log(LOG_ERROR, "%s: Failed to create MEDIA_START\n", ast_channel_name(instance->channel));
859 return NULL;
860 }
861 res = ast_websocket_write_string(instance->websocket, command);
862 if (res != 0) {
863 ast_log(LOG_ERROR, "%s: Failed to send MEDIA_START\n", ast_channel_name(instance->channel));
864 ast_queue_control(instance->channel, AST_CONTROL_HANGUP);
865 return NULL;
866 }
867 ast_debug(3, "%s: Sent %s\n", ast_channel_name(instance->channel),
868 command);
869
870 if (!instance->no_auto_answer) {
871 ast_debug(3, "%s: ANSWER by auto_answer\n", ast_channel_name(instance->channel));
872 ast_queue_control(instance->channel, AST_CONTROL_ANSWER);
873 }
874
875 while (read_from_ws_and_queue(instance) == 0)
876 {
877 }
878
879 /*
880 * websocket_hangup will take care of closing the websocket if needed.
881 */
882 ast_debug(3, "%s: HANGUP by websocket close/error\n", ast_channel_name(instance->channel));
883 ast_queue_control(instance->channel, AST_CONTROL_HANGUP);
884
885 return NULL;
886}
#define MEDIA_START
static int read_from_ws_and_queue(struct websocket_pvt *instance)
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition format.c:334

References ao2_cleanup, ast_asprintf, ast_channel_name(), ast_channel_uniqueid(), AST_CONTROL_ANSWER, AST_CONTROL_HANGUP, ast_debug, ast_format_get_name(), ast_free, ast_log, ast_queue_control(), ast_websocket_write_string(), LOG_ERROR, MEDIA_START, NULL, RAII_VAR, and read_from_ws_and_queue().

Referenced by incoming_ws_established_cb(), and webchan_call().

◆ set_channel_format()

static void set_channel_format ( struct websocket_pvt instance,
struct ast_format fmt 
)
static

Definition at line 201 of file chan_websocket.c.

203{
208 ast_debug(4, "Switching readformat to %s\n", ast_format_get_name(fmt));
209 }
210}
struct ast_format * ast_channel_rawreadformat(struct ast_channel *chan)
int ast_set_read_format(struct ast_channel *chan, struct ast_format *format)
Sets read format on channel chan.
Definition channel.c:5757
void ast_channel_set_rawreadformat(struct ast_channel *chan, struct ast_format *format)
struct ast_format * ast_channel_readformat(struct ast_channel *chan)
enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2)
Compare two formats.
Definition format.c:201
@ AST_FORMAT_CMP_NOT_EQUAL
Definition format.h:38

References ast_channel_rawreadformat(), ast_channel_readformat(), ast_channel_set_rawreadformat(), ast_debug, ast_format_cmp(), AST_FORMAT_CMP_NOT_EQUAL, ast_format_get_name(), ast_set_read_format(), and websocket_pvt::channel.

Referenced by webchan_read().

◆ set_channel_timer()

static int set_channel_timer ( struct websocket_pvt instance)
static

Definition at line 1210 of file chan_websocket.c.

1211{
1212 int rate = 0;
1213 instance->timer = ast_timer_open();
1214 if (!instance->timer) {
1215 return -1;
1216 }
1217 /* Rate is the number of ticks per second, not the interval. */
1218 rate = 1000 / ast_format_get_default_ms(instance->native_format);
1219 ast_debug(3, "%s: WebSocket timer rate %d\n",
1220 ast_channel_name(instance->channel), rate);
1221 ast_timer_set_rate(instance->timer, rate);
1222 /*
1223 * Calling ast_channel_set_fd will cause the channel thread to call
1224 * webchan_read at 'rate' times per second.
1225 */
1226 ast_channel_set_fd(instance->channel, 0, ast_timer_fd(instance->timer));
1227
1228 return 0;
1229}
void ast_channel_set_fd(struct ast_channel *chan, int which, int fd)
Definition channel.c:2416
unsigned int ast_format_get_default_ms(const struct ast_format *format)
Get the default framing size (in milliseconds) for a format.
Definition format.c:359
struct ast_timer * timer
int ast_timer_set_rate(const struct ast_timer *handle, unsigned int rate)
Set the timing tick rate.
Definition timing.c:166
struct ast_timer * ast_timer_open(void)
Open a timer.
Definition timing.c:122
int ast_timer_fd(const struct ast_timer *handle)
Get a poll()-able file descriptor for a timer.
Definition timing.c:161

References ast_channel_name(), ast_channel_set_fd(), ast_debug, ast_format_get_default_ms(), ast_timer_fd(), ast_timer_open(), ast_timer_set_rate(), websocket_pvt::channel, websocket_pvt::native_format, and websocket_pvt::timer.

Referenced by webchan_request().

◆ set_channel_variables()

static int set_channel_variables ( struct websocket_pvt instance)
static

Definition at line 1231 of file chan_websocket.c.

1232{
1233 char *pkt_size = NULL;
1234 int res = ast_asprintf(&pkt_size, "%d", instance->optimal_frame_size);
1235 if (res <= 0) {
1236 return -1;
1237 }
1238
1240 pkt_size);
1241 ast_free(pkt_size);
1243 instance->connection_id);
1244
1245 return 0;
1246}
#define MEDIA_WEBSOCKET_CONNECTION_ID
#define MEDIA_WEBSOCKET_OPTIMAL_FRAME_SIZE
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.

References ast_asprintf, ast_free, websocket_pvt::channel, websocket_pvt::connection_id, MEDIA_WEBSOCKET_CONNECTION_ID, MEDIA_WEBSOCKET_OPTIMAL_FRAME_SIZE, NULL, websocket_pvt::optimal_frame_size, and pbx_builtin_setvar_helper().

Referenced by webchan_request().

◆ set_instance_silence_frame()

static int set_instance_silence_frame ( struct websocket_pvt instance)
static

Definition at line 1187 of file chan_websocket.c.

1188{
1189 instance->silence.frametype = AST_FRAME_VOICE;
1190 instance->silence.datalen =
1191 (instance->slin_codec->default_ms * instance->slin_codec->minimum_bytes) / instance->slin_codec->minimum_ms;
1192 instance->silence.samples = instance->silence.datalen / sizeof(uint16_t);
1193 /*
1194 * Even though we'll calloc the data pointer, we don't mark it as
1195 * mallocd because this frame will be around for a while and we don't
1196 * want it accidentally freed before we're done with it.
1197 */
1198 instance->silence.mallocd = 0;
1199 instance->silence.offset = 0;
1200 instance->silence.src = __PRETTY_FUNCTION__;
1201 instance->silence.subclass.format = instance->slin_format;
1202 instance->silence.data.ptr = ast_calloc(1, instance->silence.datalen);
1203 if (!instance->silence.data.ptr) {
1204 return -1;
1205 }
1206
1207 return 0;
1208}
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
unsigned int minimum_bytes
Length in bytes of the data payload of a minimum_ms frame.
Definition codec.h:60
unsigned int default_ms
Default length of media carried (in milliseconds) in a frame.
Definition codec.h:58
unsigned int minimum_ms
Minimum length of media that can be carried (in milliseconds) in a frame.
Definition codec.h:54
struct ast_codec * slin_codec
struct ast_format * slin_format
struct ast_frame silence

References ast_calloc, AST_FRAME_VOICE, ast_frame::data, ast_frame::datalen, ast_codec::default_ms, ast_frame_subclass::format, ast_frame::frametype, ast_frame::mallocd, ast_codec::minimum_bytes, ast_codec::minimum_ms, ast_frame::offset, ast_frame::ptr, ast_frame::samples, websocket_pvt::silence, websocket_pvt::slin_codec, websocket_pvt::slin_format, ast_frame::src, and ast_frame::subclass.

Referenced by webchan_request().

◆ set_instance_translator()

static int set_instance_translator ( struct websocket_pvt instance)
static

Definition at line 1156 of file chan_websocket.c.

1157{
1159 instance->slin_format = ao2_bump(instance->native_format);
1160 instance->slin_codec = ast_format_get_codec(instance->slin_format);
1161 return 0;
1162 }
1163
1165 if (!instance->slin_format) {
1166 ast_log(LOG_ERROR, "%s: Unable to get slin format for rate %d\n",
1167 ast_channel_name(instance->channel), instance->native_codec->sample_rate);
1168 return -1;
1169 }
1170 ast_debug(3, "%s: WebSocket channel slin format '%s' Sample rate: %d ptime: %dms\n",
1174
1175 instance->translator = ast_translator_build_path(instance->slin_format, instance->native_format);
1176 if (!instance->translator) {
1177 ast_log(LOG_ERROR, "%s: Unable to build translator path from '%s' to '%s'\n",
1179 ast_format_get_name(instance->slin_format));
1180 return -1;
1181 }
1182
1183 instance->slin_codec = ast_format_get_codec(instance->slin_format);
1184 return 0;
1185}
struct ast_codec * ast_format_get_codec(const struct ast_format *format)
Get the codec associated with a format.
Definition format.c:324
unsigned int ast_format_get_sample_rate(const struct ast_format *format)
Get the sample rate of a media format.
Definition format.c:379
int ast_format_cache_is_slinear(struct ast_format *format)
Determines if a format is one of the cached slin formats.
struct ast_format * ast_format_cache_get_slin_by_rate(unsigned int rate)
Retrieve the best signed linear format given a sample rate.
unsigned int sample_rate
Sample rate (number of samples carried in a second)
Definition codec.h:52
struct ast_trans_pvt * translator
struct ast_trans_pvt * ast_translator_build_path(struct ast_format *dest, struct ast_format *source)
Builds a translator path Build a path (possibly NULL) from source to dest.
Definition translate.c:486

References ao2_bump, ast_channel_name(), ast_debug, ast_format_cache_get_slin_by_rate(), ast_format_cache_is_slinear(), ast_format_get_codec(), ast_format_get_default_ms(), ast_format_get_name(), ast_format_get_sample_rate(), ast_log, ast_translator_build_path(), websocket_pvt::channel, LOG_ERROR, websocket_pvt::native_codec, websocket_pvt::native_format, ast_codec::sample_rate, websocket_pvt::slin_codec, websocket_pvt::slin_format, and websocket_pvt::translator.

Referenced by webchan_request().

◆ unload_module()

static int unload_module ( void  )
static

Function called when our module is unloaded.

Definition at line 1691 of file chan_websocket.c.

1692{
1696
1700
1702 instances = NULL;
1703
1704 return 0;
1705}
void ast_channel_unregister(const struct ast_channel_tech *tech)
Unregister a channel technology.
Definition channel.c:570
void ast_http_uri_unlink(struct ast_http_uri *urihandler)
Unregister a URI handler.
Definition http.c:721

References ao2_cleanup, ast_channel_unregister(), ast_http_uri_unlink(), ast_ws_server, ast_channel_tech::capabilities, http_uri, instances, NULL, and websocket_tech.

Referenced by load_module().

◆ validate_uri_parameters()

static int validate_uri_parameters ( const char *  uri_params)
static

Definition at line 1248 of file chan_websocket.c.

1249{
1250 char *params = ast_strdupa(uri_params);
1251 char *nvp = NULL;
1252 char *nv = NULL;
1253
1254 /*
1255 * uri_params should be a comma-separated list of key=value pairs.
1256 * For example:
1257 * name1=value1,name2=value2
1258 * We're verifying that each name and value either doesn't need
1259 * to be encoded or that it already is.
1260 */
1261
1262 while((nvp = ast_strsep(&params, ',', 0))) {
1263 /* nvp will be name1=value1 */
1264 while((nv = ast_strsep(&nvp, '=', 0))) {
1265 /* nv will be either name1 or value1 */
1266 if (!ast_uri_verify_encoded(nv)) {
1267 return 0;
1268 }
1269 }
1270 }
1271
1272 return 1;
1273}
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition astmm.h:298
char * ast_strsep(char **s, const char sep, uint32_t flags)
Act like strsep but ignore separators inside quotes.
Definition utils.c:1871
int ast_uri_verify_encoded(const char *string)
Verify if a string is valid as a URI component.
Definition utils.c:781

References ast_strdupa, ast_strsep(), ast_uri_verify_encoded(), NULL, and websocket_pvt::uri_params.

Referenced by webchan_request().

◆ webchan_call()

static int webchan_call ( struct ast_channel ast,
const char *  dest,
int  timeout 
)
static

Definition at line 921 of file chan_websocket.c.

923{
924 struct websocket_pvt *instance = ast_channel_tech_pvt(ast);
925 int nodelay = 1;
927
928 if (!instance) {
929 ast_log(LOG_WARNING, "%s: WebSocket instance not found\n",
930 ast_channel_name(ast));
931 return -1;
932 }
933
934 if (instance->type == AST_WS_TYPE_SERVER) {
935 ast_debug(3, "%s: Websocket call incoming\n", ast_channel_name(instance->channel));
936 return 0;
937 }
938 ast_debug(3, "%s: Websocket call outgoing\n", ast_channel_name(instance->channel));
939
940 if (!instance->client) {
941 ast_log(LOG_WARNING, "%s: WebSocket client not found\n",
942 ast_channel_name(ast));
943 return -1;
944 }
945
946 ast_debug(3, "%s: WebSocket call requested to %s. cid: %s\n",
947 ast_channel_name(ast), dest, instance->connection_id);
948
949 if (!ast_strlen_zero(instance->uri_params)) {
951 }
952
953 instance->websocket = ast_websocket_client_connect(instance->client,
954 instance, ast_channel_name(ast), &result);
955 if (!instance->websocket || result != WS_OK) {
956 ast_log(LOG_WARNING, "%s: WebSocket connection failed to %s: %s\n",
958 return -1;
959 }
960
961 if (setsockopt(ast_websocket_fd(instance->websocket),
962 IPPROTO_TCP, TCP_NODELAY, (char *) &nodelay, sizeof(nodelay)) < 0) {
963 ast_log(LOG_WARNING, "Failed to set TCP_NODELAY on websocket connection: %s\n", strerror(errno));
964 }
965
966 ast_debug(3, "%s: WebSocket connection to %s established\n",
967 ast_channel_name(ast), dest);
968
969 /* read_thread_handler() will clean up the bump */
971 read_thread_handler, ao2_bump(instance))) {
972 ast_log(LOG_WARNING, "%s: Failed to create thread.\n", ast_channel_name(ast));
973 ao2_cleanup(instance);
974 return -1;
975 }
976
977 return 0;
978}
static PGresult * result
Definition cel_pgsql.c:84
void * ast_channel_tech_pvt(const struct ast_channel *chan)
ast_websocket_result
Result code for a websocket client.
@ WS_OK
@ AST_WS_TYPE_SERVER
const char *AST_OPTIONAL_API_NAME() ast_websocket_result_to_str(enum ast_websocket_result result)
Convert a websocket result code to a string.
enum ast_websocket_type type
pthread_t outbound_read_thread
struct ast_websocket_client * client
#define ast_pthread_create_detached_background(a, b, c, d)
Definition utils.h:637
void ast_websocket_client_add_uri_params(struct ast_websocket_client *wc, const char *uri_params)
Add additional parameters to the URI.
struct ast_websocket * ast_websocket_client_connect(struct ast_websocket_client *wc, void *lock_obj, const char *display_name, enum ast_websocket_result *result)
Connect to a websocket server using the configured authentication, retry and TLS options.

References ao2_bump, ao2_cleanup, ast_channel_name(), ast_channel_tech_pvt(), ast_debug, ast_log, ast_pthread_create_detached_background, ast_strlen_zero(), ast_websocket_client_add_uri_params(), ast_websocket_client_connect(), ast_websocket_fd(), ast_websocket_result_to_str(), AST_WS_TYPE_SERVER, websocket_pvt::channel, websocket_pvt::client, websocket_pvt::connection_id, errno, LOG_WARNING, NULL, websocket_pvt::outbound_read_thread, read_thread_handler(), result, websocket_pvt::type, websocket_pvt::uri_params, websocket_pvt::websocket, and WS_OK.

◆ webchan_hangup()

static int webchan_hangup ( struct ast_channel ast)
static

Definition at line 1472 of file chan_websocket.c.

1473{
1474 struct websocket_pvt *instance = ast_channel_tech_pvt(ast);
1475
1476 if (!instance) {
1477 return -1;
1478 }
1479 ast_debug(3, "%s: WebSocket call hangup. cid: %s\n",
1480 ast_channel_name(ast), instance->connection_id);
1481
1482 /*
1483 * We need to lock because read_from_ws_and_queue() is probably waiting
1484 * on the websocket file descriptor and will unblock and immediately try to
1485 * check the websocket and read from it. We don't want to pull the
1486 * websocket out from under it between the check and read.
1487 */
1488 ao2_lock(instance);
1489 if (instance->websocket) {
1490 ast_websocket_close(instance->websocket, 1000);
1491 ast_websocket_unref(instance->websocket);
1492 instance->websocket = NULL;
1493 }
1495 ao2_unlock(instance);
1496
1497 /* Clean up the reference from adding the instance to the channel */
1498 ao2_cleanup(instance);
1499
1500 return 0;
1501}
void ast_channel_tech_pvt_set(struct ast_channel *chan, void *value)

References ao2_cleanup, ao2_lock, ao2_unlock, ast_channel_name(), ast_channel_tech_pvt(), ast_channel_tech_pvt_set(), ast_debug, ast_websocket_close(), ast_websocket_unref(), websocket_pvt::connection_id, NULL, and websocket_pvt::websocket.

◆ webchan_read()

static struct ast_frame * webchan_read ( struct ast_channel ast)
static

Definition at line 323 of file chan_websocket.c.

324{
325 struct websocket_pvt *instance = NULL;
326 struct ast_frame *native_frame = NULL;
327 struct ast_frame *slin_frame = NULL;
328
329 instance = ast_channel_tech_pvt(ast);
330 if (!instance) {
331 return NULL;
332 }
333
335 ast_timer_ack(instance->timer, 1);
336 }
337
338 native_frame = dequeue_frame(instance);
339
340 /*
341 * No frame when the timer fires means we have to create and
342 * return a silence frame in its place.
343 */
344 if (!native_frame) {
345 ast_debug(5, "%s: WebSocket read timer fired with no frame available. Returning silence.\n", ast_channel_name(ast));
346 set_channel_format(instance, instance->slin_format);
347 slin_frame = ast_frdup(&instance->silence);
348 return slin_frame;
349 }
350
351 /*
352 * If we're in passthrough mode or the frame length is already optimal_frame_size,
353 * we can just return it.
354 */
355 if (instance->passthrough || native_frame->datalen == instance->optimal_frame_size) {
356 set_channel_format(instance, instance->native_format);
357 return native_frame;
358 }
359
360 /*
361 * If we're here, we have a short frame that we need to pad
362 * with silence.
363 */
364
365 if (instance->translator) {
366 slin_frame = ast_translate(instance->translator, native_frame, 0);
367 if (!slin_frame) {
368 ast_log(LOG_WARNING, "%s: Failed to translate %d byte frame\n",
369 ast_channel_name(ast), native_frame->datalen);
370 return NULL;
371 }
372 ast_frame_free(native_frame, 0);
373 } else {
374 /*
375 * If there was no translator then the native format
376 * was already slin.
377 */
378 slin_frame = native_frame;
379 }
380
381 set_channel_format(instance, instance->slin_format);
382
383 /*
384 * So now we have an slin frame but it's probably still short
385 * so we create a new data buffer with the correct length
386 * which is filled with zeros courtesy of ast_calloc.
387 * We then copy the short frame data into the new buffer
388 * and set the offset to AST_FRIENDLY_OFFSET so that
389 * the core can read the data without any issues.
390 * If the original frame data was mallocd, we need to free the old
391 * data buffer so we don't leak memory and we need to set
392 * mallocd to AST_MALLOCD_DATA so that the core knows
393 * it needs to free the new data buffer when it's done.
394 */
395
396 if (slin_frame->datalen != instance->silence.datalen) {
397 char *old_data = slin_frame->data.ptr;
398 int old_len = slin_frame->datalen;
399 int old_offset = slin_frame->offset;
400 ast_debug(4, "%s: WebSocket read short frame. Expected %d got %d. Filling with silence\n",
401 ast_channel_name(ast), instance->silence.datalen,
402 slin_frame->datalen);
403
404 slin_frame->data.ptr = ast_calloc(1, instance->silence.datalen + AST_FRIENDLY_OFFSET);
405 if (!slin_frame->data.ptr) {
406 ast_frame_free(slin_frame, 0);
407 return NULL;
408 }
409 slin_frame->data.ptr += AST_FRIENDLY_OFFSET;
410 slin_frame->offset = AST_FRIENDLY_OFFSET;
411 memcpy(slin_frame->data.ptr, old_data, old_len);
412 if (slin_frame->mallocd & AST_MALLOCD_DATA) {
413 ast_free(old_data - old_offset);
414 }
415 slin_frame->mallocd |= AST_MALLOCD_DATA;
416 slin_frame->datalen = instance->silence.datalen;
417 slin_frame->samples = instance->silence.samples;
418 }
419
420 return slin_frame;
421}
static struct ast_frame * dequeue_frame(struct websocket_pvt *instance)
static void set_channel_format(struct websocket_pvt *instance, struct ast_format *fmt)
#define ast_frdup(fr)
Copies a frame.
#define AST_MALLOCD_DATA
#define AST_FRIENDLY_OFFSET
Offset into a frame's data buffer.
int ast_timer_ack(const struct ast_timer *handle, unsigned int quantity)
Acknowledge a timer event.
Definition timing.c:171
enum ast_timer_event ast_timer_get_event(const struct ast_timer *handle)
Retrieve timing event.
Definition timing.c:186
@ AST_TIMING_EVENT_EXPIRED
Definition timing.h:58
struct ast_frame * ast_translate(struct ast_trans_pvt *tr, struct ast_frame *f, int consume)
translates one or more frames Apply an input frame into the translator and receive zero or one output...
Definition translate.c:566

References ast_calloc, ast_channel_name(), ast_channel_tech_pvt(), ast_debug, ast_frame_free(), ast_frdup, ast_free, AST_FRIENDLY_OFFSET, ast_log, AST_MALLOCD_DATA, ast_timer_ack(), ast_timer_get_event(), AST_TIMING_EVENT_EXPIRED, ast_translate(), ast_frame::data, ast_frame::datalen, dequeue_frame(), LOG_WARNING, ast_frame::mallocd, websocket_pvt::native_format, NULL, ast_frame::offset, websocket_pvt::optimal_frame_size, websocket_pvt::passthrough, ast_frame::ptr, ast_frame::samples, set_channel_format(), websocket_pvt::silence, websocket_pvt::slin_format, websocket_pvt::timer, and websocket_pvt::translator.

◆ webchan_request()

static struct ast_channel * webchan_request ( const char *  type,
struct ast_format_cap cap,
const struct ast_assigned_ids assignedids,
const struct ast_channel requestor,
const char *  data,
int *  cause 
)
static

Definition at line 1297 of file chan_websocket.c.

1300{
1301 char *parse;
1302 RAII_VAR(struct websocket_pvt *, instance, NULL, ao2_cleanup);
1303 struct ast_channel *chan = NULL;
1304 struct ast_format *fmt = NULL;
1305 struct ast_format_cap *caps = NULL;
1307 AST_APP_ARG(connection_id);
1309 );
1310 struct ast_flags opts = { 0, };
1311 char *opt_args[OPT_ARG_ARRAY_SIZE];
1312 const char *requestor_name = requestor ? ast_channel_name(requestor) : "no channel";
1313
1314 ast_debug(3, "%s: WebSocket channel requested\n",
1315 requestor_name);
1316
1317 if (ast_strlen_zero(data)) {
1318 ast_log(LOG_ERROR, "%s: A connection id is required for the 'WebSocket' channel\n",
1319 requestor_name);
1320 goto failure;
1321 }
1322 parse = ast_strdupa(data);
1323 AST_NONSTANDARD_APP_ARGS(args, parse, '/');
1324
1325 if (ast_strlen_zero(args.connection_id)) {
1326 ast_log(LOG_ERROR, "%s: connection_id is required for the 'WebSocket' channel\n",
1327 requestor_name);
1328 goto failure;
1329 }
1330
1331 if (!ast_strlen_zero(args.options)
1332 && ast_app_parse_options(websocket_options, &opts, opt_args,
1333 ast_strdupa(args.options))) {
1334 ast_log(LOG_ERROR, "%s: 'WebSocket' channel options '%s' parse error\n",
1335 requestor_name, args.options);
1336 goto failure;
1337 }
1338
1339 if (ast_test_flag(&opts, OPT_WS_CODEC)
1340 && !ast_strlen_zero(opt_args[OPT_ARG_WS_CODEC])) {
1341 ast_debug(3, "%s: Using specified format %s\n",
1342 requestor_name, opt_args[OPT_ARG_WS_CODEC]);
1343 fmt = ast_format_cache_get(opt_args[OPT_ARG_WS_CODEC]);
1344 } else {
1345 /*
1346 * If codec wasn't specified in the dial string,
1347 * use the first format in the capabilities.
1348 */
1349 ast_debug(3, "%s: Using format %s from requesting channel\n",
1350 requestor_name, opt_args[OPT_ARG_WS_CODEC]);
1351 fmt = ast_format_cap_get_format(cap, 0);
1352 }
1353
1354 if (!fmt) {
1355 ast_log(LOG_WARNING, "%s: No codec found for sending media to connection '%s'\n",
1356 requestor_name, args.connection_id);
1357 goto failure;
1358 }
1359
1360 instance = websocket_new(requestor_name, args.connection_id, fmt);
1361 if (!instance) {
1362 ast_log(LOG_ERROR, "%s: Failed to allocate WebSocket channel pvt\n",
1363 requestor_name);
1364 goto failure;
1365 }
1366
1367 instance->no_auto_answer = ast_test_flag(&opts, OPT_WS_NO_AUTO_ANSWER);
1368 if (!instance->passthrough) {
1369 instance->passthrough = ast_test_flag(&opts, OPT_WS_PASSTHROUGH);
1370 }
1371
1373 && !ast_strlen_zero(opt_args[OPT_ARG_WS_URI_PARAM])) {
1374 char *comma;
1375
1376 if (ast_strings_equal(args.connection_id, INCOMING_CONNECTION_ID)) {
1378 "%s: URI parameters are not allowed for 'WebSocket/INCOMING' channels\n",
1379 requestor_name);
1380 goto failure;
1381 }
1382
1383 ast_debug(3, "%s: Using URI parameters '%s'\n",
1384 requestor_name, opt_args[OPT_ARG_WS_URI_PARAM]);
1385
1387 ast_log(LOG_ERROR, "%s: Invalid URI parameters '%s' in WebSocket/%s dial string\n",
1388 requestor_name, opt_args[OPT_ARG_WS_URI_PARAM],
1389 args.connection_id);
1390 goto failure;
1391 }
1392
1393 instance->uri_params = ast_strdup(opt_args[OPT_ARG_WS_URI_PARAM]);
1394 comma = instance->uri_params;
1395 /*
1396 * The normal separator for query string components is an
1397 * ampersand ('&') but the Dial app interprets them as additional
1398 * channels to dial in parallel so we instruct users to separate
1399 * the parameters with commas (',') instead. We now have to
1400 * convert those commas back to ampersands.
1401 */
1402 while ((comma = strchr(comma,','))) {
1403 *comma = '&';
1404 }
1405 ast_debug(3, "%s: Using final URI '%s'\n", requestor_name, instance->uri_params);
1406 }
1407
1408 chan = ast_channel_alloc(1, AST_STATE_DOWN, "", "", "", "", "", assignedids,
1409 requestor, 0, "WebSocket/%s/%p", args.connection_id, instance);
1410 if (!chan) {
1411 ast_log(LOG_ERROR, "%s: Unable to alloc channel\n", ast_channel_name(requestor));
1412 goto failure;
1413 }
1414
1415 ast_debug(3, "%s: WebSocket channel %s allocated for connection %s\n",
1416 ast_channel_name(chan), requestor_name,
1417 instance->connection_id);
1418
1419 instance->channel = ao2_bump(chan);
1420 ast_channel_tech_set(instance->channel, &websocket_tech);
1421
1422 if (set_instance_translator(instance) != 0) {
1423 goto failure;
1424 }
1425
1426 if (set_instance_silence_frame(instance) != 0) {
1427 goto failure;
1428 }
1429
1430 if (set_channel_timer(instance) != 0) {
1431 goto failure;
1432 }
1433
1434 if (set_channel_variables(instance) != 0) {
1435 goto failure;
1436 }
1437
1439 if (!caps) {
1440 ast_log(LOG_ERROR, "%s: Unable to alloc caps\n", requestor_name);
1441 goto failure;
1442 }
1443
1444 ast_format_cap_append(caps, instance->native_format, 0);
1445 ast_channel_nativeformats_set(instance->channel, caps);
1446 ast_channel_set_writeformat(instance->channel, instance->native_format);
1447 ast_channel_set_rawwriteformat(instance->channel, instance->native_format);
1448 ast_channel_set_readformat(instance->channel, instance->native_format);
1449 ast_channel_set_rawreadformat(instance->channel, instance->native_format);
1450 ast_channel_tech_pvt_set(chan, ao2_bump(instance));
1451 ast_channel_unlock(chan);
1452 ao2_cleanup(caps);
1453
1454 ast_debug(3, "%s: WebSocket channel created to %s\n",
1455 ast_channel_name(chan), args.connection_id);
1456
1457 return chan;
1458
1459failure:
1460 if (chan) {
1461 ast_channel_unlock(chan);
1462 }
1463 *cause = AST_CAUSE_FAILURE;
1464 return NULL;
1465}
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
#define AST_CAUSE_FAILURE
Definition causes.h:150
static int set_instance_translator(struct websocket_pvt *instance)
static int validate_uri_parameters(const char *uri_params)
static int set_channel_variables(struct websocket_pvt *instance)
static int set_channel_timer(struct websocket_pvt *instance)
static const struct ast_app_option websocket_options[128]
#define INCOMING_CONNECTION_ID
static struct websocket_pvt * websocket_new(const char *chan_name, const char *connection_id, struct ast_format *fmt)
static int set_instance_silence_frame(struct websocket_pvt *instance)
#define ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, assignedids, requestor, amaflag,...)
Create a channel structure.
Definition channel.h:1299
void ast_channel_nativeformats_set(struct ast_channel *chan, struct ast_format_cap *value)
void ast_channel_set_rawwriteformat(struct ast_channel *chan, struct ast_format *format)
void ast_channel_set_readformat(struct ast_channel *chan, struct ast_format *format)
void ast_channel_tech_set(struct ast_channel *chan, const struct ast_channel_tech *value)
#define ast_channel_unlock(chan)
Definition channel.h:2983
void ast_channel_set_writeformat(struct ast_channel *chan, struct ast_format *format)
@ AST_STATE_DOWN
#define ast_format_cache_get(name)
Retrieve a named format from the cache.
struct ast_format * ast_format_cap_get_format(const struct ast_format_cap *cap, int position)
Get the format at a specific index.
Definition format_cap.c:400
#define ast_format_cap_append(cap, format, framing)
Add format capability to capabilities structure.
Definition format_cap.h:99
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the 'nonstandard' argument separation process for an application.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition main/app.c:3066
static struct @519 args
Main Channel structure associated with a channel.
Structure used to handle boolean flags.
Definition utils.h:220
Format capabilities structure, holds formats + preference order + etc.
Definition format_cap.c:54
Definition of a media format.
Definition format.c:43
static struct test_options options
#define ast_test_flag(p, flag)
Definition utils.h:64

References ao2_bump, ao2_cleanup, args, AST_APP_ARG, ast_app_parse_options(), AST_CAUSE_FAILURE, ast_channel_alloc, ast_channel_name(), ast_channel_nativeformats_set(), ast_channel_set_rawreadformat(), ast_channel_set_rawwriteformat(), ast_channel_set_readformat(), ast_channel_set_writeformat(), ast_channel_tech_pvt_set(), ast_channel_tech_set(), ast_channel_unlock, ast_debug, AST_DECLARE_APP_ARGS, ast_format_cache_get, ast_format_cap_alloc, ast_format_cap_append, AST_FORMAT_CAP_FLAG_DEFAULT, ast_format_cap_get_format(), ast_log, AST_NONSTANDARD_APP_ARGS, AST_STATE_DOWN, ast_strdup, ast_strdupa, ast_strings_equal(), ast_strlen_zero(), ast_test_flag, INCOMING_CONNECTION_ID, LOG_ERROR, LOG_WARNING, NULL, OPT_ARG_ARRAY_SIZE, OPT_ARG_WS_CODEC, OPT_ARG_WS_URI_PARAM, OPT_WS_CODEC, OPT_WS_NO_AUTO_ANSWER, OPT_WS_PASSTHROUGH, OPT_WS_URI_PARAM, options, RAII_VAR, set_channel_timer(), set_channel_variables(), set_instance_silence_frame(), set_instance_translator(), validate_uri_parameters(), websocket_new(), websocket_options, and websocket_tech.

◆ webchan_send_dtmf_text()

static int webchan_send_dtmf_text ( struct ast_channel ast,
char  digit,
unsigned int  duration 
)
static

Definition at line 1503 of file chan_websocket.c.

1504{
1505 struct websocket_pvt *instance = ast_channel_tech_pvt(ast);
1506 char *command;
1507 int res = 0;
1508
1509 if (!instance) {
1510 return -1;
1511 }
1512
1513 res = ast_asprintf(&command, "%s digit:%c channel_id:%s", DTMF_END, digit, ast_channel_uniqueid(instance->channel));
1514 if (res <= 0 || !command) {
1515 ast_log(LOG_ERROR, "%s: Failed to create DTMF_END\n", ast_channel_name(instance->channel));
1516 return 0;
1517 }
1518 res = ast_websocket_write_string(instance->websocket, command);
1519 if (res != 0) {
1520 ast_log(LOG_ERROR, "%s: Failed to send DTMF_END\n", ast_channel_name(instance->channel));
1521 ast_free(command);
1522 return 0;
1523 }
1524 ast_debug(3, "%s: Sent %s\n", ast_channel_name(instance->channel), command);
1525 ast_free(command);
1526 return 0;
1527}
char digit
#define DTMF_END

References ast_asprintf, ast_channel_name(), ast_channel_tech_pvt(), ast_channel_uniqueid(), ast_debug, ast_free, ast_log, ast_websocket_write_string(), websocket_pvt::channel, digit, DTMF_END, LOG_ERROR, and websocket_pvt::websocket.

◆ webchan_write()

static int webchan_write ( struct ast_channel ast,
struct ast_frame f 
)
static

Function called when we should write a frame to the channel.

Definition at line 889 of file chan_websocket.c.

890{
891 struct websocket_pvt *instance = ast_channel_tech_pvt(ast);
892
893 if (!instance || !instance->websocket) {
894 ast_log(LOG_WARNING, "%s: WebSocket instance or client not found\n",
895 ast_channel_name(ast));
896 return -1;
897 }
898
899 if (f->frametype != AST_FRAME_VOICE) {
900 ast_log(LOG_WARNING, "%s: This WebSocket channel only supports AST_FRAME_VOICE frames\n",
901 ast_channel_name(ast));
902 return -1;
903 }
904
906 ast_log(LOG_WARNING, "%s: This WebSocket channel only supports the '%s' format, not '%s'\n",
909 return -1;
910 }
911
913 (char *)f->data.ptr, (uint64_t)f->datalen);
914}
int AST_OPTIONAL_API_NAME() ast_websocket_write(struct ast_websocket *session, enum ast_websocket_opcode opcode, char *payload, uint64_t payload_size)
Construct and transmit a WebSocket frame.

References ast_channel_name(), ast_channel_tech_pvt(), ast_format_cmp(), AST_FORMAT_CMP_NOT_EQUAL, ast_format_get_name(), AST_FRAME_VOICE, ast_log, AST_WEBSOCKET_OPCODE_BINARY, ast_websocket_write(), ast_frame::data, ast_frame::datalen, ast_frame_subclass::format, ast_frame::frametype, LOG_WARNING, websocket_pvt::native_format, ast_frame::ptr, ast_frame::subclass, and websocket_pvt::websocket.

◆ websocket_destructor()

static void websocket_destructor ( void *  data)
static

Definition at line 980 of file chan_websocket.c.

981{
982 struct websocket_pvt *instance = data;
983 struct ast_frame *frame = NULL;
984 ast_debug(3, "%s: WebSocket instance freed\n", instance->connection_id);
985
986 AST_LIST_LOCK(&instance->frame_queue);
987 while ((frame = AST_LIST_REMOVE_HEAD(&instance->frame_queue, frame_list))) {
988 ast_frfree(frame);
989 }
990 AST_LIST_UNLOCK(&instance->frame_queue);
991
992 if (instance->timer) {
993 ast_timer_close(instance->timer);
994 instance->timer = NULL;
995 }
996
997 if (instance->channel) {
998 ast_channel_unref(instance->channel);
999 instance->channel = NULL;
1000 }
1001 if (instance->websocket) {
1002 ast_websocket_unref(instance->websocket);
1003 instance->websocket = NULL;
1004 }
1005
1006 ao2_cleanup(instance->client);
1007 instance->client = NULL;
1008
1009 ao2_cleanup(instance->native_codec);
1010 instance->native_codec = NULL;
1011
1012 ao2_cleanup(instance->native_format);
1013 instance->native_format = NULL;
1014
1015 ao2_cleanup(instance->slin_codec);
1016 instance->slin_codec = NULL;
1017
1018 ao2_cleanup(instance->slin_format);
1019 instance->slin_format = NULL;
1020
1021 if (instance->silence.data.ptr) {
1022 ast_free(instance->silence.data.ptr);
1023 instance->silence.data.ptr = NULL;
1024 }
1025
1026 if (instance->translator) {
1028 instance->translator = NULL;
1029 }
1030
1031 if (instance->leftover_data) {
1032 ast_free(instance->leftover_data);
1033 instance->leftover_data = NULL;
1034 }
1035
1036 ast_free(instance->uri_params);
1037}
#define ast_channel_unref(c)
Decrease channel reference count.
Definition channel.h:3018
void ast_timer_close(struct ast_timer *handle)
Close an opened timing handle.
Definition timing.c:154
void ast_translator_free_path(struct ast_trans_pvt *tr)
Frees a translator path Frees the given translator path structure.
Definition translate.c:476

References ao2_cleanup, ast_channel_unref, ast_debug, ast_free, ast_frfree, AST_LIST_LOCK, AST_LIST_REMOVE_HEAD, AST_LIST_UNLOCK, ast_timer_close(), ast_translator_free_path(), ast_websocket_unref(), websocket_pvt::channel, websocket_pvt::client, websocket_pvt::connection_id, ast_frame::data, websocket_pvt::frame_queue, websocket_pvt::leftover_data, websocket_pvt::native_codec, websocket_pvt::native_format, NULL, ast_frame::ptr, websocket_pvt::silence, websocket_pvt::slin_codec, websocket_pvt::slin_format, websocket_pvt::timer, websocket_pvt::translator, websocket_pvt::uri_params, and websocket_pvt::websocket.

Referenced by websocket_new().

◆ websocket_new()

static struct websocket_pvt * websocket_new ( const char *  chan_name,
const char *  connection_id,
struct ast_format fmt 
)
static

Definition at line 1052 of file chan_websocket.c.

1054{
1055 RAII_VAR(struct instance_proxy *, proxy, NULL, ao2_cleanup);
1056 RAII_VAR(struct websocket_pvt *, instance, NULL, ao2_cleanup);
1057 char uuid[AST_UUID_STR_LEN];
1058 enum ast_websocket_type ws_type;
1059
1060 SCOPED_AO2WRLOCK(locker, instances);
1061
1062 if (ast_strings_equal(connection_id, INCOMING_CONNECTION_ID)) {
1063 connection_id = ast_uuid_generate_str(uuid, sizeof(uuid));
1064 ws_type = AST_WS_TYPE_SERVER;
1065 } else {
1066 ws_type = AST_WS_TYPE_CLIENT;
1067 }
1068
1069 proxy = ao2_weakproxy_alloc(sizeof(*proxy) + strlen(connection_id) + 1, NULL);
1070 if (!proxy) {
1071 return NULL;
1072 }
1073 strcpy(proxy->connection_id, connection_id); /* Safe */
1074
1075 instance = ao2_alloc(sizeof(*instance) + strlen(connection_id) + 1,
1077 if (!instance) {
1078 return NULL;
1079 }
1080 strcpy(instance->connection_id, connection_id); /* Safe */
1081
1082 instance->type = ws_type;
1083 if (ws_type == AST_WS_TYPE_CLIENT) {
1084 instance->client = ast_websocket_client_retrieve_by_id(instance->connection_id);
1085 if (!instance->client) {
1086 ast_log(LOG_ERROR, "%s: WebSocket client connection '%s' not found\n",
1087 chan_name, instance->connection_id);
1088 return NULL;
1089 }
1090 }
1091
1092 AST_LIST_HEAD_INIT(&instance->frame_queue);
1093
1094 /*
1095 * We need the codec to calculate the number of samples in a frame
1096 * so we'll get it once and store it in the instance.
1097 *
1098 * References for native_format and native_codec are now held by the
1099 * instance and will be released when the instance is destroyed.
1100 */
1101 instance->native_format = fmt;
1102 instance->native_codec = ast_format_get_codec(instance->native_format);
1103 /*
1104 * References for native_format and native_codec are now held by the
1105 * instance and will be released when the instance is destroyed.
1106 */
1107
1108 /*
1109 * It's not possible for us to re-time or re-frame media if the data
1110 * stream can't be broken up on arbitrary byte boundaries. This is usually
1111 * indicated by the codec's minimum_bytes being small (10 bytes or less).
1112 * We need to force passthrough mode in this case.
1113 */
1114 if (instance->native_codec->minimum_bytes <= 10) {
1115 instance->passthrough = 1;
1116 instance->optimal_frame_size = 0;
1117 } else {
1118 instance->optimal_frame_size =
1119 (instance->native_codec->default_ms * instance->native_codec->minimum_bytes)
1120 / instance->native_codec->minimum_ms;
1121 instance->leftover_data = ast_calloc(1, instance->optimal_frame_size);
1122 if (!instance->leftover_data) {
1123 return NULL;
1124 }
1125 }
1126
1127 ast_debug(3,
1128 "%s: WebSocket channel native format '%s' Sample rate: %d ptime: %dms minms: %u minbytes: %u passthrough: %d optimal_frame_size: %d\n",
1129 chan_name, ast_format_get_name(instance->native_format),
1130 ast_format_get_sample_rate(instance->native_format),
1131 ast_format_get_default_ms(instance->native_format),
1132 ast_format_get_minimum_ms(instance->native_format),
1133 ast_format_get_minimum_bytes(instance->native_format),
1134 instance->passthrough,
1135 instance->optimal_frame_size);
1136
1137 /* We have exclusive access to proxy and sorcery, no need for locking here. */
1138 if (ao2_weakproxy_set_object(proxy, instance, OBJ_NOLOCK)) {
1139 return NULL;
1140 }
1141
1143 return NULL;
1144 }
1145
1146 if (!ao2_link_flags(instances, proxy, OBJ_NOLOCK)) {
1147 ast_log(LOG_ERROR, "%s: Unable to link WebSocket instance to instances\n",
1148 proxy->connection_id);
1149 return NULL;
1150 }
1151 ast_debug(3, "%s: WebSocket instance created and linked\n", proxy->connection_id);
1152
1153 return ao2_bump(instance);
1154}
#define ao2_weakproxy_set_object(weakproxy, obj, flags)
Associate weakproxy with obj.
Definition astobj2.h:579
int ao2_weakproxy_subscribe(void *weakproxy, ao2_weakproxy_notification_cb cb, void *data, int flags)
Request notification when weakproxy points to NULL.
Definition astobj2.c:934
#define ao2_link_flags(container, obj, flags)
Add an object to a container.
Definition astobj2.h:1554
#define ao2_weakproxy_alloc(data_size, destructor_fn)
Allocate an ao2_weakproxy object.
Definition astobj2.h:550
#define ao2_alloc(data_size, destructor_fn)
Definition astobj2.h:409
static void instance_proxy_cb(void *weakproxy, void *data)
static void websocket_destructor(void *data)
unsigned int ast_format_get_minimum_bytes(const struct ast_format *format)
Get the minimum number of bytes expected in a frame for this format.
Definition format.c:374
unsigned int ast_format_get_minimum_ms(const struct ast_format *format)
Get the minimum amount of media carried in this format.
Definition format.c:364
static int uuid(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition func_uuid.c:52
ast_websocket_type
WebSocket connection/configuration types.
@ AST_WS_TYPE_CLIENT
#define AST_LIST_HEAD_INIT(head)
Initializes a list head structure.
#define SCOPED_AO2WRLOCK(varname, obj)
scoped lock specialization for ao2 write locks.
Definition lock.h:621
#define AST_UUID_STR_LEN
Definition uuid.h:27
char * ast_uuid_generate_str(char *buf, size_t size)
Generate a UUID string.
Definition uuid.c:141
struct ast_websocket_client * ast_websocket_client_retrieve_by_id(const char *id)
Retrieve a websocket client object by ID.

References ao2_alloc, ao2_bump, ao2_cleanup, ao2_link_flags, ao2_weakproxy_alloc, ao2_weakproxy_set_object, ao2_weakproxy_subscribe(), ast_calloc, ast_debug, ast_format_get_codec(), ast_format_get_default_ms(), ast_format_get_minimum_bytes(), ast_format_get_minimum_ms(), ast_format_get_name(), ast_format_get_sample_rate(), AST_LIST_HEAD_INIT, ast_log, ast_strings_equal(), ast_uuid_generate_str(), AST_UUID_STR_LEN, ast_websocket_client_retrieve_by_id(), AST_WS_TYPE_CLIENT, AST_WS_TYPE_SERVER, websocket_pvt::connection_id, INCOMING_CONNECTION_ID, instance_proxy_cb(), instances, LOG_ERROR, NULL, OBJ_NOLOCK, RAII_VAR, SCOPED_AO2WRLOCK, uuid(), and websocket_destructor().

Referenced by webchan_request().

Variable Documentation

◆ __mod_info

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Websocket Media Channel" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .support_level = AST_MODULE_SUPPORT_CORE, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_CHANNEL_DRIVER, .requires = "res_http_websocket,res_websocket_client", }
static

Definition at line 1763 of file chan_websocket.c.

◆ ast_module_info

const struct ast_module_info* ast_module_info = &__mod_info
static

Definition at line 1763 of file chan_websocket.c.

◆ ast_ws_server

struct ast_websocket_server* ast_ws_server
static

Definition at line 122 of file chan_websocket.c.

Referenced by incoming_ws_http_callback(), load_module(), and unload_module().

◆ http_uri

struct ast_http_uri http_uri
static

Definition at line 1680 of file chan_websocket.c.

1680 {
1681 .callback = incoming_ws_http_callback,
1682 .description = "Media over Websocket",
1683 .uri = "media",
1684 .has_subtree = 1,
1685 .data = NULL,
1686 .key = __FILE__,
1687 .no_decode_uri = 1,
1688};
static int incoming_ws_http_callback(struct ast_tcptls_session_instance *ser, const struct ast_http_uri *urih, const char *uri, enum ast_http_method method, struct ast_variable *get_params, struct ast_variable *headers)

Referenced by load_module(), and unload_module().

◆ instances

struct ao2_container* instances = NULL
static

◆ websocket_options

const struct ast_app_option websocket_options[128] = { [ 'c' ] = { .flag = OPT_WS_CODEC , .arg_index = OPT_ARG_WS_CODEC + 1 }, [ 'n' ] = { .flag = OPT_WS_NO_AUTO_ANSWER }, [ 'v' ] = { .flag = OPT_WS_URI_PARAM , .arg_index = OPT_ARG_WS_URI_PARAM + 1 }, [ 'p' ] = { .flag = OPT_WS_PASSTHROUGH }, }
static

Definition at line 1295 of file chan_websocket.c.

Referenced by webchan_request().

◆ websocket_tech

struct ast_channel_tech websocket_tech
static

Definition at line 190 of file chan_websocket.c.

190 {
191 .type = "WebSocket",
192 .description = "Media over WebSocket Channel Driver",
193 .requester = webchan_request,
194 .call = webchan_call,
195 .read = webchan_read,
196 .write = webchan_write,
197 .hangup = webchan_hangup,
198 .send_digit_end = webchan_send_dtmf_text,
199};
static int webchan_write(struct ast_channel *ast, struct ast_frame *f)
Function called when we should write a frame to the channel.
static int webchan_hangup(struct ast_channel *ast)
static struct ast_frame * webchan_read(struct ast_channel *ast)
static int webchan_send_dtmf_text(struct ast_channel *ast, char digit, unsigned int duration)
static int webchan_call(struct ast_channel *ast, const char *dest, int timeout)
static struct ast_channel * webchan_request(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *data, int *cause)

Referenced by load_module(), unload_module(), and webchan_request().