From 62600b2ec47e67e87fc19075c6dcee79c3bf28fc Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Mon, 13 May 2024 15:23:31 +0200 Subject: [PATCH 01/31] =?UTF-8?q?Inline=20mal=20plac=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/asterisk/strings.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/asterisk/strings.h b/include/asterisk/strings.h index 935c7e9236f..55ff22b3c2b 100644 --- a/include/asterisk/strings.h +++ b/include/asterisk/strings.h @@ -94,7 +94,7 @@ static force_inline int attribute_pure ast_strlen_zero(const char *s) \retval 1 if \a str begins with \a prefix \retval 0 otherwise. */ -static int force_inline attribute_pure ast_begins_with(const char *str, const char *prefix) +static force_inline int attribute_pure ast_begins_with(const char *str, const char *prefix) { ast_assert(str != NULL); ast_assert(prefix != NULL); @@ -113,7 +113,7 @@ static int force_inline attribute_pure ast_begins_with(const char *str, const ch \retval 1 if \a str ends with \a suffix \retval 0 otherwise. */ -static int force_inline attribute_pure ast_ends_with(const char *str, const char *suffix) +static force_inline int attribute_pure ast_ends_with(const char *str, const char *suffix) { size_t str_len; size_t suffix_len; From 1b77fdfdc9336917eac6789981cfee3d1347cc76 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Mon, 13 May 2024 17:46:55 +0200 Subject: [PATCH 02/31] =?UTF-8?q?Premiere=20=C3=A9tape=20pour=20un=20modul?= =?UTF-8?q?e=20WebRTC=20Datachannel=20et=20un=20second=20Websocket=20pour?= =?UTF-8?q?=20le=20texte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/asterisk/res_pjsip.h | 26 ++ include/asterisk/res_pjsip_session.h | 7 + res/res_pjsip_webrtc_datachannel.c | 372 +++++++++++++++++++++++++++ res/res_pjsip_websocket_text.c | 335 ++++++++++++++++++++++++ 4 files changed, 740 insertions(+) create mode 100644 res/res_pjsip_webrtc_datachannel.c create mode 100644 res/res_pjsip_websocket_text.c diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 6234098749d..265b8212f5a 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -887,6 +887,28 @@ struct ast_sip_t38_configuration { unsigned int bind_udptl_to_media_address; }; +/*! + * \brief WebRTC Datachannel configuration for SIP endpoints + */ +struct ast_sip_webrtc_datachannel_configuration +{ + /*! Whether WebRTC Datachannel support is enabled or not */ + unsigned int enabled; + /*! Whether to use IPv6 for WebRTC Datachannel or not */ + unsigned int ipv6; +}; + +/*! + * \brief Websocket text configuration for SIP endpoints + */ +struct ast_sip_websocket_text_configuration +{ + /*! Whether websocket text support is enabled or not */ + unsigned int enabled; + /*! Whether to use IPv6 for websocket text or not */ + unsigned int ipv6; +}; + /*! * \brief Media configuration for SIP endpoints */ @@ -905,6 +927,10 @@ struct ast_sip_endpoint_media_configuration { struct ast_sip_direct_media_configuration direct_media; /*! T.38 (FoIP) options */ struct ast_sip_t38_configuration t38; + /*! WebRTC Datachannel configuration options */ + struct ast_sip_webrtc_datachannel_configuration webrtc_datachannel_configuration; + /*! Websocket text configuration options */ + struct ast_sip_websocket_text_configuration websocket_text_configuration; /*! Configured codecs */ struct ast_format_cap *codecs; /*! Capabilities in topology form */ diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h index d287e968654..331ef50ceb4 100644 --- a/include/asterisk/res_pjsip_session.h +++ b/include/asterisk/res_pjsip_session.h @@ -51,6 +51,8 @@ struct pjmedia_sdp_media; struct pjmedia_sdp_session; struct ast_dsp; struct ast_udptl; +struct ast_webrtc_datachannel; +struct ast_websocket_text; /*! \brief T.38 states for a session */ enum ast_sip_session_t38state { @@ -127,6 +129,11 @@ struct ast_sip_session_media { char *remote_label; /*! \brief Stream name */ char *stream_name; + + /*! \brief WebRTC Datachannel instance itself */ + struct ast_webrtc_datachannel *webrtc_datachannel; + /*! \brief Websocket text instance itself */ + struct ast_websocket_text *websocket_text; }; /*! diff --git a/res/res_pjsip_webrtc_datachannel.c b/res/res_pjsip_webrtc_datachannel.c new file mode 100644 index 00000000000..724b843ec6f --- /dev/null +++ b/res/res_pjsip_webrtc_datachannel.c @@ -0,0 +1,372 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2024, IVèS. + * + * Jean-Pierre BROCHET + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \author Jean-Pierre BROCHET + * + * \brief WebRTC Datachannel handling + */ + +/*** MODULEINFO + pjproject + res_pjsip + res_pjsip_session + core + ***/ + +#include "asterisk.h" + +#include +#include +#include +#include + +#include "asterisk/utils.h" +#include "asterisk/module.h" +//#include "asterisk/udptl.h" +#include "asterisk/netsock2.h" +#include "asterisk/channel.h" +#include "asterisk/acl.h" +#include "asterisk/stream.h" +#include "asterisk/format_cache.h" + +#include "asterisk/res_pjsip.h" +#include "asterisk/res_pjsip_session.h" + +static const char STR_APPLICATION[] = "application"; + +/*! \brief Address for webRTC datachannel */ +static struct ast_sockaddr address; + +/*! \brief webrtc datachannel information */ +struct ast_webrtc_datachannel +{ + int fd; +}; + +/*! \brief Supplement for adding framehook to session channel */ +static struct ast_sip_session_supplement webrtc_datachannel_supplement = { + .method = "INVITE", + .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1, +// .incoming_request = t38_incoming_invite_request, +// .outgoing_request = t38_outgoing_invite_request, +}; + +static int ast_webrtc_datachannel_fd(const struct ast_webrtc_datachannel *datachannel) +{ + return datachannel->fd; +} + +/*! \brief Destructor for T.38 state information */ +static void ast_webrtc_datachannel_destroy(void *obj) +{ + ast_free(obj); +} + +/*! \brief Function which negotiates an incoming media stream */ +static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, + struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *sdp, + int index, struct ast_stream *asterisk_stream) +{ + char host[NI_MAXHOST]; + pjmedia_sdp_media *stream = sdp->media[index]; + RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); + + if (!session->endpoint->media.webrtc_datachannel_configuration.enabled) { + ast_debug(3, "Declining; WebRTC Datachannel_configuration not enabled on session\n"); + return 0; + } + + ast_copy_pj_str(host, stream->conn ? &stream->conn->addr : &sdp->conn->addr, sizeof(host)); + + /* Ensure that the address provided is valid */ + if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) { + /* The provided host was actually invalid so we error out this negotiation */ + ast_debug(3, "Declining; provided host is invalid\n"); + return 0; + } + + /* Check the address family to make sure it matches configured */ + if ((ast_sockaddr_is_ipv6(addrs) && !session->endpoint->media.webrtc_datachannel_configuration.ipv6) || + (ast_sockaddr_is_ipv4(addrs) && session->endpoint->media.webrtc_datachannel_configuration.ipv6)) { + /* The address does not match configured */ + ast_debug(3, "Declining, provided host does not match configured address family\n"); + return 0; + } + + return 1; +} + +/*! \brief Function which creates an outgoing stream */ +static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, + struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_session *remote, struct ast_stream *stream) +{ + pj_pool_t *pool = session->inv_session->pool_prov; + static const pj_str_t STR_IN = {"IN", 2}; + static const pj_str_t STR_IP4 = {"IP4", 3}; + static const pj_str_t STR_IP6 = {"IP6", 3}; + struct t38_state *state; + pjmedia_sdp_media *media; + const char *hostip = NULL; + struct ast_sockaddr addr; + char tmp[512]; + pj_str_t stmp; + + + if (!session->endpoint->media.webrtc_datachannel_configuration.enabled) { + ast_debug(3, "Not creating outgoing SDP stream: WebRTC Datachannel not enabled\n"); + return 1; + } else + + /* + if ((session->t38state != T38_LOCAL_REINVITE) && (session->t38state != T38_PEER_REINVITE) && + (session->t38state != T38_ENABLED)) { + ast_debug(3, "Not creating outgoing SDP stream: T.38 not enabled\n"); + return 1; + } else if (!(state = t38_state_get_or_alloc(session))) { + return -1; + } else if (t38_initialize_session(session, session_media)) { + ast_debug(3, "Not creating outgoing SDP stream: Failed to initialize T.38 session\n"); + return -1; + } + + if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) || + !(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) { + return -1; + } + + pj_strdup2(pool, &media->desc.media, ast_codec_media_type2str(session_media->type)); + media->desc.transport = STR_UDPTL; + + if (ast_strlen_zero(session->endpoint->media.address)) { + hostip = ast_sip_get_host_ip_string(session->endpoint->media.t38.ipv6 ? pj_AF_INET6() : pj_AF_INET()); + } else { + hostip = session->endpoint->media.address; + } + + if (ast_strlen_zero(hostip)) { + ast_debug(3, "Not creating outgoing SDP stream: no known host IP\n"); + return -1; + } + + media->conn->net_type = STR_IN; + media->conn->addr_type = session->endpoint->media.t38.ipv6 ? STR_IP6 : STR_IP4; + pj_strdup2(pool, &media->conn->addr, hostip); + ast_udptl_get_us(session_media->udptl, &addr); + media->desc.port = (pj_uint16_t) ast_sockaddr_port(&addr); + media->desc.port_count = 1; + media->desc.fmt[media->desc.fmt_count++] = STR_T38; + + snprintf(tmp, sizeof(tmp), "%u", state->our_parms.version); + media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxVersion", pj_cstr(&stmp, tmp)); + + snprintf(tmp, sizeof(tmp), "%u", t38_get_rate(state->our_parms.rate)); + media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38MaxBitRate", pj_cstr(&stmp, tmp)); + + if (state->our_parms.fill_bit_removal) { + media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxFillBitRemoval", NULL); + } + + if (state->our_parms.transcoding_mmr) { + media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxTranscodingMMR", NULL); + } + + if (state->our_parms.transcoding_jbig) { + media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxTranscodingJBIG", NULL); + } + + switch (state->our_parms.rate_management) { + case AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF: + media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxRateManagement", &STR_TRANSFERREDTCF); + break; + case AST_T38_RATE_MANAGEMENT_LOCAL_TCF: + media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxRateManagement", &STR_LOCALTCF); + break; + } + + snprintf(tmp, sizeof(tmp), "%u", ast_udptl_get_local_max_datagram(session_media->udptl)); + media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxMaxDatagram", pj_cstr(&stmp, tmp)); + + switch (ast_udptl_get_error_correction_scheme(session_media->udptl)) { + case UDPTL_ERROR_CORRECTION_NONE: + break; + case UDPTL_ERROR_CORRECTION_FEC: + media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxUdpEC", &STR_T38UDPFEC); + break; + case UDPTL_ERROR_CORRECTION_REDUNDANCY: + media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxUdpEC", &STR_T38UDPREDUNDANCY); + break; + } + + sdp->media[sdp->media_count++] = media; + */ + return 1; +} + +static struct ast_frame *media_session_webrtc_datachannel_read_callback(struct ast_sip_session *session, struct ast_sip_session_media *session_media) +{ + struct ast_frame *frame = NULL; + + if (!session_media->webrtc_datachannel) { + return &ast_null_frame; + } + + //frame = ast_udptl_read(session_media->udptl); + if (!frame) { + return NULL; + } + + frame->stream_num = session_media->stream_num; + + return frame; +} + +static int media_session_webrtc_datachannel_write_callback(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct ast_frame *frame) +{ + if (!session_media->webrtc_datachannel) { + return 0; + } + + //return ast_udptl_write(session_media->udptl, frame); + return 0; +} + +/*! \brief Function which applies a negotiated stream */ +static int apply_negotiated_sdp_stream(struct ast_sip_session *session, + struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *local, + const struct pjmedia_sdp_session *remote, int index, struct ast_stream *asterisk_stream) +{ + RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); + pjmedia_sdp_media *remote_stream = remote->media[index]; + char host[NI_MAXHOST]; + + if (!session_media->webrtc_datachannel) { + ast_debug(3, "Not applying negotiated SDP stream: no WebRTC Datachannel session\n"); + return 0; + } + + ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host)); + + /* Ensure that the address provided is valid */ + if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) { + /* The provided host was actually invalid so we error out this negotiation */ + ast_debug(3, "Not applying negotiated SDP stream: failed to resolve remote stream host\n"); + return -1; + } + + ast_sip_session_media_set_write_callback(session, session_media, media_session_webrtc_datachannel_write_callback); + ast_sip_session_media_add_read_callback(session, session_media, ast_webrtc_datachannel_fd(session_media->webrtc_datachannel), + media_session_webrtc_datachannel_read_callback); + + return 0; +} + +/*! \brief Function which updates the media stream with external media address, if applicable */ +static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struct pjmedia_sdp_media *stream, struct ast_sip_transport *transport) +{ + RAII_VAR(struct ast_sip_transport_state *, transport_state, ast_sip_get_transport_state(ast_sorcery_object_get_id(transport)), ao2_cleanup); + char host[NI_MAXHOST]; + struct ast_sockaddr our_sdp_addr = {{0, }}; + + /* If the stream has been rejected there will be no connection line */ + if (!stream->conn || !transport_state) { + return; + } + + ast_copy_pj_str(host, &stream->conn->addr, sizeof(host)); + ast_sockaddr_parse(&our_sdp_addr, host, PARSE_PORT_FORBID); + + /* Reversed check here. We don't check the remote endpoint being + * in our local net, but whether our outgoing session IP is + * local. If it is not, we won't do rewriting. No localnet + * configured? Always rewrite. */ + if (ast_sip_transport_is_nonlocal(transport_state, &our_sdp_addr) && transport_state->localnet) { + return; + } + ast_debug(5, "Setting media address to %s\n", ast_sockaddr_stringify_addr_remote(&transport_state->external_media_address)); + pj_strdup2(tdata->pool, &stream->conn->addr, ast_sockaddr_stringify_addr_remote(&transport_state->external_media_address)); +} + +/*! \brief Function which destroys the Webrtc Datachannel instance when session ends */ +static void stream_destroy(struct ast_sip_session_media *session_media) +{ + if (session_media->webrtc_datachannel) { + ast_webrtc_datachannel_destroy(session_media->webrtc_datachannel); + } + session_media->webrtc_datachannel = NULL; +} + +/*! \brief SDP handler for 'application' media stream */ +static struct ast_sip_session_sdp_handler application_sdp_handler = { + .id = STR_APPLICATION, + .negotiate_incoming_sdp_stream = negotiate_incoming_sdp_stream, + .create_outgoing_sdp_stream = create_outgoing_sdp_stream, + .apply_negotiated_sdp_stream = apply_negotiated_sdp_stream, + .change_outgoing_sdp_stream_media_address = change_outgoing_sdp_stream_media_address, + .stream_destroy = stream_destroy, +}; + +/*! \brief Unloads the SIP WebRTC Datachannel module from Asterisk */ +static int unload_module(void) +{ + ast_sip_session_unregister_sdp_handler(&application_sdp_handler, STR_APPLICATION); + ast_sip_session_unregister_supplement(&webrtc_datachannel_supplement); + + return 0; +} + +/*! + * \brief Load the module + * + * Module loading including tests for configuration or dependencies. + * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, + * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails + * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the + * configuration file or other non-critical problem return + * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. + */ +static int load_module(void) +{ + if (ast_check_ipv6()) { + ast_sockaddr_parse(&address, "::", 0); + } else { + ast_sockaddr_parse(&address, "0.0.0.0", 0); + } + + ast_sip_session_register_supplement(&webrtc_datachannel_supplement); + + if (ast_sip_session_register_sdp_handler(&application_sdp_handler, STR_APPLICATION)) { + ast_log(LOG_ERROR, "Unable to register SDP handler for %s stream type\n", STR_APPLICATION); + goto end; + } + + return AST_MODULE_LOAD_SUCCESS; +end: + unload_module(); + + return AST_MODULE_LOAD_DECLINE; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP WebRTC Datachannel Support", + .support_level = AST_MODULE_SUPPORT_CORE, + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_CHANNEL_DRIVER, + .requires = "res_pjsip,res_pjsip_session", + ); diff --git a/res/res_pjsip_websocket_text.c b/res/res_pjsip_websocket_text.c new file mode 100644 index 00000000000..596d05aeaec --- /dev/null +++ b/res/res_pjsip_websocket_text.c @@ -0,0 +1,335 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2024, IVèS. + * + * Jean-Pierre BROCHET + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \author Jean-Pierre BROCHET + * + * \brief Websocket Text handling + */ + +/*** MODULEINFO + pjproject + res_pjsip + res_pjsip_session + core + ***/ + +#include "asterisk.h" + +#include +#include +#include +#include + +#include "asterisk/utils.h" +#include "asterisk/module.h" +#include "asterisk/netsock2.h" +#include "asterisk/channel.h" +#include "asterisk/acl.h" +#include "asterisk/stream.h" +#include "asterisk/format_cache.h" + +#include "asterisk/res_pjsip.h" +#include "asterisk/res_pjsip_session.h" + +static const char STR_TEXT[] = "text"; + +/*! \brief Address for ws_text */ +static struct ast_sockaddr address; + +/*! \brief Websocket Text information */ +struct ast_websocket_text +{ + int fd; +}; + +/*! \brief Supplement for adding framehook to session channel */ +static struct ast_sip_session_supplement websocket_text_supplement = { + .method = "INVITE", + .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1, +// .incoming_request = t38_incoming_invite_request, +// .outgoing_request = t38_outgoing_invite_request, +}; + +static int ast_websocket_text_fd(const struct ast_websocket_text *ws_text) +{ + return ws_text->fd; +} + +/*! \brief Destructor for T.38 state information */ +static void ast_websocket_text_destroy(void *obj) +{ + ast_free(obj); +} + +/*! \brief Function which negotiates an incoming media stream */ +static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, + struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *sdp, + int index, struct ast_stream *asterisk_stream) +{ + char host[NI_MAXHOST]; + pjmedia_sdp_media *stream = sdp->media[index]; + RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); + + if (!session->endpoint->media.websocket_text_configuration.enabled) { + ast_debug(3, "Declining: websocket text configuration not enabled on session\n"); + return 0; + } + + ast_copy_pj_str(host, stream->conn ? &stream->conn->addr : &sdp->conn->addr, sizeof(host)); + + /* Ensure that the address provided is valid */ + if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) { + /* The provided host was actually invalid so we error out this negotiation */ + ast_debug(3, "Declining: provided host is invalid\n"); + return 0; + } + + /* Check the address family to make sure it matches configured */ + if ((ast_sockaddr_is_ipv6(addrs) && !session->endpoint->media.websocket_text_configuration.ipv6) || + (ast_sockaddr_is_ipv4(addrs) && session->endpoint->media.websocket_text_configuration.ipv6)) { + /* The address does not match configured */ + ast_debug(3, "Declining: provided host does not match configured address family\n"); + return 0; + } + + return 1; +} + +/*! \brief Function which creates an outgoing stream */ +static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, + struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_session *remote, struct ast_stream *stream) +{ + pj_pool_t *pool = session->inv_session->pool_prov; + static const pj_str_t STR_IN = {"IN", 2}; + static const pj_str_t STR_IP4 = {"IP4", 3}; + static const pj_str_t STR_IP6 = {"IP6", 3}; + static const pj_str_t STR_TEXT = {"text", 4}; + + pjmedia_sdp_media *media; + const char *hostip = NULL; + struct ast_sockaddr addr; +/* + char tmp[512]; + pj_str_t stmp; +*/ + + if (!session->endpoint->media.websocket_text_configuration.enabled) { + ast_debug(3, "Not creating outgoing SDP stream: websocket text not enabled\n"); + return 1; + } else + + /* + if ((session->t38state != T38_LOCAL_REINVITE) && (session->t38state != T38_PEER_REINVITE) && + (session->t38state != T38_ENABLED)) { + ast_debug(3, "Not creating outgoing SDP stream: T.38 not enabled\n"); + return 1; + } else if (!(state = t38_state_get_or_alloc(session))) { + return -1; + } else if (t38_initialize_session(session, session_media)) { + ast_debug(3, "Not creating outgoing SDP stream: Failed to initialize T.38 session\n"); + return -1; + } + */ + + if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) || + !(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) { + return -1; + } + + pj_strdup2(pool, &media->desc.media, ast_codec_media_type2str(session_media->type)); + media->desc.transport = STR_TEXT; + + if (ast_strlen_zero(session->endpoint->media.address)) { + hostip = ast_sip_get_host_ip_string(session->endpoint->media.websocket_text_configuration.ipv6 ? pj_AF_INET6() : pj_AF_INET()); + } else { + hostip = session->endpoint->media.address; + } + + if (ast_strlen_zero(hostip)) { + ast_debug(3, "Not creating outgoing SDP stream: no known host IP\n"); + return -1; + } + + media->conn->net_type = STR_IN; + media->conn->addr_type = session->endpoint->media.websocket_text_configuration.ipv6 ? STR_IP6 : STR_IP4; + pj_strdup2(pool, &media->conn->addr, hostip); + media->desc.port = (pj_uint16_t) ast_sockaddr_port(&addr); + media->desc.port_count = 1; + media->desc.fmt[media->desc.fmt_count++] = STR_TEXT; + + sdp->media[sdp->media_count++] = media; + + return 1; +} + +static struct ast_frame *media_session_websocket_text_read_callback(struct ast_sip_session *session, struct ast_sip_session_media *session_media) +{ + struct ast_frame *frame = NULL; + + if (!session_media->websocket_text) { + return &ast_null_frame; + } + + //frame = ast_udptl_read(session_media->udptl); + if (!frame) { + return NULL; + } + + frame->stream_num = session_media->stream_num; + + return frame; +} + +static int media_session_websocket_text_write_callback(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct ast_frame *frame) +{ + if (!session_media->websocket_text) { + return 0; + } + + //return ast_udptl_write(session_media->udptl, frame); + return 0; +} + +/*! \brief Function which applies a negotiated stream */ +static int apply_negotiated_sdp_stream(struct ast_sip_session *session, + struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *local, + const struct pjmedia_sdp_session *remote, int index, struct ast_stream *asterisk_stream) +{ + RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); + pjmedia_sdp_media *remote_stream = remote->media[index]; + char host[NI_MAXHOST]; + + if (!session_media->websocket_text) { + ast_debug(3, "Not applying negotiated SDP stream: no Websocket Text session\n"); + return 0; + } + + ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host)); + + /* Ensure that the address provided is valid */ + if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) { + /* The provided host was actually invalid so we error out this negotiation */ + ast_debug(3, "Not applying negotiated SDP stream: failed to resolve remote stream host\n"); + return -1; + } + + ast_sip_session_media_set_write_callback(session, session_media, media_session_websocket_text_write_callback); + ast_sip_session_media_add_read_callback(session + , session_media + , ast_websocket_text_fd(session_media->websocket_text) + , media_session_websocket_text_read_callback + ); + + return 0; +} + +/*! \brief Function which updates the media stream with external media address, if applicable */ +static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struct pjmedia_sdp_media *stream, struct ast_sip_transport *transport) +{ + RAII_VAR(struct ast_sip_transport_state *, transport_state, ast_sip_get_transport_state(ast_sorcery_object_get_id(transport)), ao2_cleanup); + char host[NI_MAXHOST]; + struct ast_sockaddr our_sdp_addr = {{0, }}; + + /* If the stream has been rejected there will be no connection line */ + if (!stream->conn || !transport_state) { + return; + } + + ast_copy_pj_str(host, &stream->conn->addr, sizeof(host)); + ast_sockaddr_parse(&our_sdp_addr, host, PARSE_PORT_FORBID); + + /* Reversed check here. We don't check the remote endpoint being + * in our local net, but whether our outgoing session IP is + * local. If it is not, we won't do rewriting. No localnet + * configured? Always rewrite. */ + if (ast_sip_transport_is_nonlocal(transport_state, &our_sdp_addr) && transport_state->localnet) { + return; + } + ast_debug(5, "Setting media address to %s\n", ast_sockaddr_stringify_addr_remote(&transport_state->external_media_address)); + pj_strdup2(tdata->pool, &stream->conn->addr, ast_sockaddr_stringify_addr_remote(&transport_state->external_media_address)); +} + +/*! \brief Function which destroys the Websocket Text instance when session ends */ +static void stream_destroy(struct ast_sip_session_media *session_media) +{ + if (session_media->websocket_text) { + ast_websocket_text_destroy(session_media->websocket_text); + } + session_media->websocket_text = NULL; +} + +/*! \brief SDP handler for 'application' media stream */ +static struct ast_sip_session_sdp_handler text_sdp_handler = { + .id = STR_TEXT, + .negotiate_incoming_sdp_stream = negotiate_incoming_sdp_stream, + .create_outgoing_sdp_stream = create_outgoing_sdp_stream, + .apply_negotiated_sdp_stream = apply_negotiated_sdp_stream, + .change_outgoing_sdp_stream_media_address = change_outgoing_sdp_stream_media_address, + .stream_destroy = stream_destroy, +}; + +/*! \brief Unloads the SIP Websocket Text module from Asterisk */ +static int unload_module(void) +{ + ast_sip_session_unregister_sdp_handler(&text_sdp_handler, STR_TEXT); + ast_sip_session_unregister_supplement(&websocket_text_supplement); + + return 0; +} + +/*! + * \brief Load the module + * + * Module loading including tests for configuration or dependencies. + * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, + * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails + * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the + * configuration file or other non-critical problem return + * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. + */ +static int load_module(void) +{ + if (ast_check_ipv6()) { + ast_sockaddr_parse(&address, "::", 0); + } else { + ast_sockaddr_parse(&address, "0.0.0.0", 0); + } + + ast_sip_session_register_supplement(&websocket_text_supplement); + + if (ast_sip_session_register_sdp_handler(&text_sdp_handler, STR_TEXT)) { + ast_log(LOG_ERROR, "Unable to register SDP handler for %s stream type\n", STR_TEXT); + goto end; + } + + return AST_MODULE_LOAD_SUCCESS; +end: + unload_module(); + + return AST_MODULE_LOAD_DECLINE; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Websocket Text Support", + .support_level = AST_MODULE_SUPPORT_CORE, + .load = load_module, + .unload = unload_module, + .load_pri = AST_MODPRI_CHANNEL_DRIVER, + .requires = "res_pjsip,res_pjsip_session", + ); From c148f4eaae362e12cfd7fb8393e59d5b98b72a0c Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Mon, 13 May 2024 17:48:03 +0200 Subject: [PATCH 03/31] =?UTF-8?q?Pour=20eviter=20d'avoir=20des=20warnings?= =?UTF-8?q?=20de=20compile=20qd=20le=20flag=20DONTOPTIMIZE=20est=20positio?= =?UTF-8?q?nn=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configure | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure b/configure index 9bab0e6a73d..af5b9b79378 100755 --- a/configure +++ b/configure @@ -714,7 +714,7 @@ AST_NO_STRINGOP_TRUNCATION AST_NO_FORMAT_Y2K AST_NO_FORMAT_TRUNCATION AST_NO_STRICT_OVERFLOW -AST_FORTIFY_SOURCE +# AST_FORTIFY_SOURCE AST_TRAMPOLINES AST_DECLARATION_AFTER_STATEMENT AST_UNDEFINED_SANITIZER From 142a261a2b30132c7b2823a0f6c90e7d675fc5c4 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Tue, 21 May 2024 15:16:09 +0200 Subject: [PATCH 04/31] Module qui rajoute la gestion d'un websocket pour le texte provenant du webrtc/ives --- res/res_my_http_websocket.c | 155 ++++++++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 res/res_my_http_websocket.c diff --git a/res/res_my_http_websocket.c b/res/res_my_http_websocket.c new file mode 100644 index 00000000000..e324c3f207a --- /dev/null +++ b/res/res_my_http_websocket.c @@ -0,0 +1,155 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2024, IVèS. + * + * Jean-Pierre BROCHET + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! \file + * + * \author Jean-Pierre BROCHET + * + * \brief Websocket Text handling + */ + +/*** MODULEINFO + core + res_http_websocket + ***/ + +#include "asterisk.h" + +#include "asterisk/module.h" +#include "asterisk/http.h" +#include "asterisk/astobj2.h" +#include "asterisk/strings.h" +#include "asterisk/file.h" +#include "asterisk/unaligned.h" +#include "asterisk/uri.h" +#include "asterisk/uuid.h" +#include "asterisk/lock.h" + +#define AST_API_MODULE +#include "asterisk/http_websocket.h" + +struct websocket_client +{ + struct ast_websocket *websocket; + AST_LIST_ENTRY(websocket_client) entry; +}; + +static AST_LIST_HEAD(clients, websocket_client) clients; + +static struct ast_http_uri websocketuri = { + .callback = AST_OPTIONAL_API_NAME(ast_websocket_uri_cb), + .description = "Asterisk my HTTP WebSocket", + .uri = "ws_text", + .has_subtree = 0, + .data = NULL, + .key = __FILE__, +}; + +/*! \brief Simple echo implementation which echoes received text and binary frames */ +static void websocket_echo_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers) +{ + int res; + struct websocket_client *client; + + ast_debug(1, "Entering WebSocket echo2 loop %s\n", ast_websocket_session_id(session)); + + client = ast_calloc(1, sizeof(*client)); + if (!client) { + ast_log(LOG_ERROR, "Failed to allocate memory for WebSocket client\n"); + ast_websocket_unref(session); + return; + } + + ast_websocket_ref(session); + client->websocket = session; + + AST_LIST_LOCK(&clients); + AST_LIST_INSERT_HEAD(&clients, client, entry); + AST_LIST_UNLOCK(&clients); + + if (ast_fd_set_flags(ast_websocket_fd(session), O_NONBLOCK)) { + goto end; + } + + while ((res = ast_websocket_wait_for_input(session, -1)) > 0) { + char *payload; + uint64_t payload_len; + enum ast_websocket_opcode opcode; + int fragmented; + + if (ast_websocket_read(session, &payload, &payload_len, &opcode, &fragmented)) { + /* We err on the side of caution and terminate the session if any error occurs */ + ast_log(LOG_WARNING, "Read failure during WebSocket echo2 loop\n"); + break; + } + + if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) { + ast_websocket_write(session, opcode, payload, payload_len); + } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) { + break; + } else { + ast_debug(1, "Ignored WebSocket opcode %u\n", opcode); + } + } + +end: + ast_debug(1, "Exiting WebSocket echo2 loop %s\n", ast_websocket_session_id(session)); + ast_websocket_unref(session); +} + + +static int load_module(void) +{ + websocketuri.data = ast_websocket_server_create(); + if (!websocketuri.data) { + return AST_MODULE_LOAD_DECLINE; + } + ast_http_uri_link(&websocketuri); + ast_websocket_server_add_protocol(websocketuri.data, "echo2", websocket_echo_callback); + + return 0; +} + +static int unload_module(void) +{ + struct websocket_client *client; + + ast_websocket_server_remove_protocol(websocketuri.data, "echo2", websocket_echo_callback); + ast_http_uri_unlink(&websocketuri); + ao2_ref(websocketuri.data, -1); + websocketuri.data = NULL; + + AST_LIST_LOCK(&clients); + AST_LIST_TRAVERSE_SAFE_BEGIN(&clients, client, entry) + { + AST_LIST_REMOVE_CURRENT(entry); + ast_websocket_unref(client->websocket); + ast_free(client); + } + AST_LIST_TRAVERSE_SAFE_END; + AST_LIST_UNLOCK(&clients); + + return 0; +} + +AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "my HTTP WebSocket Support", + .support_level = AST_MODULE_SUPPORT_CORE, + .load = load_module, + .unload = unload_module, + .requires = "res_http_websocket", +); From ad4a815eaa0b8cd17d612d316406f470973eac85 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Mon, 27 May 2024 15:52:58 +0200 Subject: [PATCH 05/31] =?UTF-8?q?Avoir=20acc=C3=A8s=20=C3=A0=20la=20callba?= =?UTF-8?q?ck=20pour=20v=C3=A9rifier=20que=20l'url=20websocket=20est=20per?= =?UTF-8?q?mise?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- res/res_my_http_websocket.c | 105 +++++++++++++++++++++++++----------- 1 file changed, 75 insertions(+), 30 deletions(-) diff --git a/res/res_my_http_websocket.c b/res/res_my_http_websocket.c index e324c3f207a..4b18150f181 100644 --- a/res/res_my_http_websocket.c +++ b/res/res_my_http_websocket.c @@ -39,67 +39,112 @@ #include "asterisk/uri.h" #include "asterisk/uuid.h" #include "asterisk/lock.h" +#include "asterisk/channel.h" +#include "asterisk/stream.h" +#include "asterisk/frame.h" -#define AST_API_MODULE + +//#define AST_API_MODULE #include "asterisk/http_websocket.h" -struct websocket_client +struct mywebsocket_client { struct ast_websocket *websocket; - AST_LIST_ENTRY(websocket_client) entry; + AST_LIST_ENTRY(mywebsocket_client) entry; }; -static AST_LIST_HEAD(clients, websocket_client) clients; +static AST_LIST_HEAD(clients, mywebsocket_client) clients; + +static int mywebsocket_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_params + , struct ast_variable *headers + ) +{ + ast_debug(1, "Entering WebSocket echo2 loop method %s uri %s\n", ast_get_http_method(method), uri); + + if (strcmp(uri, "channel_demo")) { + ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL."); + return 0; + } -static struct ast_http_uri websocketuri = { - .callback = AST_OPTIONAL_API_NAME(ast_websocket_uri_cb), + return ast_websocket_uri_cb(ser, urih, uri, method, get_params, headers); +} + +static struct ast_http_uri mywebsocketuri = { + .callback = mywebsocket_uri_cb, .description = "Asterisk my HTTP WebSocket", .uri = "ws_text", - .has_subtree = 0, + .has_subtree = 1, .data = NULL, .key = __FILE__, }; /*! \brief Simple echo implementation which echoes received text and binary frames */ -static void websocket_echo_callback(struct ast_websocket *session, struct ast_variable *parameters, struct ast_variable *headers) +static void mywebsocket_echo_callback(struct ast_websocket *ws_session, struct ast_variable *parameters, struct ast_variable *headers) { int res; - struct websocket_client *client; - - ast_debug(1, "Entering WebSocket echo2 loop %s\n", ast_websocket_session_id(session)); + struct mywebsocket_client *client; + + ast_debug(1, "Entering WebSocket echo2 loop %s, addr remote %s and local %s\n" + , ast_websocket_session_id(ws_session) + , ast_sockaddr_stringify(ast_websocket_remote_address(ws_session)) + , ast_sockaddr_stringify(ast_websocket_local_address(ws_session)) + ); + /* + struct ast_variable *i; + for (i = parameters; i; i = i->next) { + ast_debug(1, "Entering WebSocket echo2 loop %s parameters %s = %s\n", ast_websocket_session_id(ws_session), i->name, i->value); + } + for (i = headers; i; i = i->next) { + ast_debug(1, "Entering WebSocket echo2 loop %s headers %s = %s\n", ast_websocket_session_id(ws_session), i->name, i->value); + } + */ client = ast_calloc(1, sizeof(*client)); if (!client) { ast_log(LOG_ERROR, "Failed to allocate memory for WebSocket client\n"); - ast_websocket_unref(session); + //ast_websocket_unref(ws_session); return; } - ast_websocket_ref(session); - client->websocket = session; + ast_websocket_ref(ws_session); + client->websocket = ws_session; AST_LIST_LOCK(&clients); AST_LIST_INSERT_HEAD(&clients, client, entry); AST_LIST_UNLOCK(&clients); - if (ast_fd_set_flags(ast_websocket_fd(session), O_NONBLOCK)) { + if (ast_fd_set_flags(ast_websocket_fd(ws_session), O_NONBLOCK)) { goto end; } - while ((res = ast_websocket_wait_for_input(session, -1)) > 0) { + while ((res = ast_websocket_wait_for_input(ws_session, -1)) > 0) { char *payload; uint64_t payload_len; enum ast_websocket_opcode opcode; int fragmented; - if (ast_websocket_read(session, &payload, &payload_len, &opcode, &fragmented)) { - /* We err on the side of caution and terminate the session if any error occurs */ + if (ast_websocket_read(ws_session, &payload, &payload_len, &opcode, &fragmented)) { + /* We err on the side of caution and terminate the ws_session if any error occurs */ ast_log(LOG_WARNING, "Read failure during WebSocket echo2 loop\n"); break; } + // On empile une ast_frame avec le texte recu dans une liste + /* + struct ast_frame f = { + .frametype = AST_FRAME_TEXT, + .subclass.integer = 0, + .len = payload_len, + .data.ptr = (void *)payload // +1 ? Allocation ? + }; + */ + if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) { - ast_websocket_write(session, opcode, payload, payload_len); + ast_websocket_write(ws_session, opcode, payload, payload_len); } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) { break; } else { @@ -108,31 +153,31 @@ static void websocket_echo_callback(struct ast_websocket *session, struct ast_va } end: - ast_debug(1, "Exiting WebSocket echo2 loop %s\n", ast_websocket_session_id(session)); - ast_websocket_unref(session); + ast_debug(1, "Exiting WebSocket echo2 loop %s\n", ast_websocket_session_id(ws_session)); + ast_websocket_unref(ws_session); } static int load_module(void) { - websocketuri.data = ast_websocket_server_create(); - if (!websocketuri.data) { + mywebsocketuri.data = ast_websocket_server_create(); + if (!mywebsocketuri.data) { return AST_MODULE_LOAD_DECLINE; } - ast_http_uri_link(&websocketuri); - ast_websocket_server_add_protocol(websocketuri.data, "echo2", websocket_echo_callback); + ast_http_uri_link(&mywebsocketuri); + ast_websocket_server_add_protocol(mywebsocketuri.data, "echo2", mywebsocket_echo_callback); return 0; } static int unload_module(void) { - struct websocket_client *client; + struct mywebsocket_client *client; - ast_websocket_server_remove_protocol(websocketuri.data, "echo2", websocket_echo_callback); - ast_http_uri_unlink(&websocketuri); - ao2_ref(websocketuri.data, -1); - websocketuri.data = NULL; + ast_websocket_server_remove_protocol(mywebsocketuri.data, "echo2", mywebsocket_echo_callback); + ast_http_uri_unlink(&mywebsocketuri); + ao2_ref(mywebsocketuri.data, -1); + mywebsocketuri.data = NULL; AST_LIST_LOCK(&clients); AST_LIST_TRAVERSE_SAFE_BEGIN(&clients, client, entry) From 4fc9aba5fb6df072d7c1a387c37860b556fc6192 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Tue, 28 May 2024 15:57:04 +0200 Subject: [PATCH 06/31] =?UTF-8?q?Ajout=20d'une=20pile=20de=20frame=20de=20?= =?UTF-8?q?type=20TEXT=20pour=20les=20=C3=A9changes=20texte?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- res/res_my_http_websocket.c | 254 ++++++++++++++++++++++++++++-------- 1 file changed, 202 insertions(+), 52 deletions(-) diff --git a/res/res_my_http_websocket.c b/res/res_my_http_websocket.c index 4b18150f181..1b6523a1a38 100644 --- a/res/res_my_http_websocket.c +++ b/res/res_my_http_websocket.c @@ -42,18 +42,87 @@ #include "asterisk/channel.h" #include "asterisk/stream.h" #include "asterisk/frame.h" - - -//#define AST_API_MODULE #include "asterisk/http_websocket.h" -struct mywebsocket_client +struct websocket_frame +{ + struct ast_frame *frame; + AST_LIST_ENTRY(websocket_frame) entry; +}; + +struct websocket_session { struct ast_websocket *websocket; - AST_LIST_ENTRY(mywebsocket_client) entry; + char channel_name[256]; + AST_LIST_HEAD(frame_stack, websocket_frame) frame_stack; + AST_LIST_ENTRY(websocket_session) entry; }; -static AST_LIST_HEAD(clients, mywebsocket_client) clients; +static AST_LIST_HEAD(websocket_session_list, websocket_session) websocket_session_list; + +static int push_frame(struct websocket_session *ws_session, struct ast_frame *frame) +{ + struct websocket_frame *new_frame; + + new_frame = ast_calloc(1, sizeof(*new_frame)); + if (!new_frame) { + ast_log(LOG_ERROR, "Failed to allocate memory for new frame\n"); + return -1; + } + + new_frame->frame = ast_frdup(frame); + + AST_LIST_LOCK(&ws_session->frame_stack); + AST_LIST_INSERT_HEAD(&ws_session->frame_stack, new_frame, entry); + AST_LIST_UNLOCK(&ws_session->frame_stack); + + ast_log(LOG_DEBUG, "Frame pushed to stack for websocket session with channel name: %s\n", ws_session->channel_name); + return 0; +} + +static struct ast_frame *pop_frame(struct websocket_session *ws_session) +{ + struct websocket_frame *frame_wrapper; + struct ast_frame *frame = NULL; + + AST_LIST_LOCK(&ws_session->frame_stack); + frame_wrapper = AST_LIST_REMOVE_HEAD(&ws_session->frame_stack, entry); + AST_LIST_UNLOCK(&ws_session->frame_stack); + + if (frame_wrapper) { + frame = frame_wrapper->frame; + ast_free(frame_wrapper); + ast_log(LOG_DEBUG, "Frame popped from stack for websocket session with channel name: %s\n", ws_session->channel_name); + } else { + ast_log(LOG_DEBUG, "No frame in stack for websocket session with with channel name: %s\n", ws_session->channel_name); + } + + return frame; +} + +/* Function to add a variable to a list */ +static void add_variable_to_list(struct ast_variable **head, const char *name, const char *value) +{ + struct ast_variable *new_var, *last; + + new_var = ast_variable_new(name, value, ""); + if (!new_var) { + ast_log(LOG_ERROR, "Failed to create new variable\n"); + return; + } + + if (*head == NULL) { + *head = new_var; + } else { + last = *head; + while (last->next) { + last = last->next; + } + last->next = new_var; + } + + ast_log(LOG_NOTICE, "Variable added: %s=%s\n", name, value); +} static int mywebsocket_uri_cb(struct ast_tcptls_session_instance *ser , const struct ast_http_uri *urih @@ -64,11 +133,25 @@ static int mywebsocket_uri_cb(struct ast_tcptls_session_instance *ser ) { ast_debug(1, "Entering WebSocket echo2 loop method %s uri %s\n", ast_get_http_method(method), uri); +/**/ + struct websocket_session *ws_session = NULL; + + // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) + { + if (!strcmp(ws_session->channel_name, uri)) { + break; + } + } + AST_LIST_UNLOCK(&websocket_session_list); - if (strcmp(uri, "channel_demo")) { + if (!ws_session) { ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL."); return 0; } +/**/ + add_variable_to_list(&get_params, "uri", uri); return ast_websocket_uri_cb(ser, urih, uri, method, get_params, headers); } @@ -83,68 +166,111 @@ static struct ast_http_uri mywebsocketuri = { }; /*! \brief Simple echo implementation which echoes received text and binary frames */ -static void mywebsocket_echo_callback(struct ast_websocket *ws_session, struct ast_variable *parameters, struct ast_variable *headers) +static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct ast_variable *parameters, struct ast_variable *headers) { int res; - struct mywebsocket_client *client; + struct websocket_session *ws_session = NULL; ast_debug(1, "Entering WebSocket echo2 loop %s, addr remote %s and local %s\n" - , ast_websocket_session_id(ws_session) - , ast_sockaddr_stringify(ast_websocket_remote_address(ws_session)) - , ast_sockaddr_stringify(ast_websocket_local_address(ws_session)) + , ast_websocket_session_id(websocket) + , ast_sockaddr_stringify(ast_websocket_remote_address(websocket)) + , ast_sockaddr_stringify(ast_websocket_local_address(websocket)) ); - /* + struct ast_variable *i; for (i = parameters; i; i = i->next) { - ast_debug(1, "Entering WebSocket echo2 loop %s parameters %s = %s\n", ast_websocket_session_id(ws_session), i->name, i->value); + ast_debug(1, "Entering WebSocket echo2 loop %s parameters %s = %s\n", ast_websocket_session_id(websocket), i->name, i->value); + if (!strcmp(i->name, "uri")) { + // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) + { + if (!strcmp(ws_session->channel_name, i->value)) { + break; + } + } + AST_LIST_UNLOCK(&websocket_session_list); + } } for (i = headers; i; i = i->next) { - ast_debug(1, "Entering WebSocket echo2 loop %s headers %s = %s\n", ast_websocket_session_id(ws_session), i->name, i->value); + ast_debug(1, "Entering WebSocket echo2 loop %s headers %s = %s\n", ast_websocket_session_id(websocket), i->name, i->value); } - */ - client = ast_calloc(1, sizeof(*client)); - if (!client) { - ast_log(LOG_ERROR, "Failed to allocate memory for WebSocket client\n"); - //ast_websocket_unref(ws_session); - return; + //ws_session = ast_calloc(1, sizeof(*ws_session)); + if (!ws_session) { + ast_log(LOG_ERROR, "Failed to find uri or allocate memory for WebSocket client\n"); + //ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL."); + goto end; } - ast_websocket_ref(ws_session); - client->websocket = ws_session; - - AST_LIST_LOCK(&clients); - AST_LIST_INSERT_HEAD(&clients, client, entry); - AST_LIST_UNLOCK(&clients); - - if (ast_fd_set_flags(ast_websocket_fd(ws_session), O_NONBLOCK)) { + ast_websocket_ref(websocket); + ws_session->websocket = websocket; +/* + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); + AST_LIST_UNLOCK(&websocket_session_list); +*/ + if (ast_fd_set_flags(ast_websocket_fd(websocket), O_NONBLOCK)) { goto end; } - while ((res = ast_websocket_wait_for_input(ws_session, -1)) > 0) { + while ((res = ast_websocket_wait_for_input(websocket, -1)) > 0) { char *payload; uint64_t payload_len; enum ast_websocket_opcode opcode; int fragmented; - if (ast_websocket_read(ws_session, &payload, &payload_len, &opcode, &fragmented)) { - /* We err on the side of caution and terminate the ws_session if any error occurs */ + if (ast_websocket_read(websocket, &payload, &payload_len, &opcode, &fragmented)) { + // We err on the side of caution and terminate the ws_session if any error occurs. ast_log(LOG_WARNING, "Read failure during WebSocket echo2 loop\n"); break; } +/**/ + if (opcode == AST_WEBSOCKET_OPCODE_TEXT && payload_len > 0) { + // On empile une ast_frame avec le texte recu dans une liste + struct ast_frame f_in; + memset(&f_in, 0, sizeof(f_in)); + f_in.frametype = AST_FRAME_TEXT; + f_in.datalen = payload_len + 1; + f_in.data.ptr = (void *)payload; + f_in.subclass.integer = 0; + f_in.offset = 0; + + ast_log(LOG_DEBUG, "Frame %d %*.s pre-push to stack for session with channel name: %s\n", (int)payload_len, (int)payload_len, (const char *)payload, ws_session->channel_name); - // On empile une ast_frame avec le texte recu dans une liste - /* - struct ast_frame f = { - .frametype = AST_FRAME_TEXT, - .subclass.integer = 0, - .len = payload_len, - .data.ptr = (void *)payload // +1 ? Allocation ? - }; - */ - - if (opcode == AST_WEBSOCKET_OPCODE_TEXT || opcode == AST_WEBSOCKET_OPCODE_BINARY) { - ast_websocket_write(ws_session, opcode, payload, payload_len); + push_frame(ws_session, &f_in); + } +/**/ + if (opcode == AST_WEBSOCKET_OPCODE_TEXT) { + + //ast_websocket_write(websocket, AST_WEBSOCKET_OPCODE_TEXT, payload, payload_len); +/**/ + struct ast_frame *f_out = pop_frame(ws_session); + if (f_out && f_out->frametype == AST_FRAME_TEXT) { + payload = f_out->data.ptr; + /* + if (payload[f_out->datalen - 1]) { + // Not zero terminated, we need to allocate. + payload = ast_strndup(payload, f_out->datalen); + if (!payload) { + ast_log(LOG_ERROR, "Failed to allocate memory for WebSocket client frame\n"); + break; + } + } + */ + + payload_len = f_out->datalen-1; + opcode = AST_WEBSOCKET_OPCODE_TEXT; + + ast_websocket_write(websocket, AST_WEBSOCKET_OPCODE_TEXT, payload, payload_len); + + if (payload != f_out->data.ptr) { + // Only free if we allocated. + ast_free(payload); + } + ast_frfree(f_out); + } +/**/ } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) { break; } else { @@ -153,8 +279,8 @@ static void mywebsocket_echo_callback(struct ast_websocket *ws_session, struct a } end: - ast_debug(1, "Exiting WebSocket echo2 loop %s\n", ast_websocket_session_id(ws_session)); - ast_websocket_unref(ws_session); + ast_debug(1, "Exiting WebSocket echo2 loop %s\n", ast_websocket_session_id(websocket)); + ast_websocket_unref(websocket); } @@ -167,27 +293,51 @@ static int load_module(void) ast_http_uri_link(&mywebsocketuri); ast_websocket_server_add_protocol(mywebsocketuri.data, "echo2", mywebsocket_echo_callback); + struct websocket_session *ws_session = NULL; + + ws_session = ast_calloc(1, sizeof(*ws_session)); + if (!ws_session) { + ast_log(LOG_ERROR, "Failed to allocate memory for WebSocket client\n"); + return -1; + } + + strcpy(ws_session->channel_name, "channel_demo"); + + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); + AST_LIST_UNLOCK(&websocket_session_list); + return 0; } static int unload_module(void) { - struct mywebsocket_client *client; + struct websocket_session *ws_session = NULL; ast_websocket_server_remove_protocol(mywebsocketuri.data, "echo2", mywebsocket_echo_callback); ast_http_uri_unlink(&mywebsocketuri); ao2_ref(mywebsocketuri.data, -1); mywebsocketuri.data = NULL; - AST_LIST_LOCK(&clients); - AST_LIST_TRAVERSE_SAFE_BEGIN(&clients, client, entry) + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_list, ws_session, entry) { + struct websocket_frame *frame_wrapper = NULL; + AST_LIST_REMOVE_CURRENT(entry); - ast_websocket_unref(client->websocket); - ast_free(client); + ast_websocket_unref(ws_session->websocket); + + AST_LIST_TRAVERSE_SAFE_BEGIN(&ws_session->frame_stack, frame_wrapper, entry) + { + ast_frfree(frame_wrapper->frame); + ast_free(frame_wrapper); + } + AST_LIST_TRAVERSE_SAFE_END; + + ast_free(ws_session); } AST_LIST_TRAVERSE_SAFE_END; - AST_LIST_UNLOCK(&clients); + AST_LIST_UNLOCK(&websocket_session_list); return 0; } From f5261141b94276f08afc1d6c984314e8de3a450e Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Wed, 12 Jun 2024 09:33:41 +0200 Subject: [PATCH 07/31] Pour completer la description du SDP --- res/res_my_http_websocket.c | 15 +- res/res_pjsip_websocket_text.c | 616 +++++++++++++++++++++++++++------ 2 files changed, 519 insertions(+), 112 deletions(-) diff --git a/res/res_my_http_websocket.c b/res/res_my_http_websocket.c index 1b6523a1a38..1682c117e74 100644 --- a/res/res_my_http_websocket.c +++ b/res/res_my_http_websocket.c @@ -322,17 +322,26 @@ static int unload_module(void) AST_LIST_LOCK(&websocket_session_list); AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_list, ws_session, entry) { - struct websocket_frame *frame_wrapper = NULL; + //struct websocket_frame *frame_wrapper = NULL; + struct ast_frame *frame = NULL; AST_LIST_REMOVE_CURRENT(entry); - ast_websocket_unref(ws_session->websocket); - + if (ws_session->websocket) { + ast_websocket_unref(ws_session->websocket); + } + /* AST_LIST_TRAVERSE_SAFE_BEGIN(&ws_session->frame_stack, frame_wrapper, entry) { ast_frfree(frame_wrapper->frame); ast_free(frame_wrapper); } AST_LIST_TRAVERSE_SAFE_END; + */ + frame = pop_frame(ws_session); + while (frame != NULL) { + ast_frfree(frame); + frame = pop_frame(ws_session); + } ast_free(ws_session); } diff --git a/res/res_pjsip_websocket_text.c b/res/res_pjsip_websocket_text.c index 596d05aeaec..2d425df8290 100644 --- a/res/res_pjsip_websocket_text.c +++ b/res/res_pjsip_websocket_text.c @@ -44,14 +44,16 @@ #include "asterisk/acl.h" #include "asterisk/stream.h" #include "asterisk/format_cache.h" +#include "asterisk/http_websocket.h" #include "asterisk/res_pjsip.h" #include "asterisk/res_pjsip_session.h" +#include "asterisk/res_pjsip_session_caps.h" static const char STR_TEXT[] = "text"; /*! \brief Address for ws_text */ -static struct ast_sockaddr address; +static struct ast_sockaddr address_ws_text; /*! \brief Websocket Text information */ struct ast_websocket_text @@ -59,12 +61,82 @@ struct ast_websocket_text int fd; }; -/*! \brief Supplement for adding framehook to session channel */ +struct websocket_frame +{ + struct ast_frame *frame; + AST_LIST_ENTRY(websocket_frame) entry; +}; + +struct websocket_session +{ + struct ast_websocket *websocket; + char channel_name[256]; + AST_LIST_HEAD(frame_stack, websocket_frame) frame_stack; + AST_LIST_ENTRY(websocket_session) entry; +}; + +static AST_LIST_HEAD(websocket_session_list, websocket_session) websocket_session_list; + + +static void replace_newline(char *buffer, char replacement) +{ + // Parcours du buffer jusqu'à la fin de la chaîne + for (int i = 0; i < strlen(buffer); i++) { + // Si le caractère courant est un retour à la ligne + if (buffer[i] == '\r' || buffer[i] == '\n') { + // Remplacement par le caractère spécifié + buffer[i] = replacement; + } + } +} + + +static int push_frame(struct websocket_session *session, struct ast_frame *frame) +{ + struct websocket_frame *new_frame; + + new_frame = ast_calloc(1, sizeof(*new_frame)); + if (!new_frame) { + ast_log(LOG_ERROR, "Failed to allocate memory for new frame\n"); + return -1; + } + + new_frame->frame = ast_frdup(frame); + + AST_LIST_LOCK(&session->frame_stack); + AST_LIST_INSERT_HEAD(&session->frame_stack, new_frame, entry); + AST_LIST_UNLOCK(&session->frame_stack); + + ast_log(LOG_DEBUG, "Frame pushed to stack for session with channel name: %s\n", session->channel_name); + return 0; +} + +static struct ast_frame *pop_frame(struct websocket_session *session) +{ + struct websocket_frame *frame_wrapper; + struct ast_frame *frame = NULL; + + AST_LIST_LOCK(&session->frame_stack); + frame_wrapper = AST_LIST_REMOVE_HEAD(&session->frame_stack, entry); + AST_LIST_UNLOCK(&session->frame_stack); + + if (frame_wrapper) { + frame = frame_wrapper->frame; + ast_free(frame_wrapper); + ast_log(LOG_DEBUG, "Frame popped from stack for session with channel name: %s\n", session->channel_name); + } else { + ast_log(LOG_DEBUG, "No frame in stack for session with with channel name: %s\n", session->channel_name); + } + + return frame; +} + +/*! \brief Supplement for adding framehook to sip_session channel */ static struct ast_sip_session_supplement websocket_text_supplement = { .method = "INVITE", .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1, -// .incoming_request = t38_incoming_invite_request, -// .outgoing_request = t38_outgoing_invite_request, +// .incoming_request = xx_incoming_invite_request, +// .outgoing_request = xx_outgoing_invite_request, }; static int ast_websocket_text_fd(const struct ast_websocket_text *ws_text) @@ -78,18 +150,54 @@ static void ast_websocket_text_destroy(void *obj) ast_free(obj); } + +static struct ast_format_cap *set_incoming_call_offer_cap( + struct ast_sip_session *session, struct ast_sip_session_media *session_media, + const struct pjmedia_sdp_media *stream) +{ + struct ast_format_cap *incoming_call_offer_cap; + struct ast_format_cap *remote; + SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session)); + + remote = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT); + if (!remote) { + ast_log(LOG_ERROR, "Failed to allocate %s incoming remote capabilities\n", + ast_codec_media_type2str(session_media->type)); + SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't allocate caps\n"); + } + + /* Get the peer's capabilities*/ + + incoming_call_offer_cap = ast_sip_session_create_joint_call_cap( + session, session_media->type, remote); + + ao2_ref(remote, -1); + + if (!incoming_call_offer_cap || ast_format_cap_empty(incoming_call_offer_cap)) { + ao2_cleanup(incoming_call_offer_cap); + SCOPE_EXIT_RTN_VALUE(NULL, "No incoming call offer caps\n"); + } + + SCOPE_EXIT_RTN_VALUE(incoming_call_offer_cap); +} + /*! \brief Function which negotiates an incoming media stream */ -static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, - struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *sdp, +static int negotiate_incoming_sdp_stream(struct ast_sip_session *sip_session, + struct ast_sip_session_media *sip_session_media, const struct pjmedia_sdp_session *sdp, int index, struct ast_stream *asterisk_stream) { char host[NI_MAXHOST]; pjmedia_sdp_media *stream = sdp->media[index]; + struct ast_format_cap *joint; + struct ast_sip_session_media *session_media_transport; + int res; RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); + SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(sip_session)); - if (!session->endpoint->media.websocket_text_configuration.enabled) { - ast_debug(3, "Declining: websocket text configuration not enabled on session\n"); - return 0; + ast_debug(3, "websocket negotiate_incoming_sdp_stream for media type '%s' direction output %d\n", ast_codec_media_type2str(sip_session_media->type), sip_session->call_direction); + + if (!sip_session->endpoint->media.websocket_text_configuration.enabled) { + SCOPE_EXIT_RTN_VALUE(0, "Declining: websocket text configuration not enabled on sip_session\n"); } ast_copy_pj_str(host, stream->conn ? &stream->conn->addr : &sdp->conn->addr, sizeof(host)); @@ -97,128 +205,377 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, /* Ensure that the address provided is valid */ if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) { /* The provided host was actually invalid so we error out this negotiation */ - ast_debug(3, "Declining: provided host is invalid\n"); - return 0; + SCOPE_EXIT_RTN_VALUE(0, "Declining: provided host is invalid\n"); } /* Check the address family to make sure it matches configured */ - if ((ast_sockaddr_is_ipv6(addrs) && !session->endpoint->media.websocket_text_configuration.ipv6) || - (ast_sockaddr_is_ipv4(addrs) && session->endpoint->media.websocket_text_configuration.ipv6)) { + if ((ast_sockaddr_is_ipv6(addrs) && !sip_session->endpoint->media.websocket_text_configuration.ipv6) || + (ast_sockaddr_is_ipv4(addrs) && sip_session->endpoint->media.websocket_text_configuration.ipv6)) { /* The address does not match configured */ - ast_debug(3, "Declining: provided host does not match configured address family\n"); - return 0; + SCOPE_EXIT_RTN_VALUE(0, "Declining: provided host does not match configured address family\n"); } - return 1; + RAII_VAR(char *, transport_str, ast_strndup(stream->desc.transport.ptr, stream->desc.transport.slen), ast_free); + + if (!transport_str || !strstr(transport_str, "TCP/WSS")) { + SCOPE_EXIT_RTN_VALUE(-1, "Incompatible transport\n"); + } + + struct websocket_session *ws_session = NULL; + + if (!sip_session_media->websocket_text) { + sip_session_media->websocket_text = ast_calloc(1, sizeof(*sip_session_media->websocket_text)); + if (!sip_session_media->websocket_text) { + SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create rtp\n"); + } + ast_debug(3, "websocket negotiate_incoming_sdp_stream created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); + } else { + ast_debug(3, "websocket negotiate_incoming_sdp_stream already created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); + } + + if (sip_session->channel && sip_session_media->websocket_text->fd != 0xDEAD) { + sip_session_media->websocket_text->fd = 0xDEAD; + + ws_session = ast_calloc(1, sizeof(*ws_session)); + if (!ws_session) { + SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); + } + + strcpy(ws_session->channel_name, "channel_demo"); + + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); + AST_LIST_UNLOCK(&websocket_session_list); + + // ast_free(ws_session); + + ast_debug(3, "websocket negotiate_incoming_sdp_stream linked with sip session channel name %s\n", ast_channel_name(sip_session->channel)); + + } else { + ast_debug(3, "websocket negotiate_incoming_sdp_stream without sip session channel\n"); + } + + session_media_transport = ast_sip_session_media_get_transport(sip_session, sip_session_media); + + if (session_media_transport == sip_session_media || !sip_session_media->bundled) { + } + + joint = set_incoming_call_offer_cap(sip_session, sip_session_media, stream); + ao2_cleanup(joint); + + SCOPE_EXIT_RTN_VALUE(1); } /*! \brief Function which creates an outgoing stream */ -static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, +static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struct ast_sip_session_media *sip_session_media, struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_session *remote, struct ast_stream *stream) { - pj_pool_t *pool = session->inv_session->pool_prov; + pj_pool_t *pool = sip_session->inv_session->pool_prov; static const pj_str_t STR_IN = {"IN", 2}; static const pj_str_t STR_IP4 = {"IP4", 3}; static const pj_str_t STR_IP6 = {"IP6", 3}; - static const pj_str_t STR_TEXT = {"text", 4}; + static const pj_str_t STR_TCP_WSS = {"TCP/WSS", 7}; + static const pj_str_t STR_T140 = {"t140", 4}; + static const pj_str_t STR_RTP_AVP = {"RTP/AVPF", 8}; pjmedia_sdp_media *media; const char *hostip = NULL; struct ast_sockaddr addr; -/* + char tmp[512]; pj_str_t stmp; -*/ - - if (!session->endpoint->media.websocket_text_configuration.enabled) { - ast_debug(3, "Not creating outgoing SDP stream: websocket text not enabled\n"); - return 1; - } else - - /* - if ((session->t38state != T38_LOCAL_REINVITE) && (session->t38state != T38_PEER_REINVITE) && - (session->t38state != T38_ENABLED)) { - ast_debug(3, "Not creating outgoing SDP stream: T.38 not enabled\n"); - return 1; - } else if (!(state = t38_state_get_or_alloc(session))) { - return -1; - } else if (t38_initialize_session(session, session_media)) { - ast_debug(3, "Not creating outgoing SDP stream: Failed to initialize T.38 session\n"); - return -1; + SCOPE_ENTER(1, "%s Type: %s %s\n", ast_sip_session_get_name(sip_session), + ast_codec_media_type2str(sip_session_media->type), ast_str_tmp(128, ast_stream_to_str(stream, &STR_TMP))); + + ast_debug(3, "websocket create_outgoing_sdp_stream for media type '%s' direction output %d\n", ast_codec_media_type2str(sip_session_media->type), sip_session->call_direction); + + if (!sip_session->endpoint->media.websocket_text_configuration.enabled) { + SCOPE_EXIT_RTN_VALUE(1, "Not creating outgoing SDP stream: websocket text not enabled\n"); + } else { + struct ast_sockaddr temp_media_address; + struct ast_sockaddr *media_address = &address_ws_text; + + if (sip_session->endpoint->media.bind_rtp_to_media_address && !ast_strlen_zero(sip_session->endpoint->media.address)) { + if (ast_sockaddr_parse(&temp_media_address, sip_session->endpoint->media.address, 0)) { + ast_debug_rtp(1, "Endpoint %s: Binding Websocket text media to %s\n", + ast_sorcery_object_get_id(sip_session->endpoint), + sip_session->endpoint->media.address); + media_address = &temp_media_address; + } else { + ast_debug_rtp(1, "Endpoint %s: Websocket text media address invalid: %s\n", + ast_sorcery_object_get_id(sip_session->endpoint), + sip_session->endpoint->media.address); + } + } else { + struct ast_sip_transport *transport; + + transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", + sip_session->endpoint->transport); + if (transport) { + struct ast_sip_transport_state *trans_state; + + trans_state = ast_sip_get_transport_state(ast_sorcery_object_get_id(transport)); + if (trans_state) { + char hoststr[PJ_INET6_ADDRSTRLEN]; + + pj_sockaddr_print(&trans_state->host, hoststr, sizeof(hoststr), 0); + if (ast_sockaddr_parse(&temp_media_address, hoststr, 0)) { + ast_debug_rtp(1, "Transport %s bound to %s: Using it for Websocket text media.\n", + sip_session->endpoint->transport, hoststr); + media_address = &temp_media_address; + } else { + ast_debug_rtp(1, "Transport %s bound to %s: Invalid for Websocket text media.\n", + sip_session->endpoint->transport, hoststr); + } + ao2_ref(trans_state, -1); + } + ao2_ref(transport, -1); + } + } } - */ - if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) || - !(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) { - return -1; + struct websocket_session *ws_session = NULL; + + if (!sip_session_media->websocket_text) { + sip_session_media->websocket_text = ast_calloc(1, sizeof(*sip_session_media->websocket_text)); + if (!sip_session_media->websocket_text) { + SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create rtp\n"); + } + ast_debug(3, "websocket create_outgoing_sdp_stream created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); + } else { + ast_debug(3, "websocket create_outgoing_sdp_stream already created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); } - pj_strdup2(pool, &media->desc.media, ast_codec_media_type2str(session_media->type)); - media->desc.transport = STR_TEXT; + if (sip_session->channel && sip_session_media->websocket_text->fd != 0xDEAD ) + { + sip_session_media->websocket_text->fd = 0xDEAD; + + ws_session = ast_calloc(1, sizeof(*ws_session)); + if (!ws_session) { + SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); + } + + strcpy(ws_session->channel_name, "channel_demo"); - if (ast_strlen_zero(session->endpoint->media.address)) { - hostip = ast_sip_get_host_ip_string(session->endpoint->media.websocket_text_configuration.ipv6 ? pj_AF_INET6() : pj_AF_INET()); + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); + AST_LIST_UNLOCK(&websocket_session_list); + + // ast_free(ws_session); + + ast_debug(3, "websocket create_outgoing_sdp_stream linked with sip session channel name %s\n", ast_channel_name(sip_session->channel)); } else { - hostip = session->endpoint->media.address; + ast_debug(3, "websocket create_outgoing_sdp_stream without sip session channel\n"); } - if (ast_strlen_zero(hostip)) { - ast_debug(3, "Not creating outgoing SDP stream: no known host IP\n"); - return -1; + pjmedia_sdp_attr *attr; + +#ifndef HAVE_PJSIP_ENDPOINT_COMPACT_FORM + extern pj_bool_t pjsip_use_compact_form; +#else + pj_bool_t pjsip_use_compact_form = pjsip_cfg()->endpt.use_compact_form; +#endif + + if (sip_session->call_direction == AST_SIP_SESSION_OUTGOING_CALL) { + int rtp_code; + pjmedia_sdp_rtpmap rtpmap; + + if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) || + !(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) { + SCOPE_EXIT_RTN_VALUE(-1, "Pool alloc failure\n"); + } + + pj_strdup2(pool, &media->desc.media, ast_codec_media_type2str(sip_session_media->type)); + + media->desc.transport = STR_RTP_AVP; + + if (ast_strlen_zero(sip_session->endpoint->media.address)) { + hostip = ast_sip_get_host_ip_string(sip_session->endpoint->media.websocket_text_configuration.ipv6 ? pj_AF_INET6() : pj_AF_INET()); + } else { + hostip = sip_session->endpoint->media.address; + } + + if (ast_strlen_zero(hostip)) { + SCOPE_EXIT_RTN_VALUE(-1, "No local host ip\n"); + } + + media->conn->net_type = STR_IN; + media->conn->addr_type = sip_session->endpoint->media.websocket_text_configuration.ipv6 ? STR_IP6 : STR_IP4; + pj_strdup2(pool, &media->conn->addr, hostip); + media->desc.port = 16922; // (pj_uint16_t)ast_sockaddr_port(&addr); + media->desc.port_count = 1; + + rtp_code = 99; + snprintf(tmp, sizeof(tmp), "%d", rtp_code); + pj_strdup2(pool, &media->desc.fmt[media->desc.fmt_count++], tmp); + + rtpmap.pt = media->desc.fmt[media->desc.fmt_count - 1]; + + rtpmap.clock_rate = 1000; + pj_strdup2(pool, &rtpmap.enc_name, "red"); + pj_cstr(&rtpmap.param, NULL); + + pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr); + media->attr[media->attr_count++] = attr; + + rtp_code = rtp_code - 1; + + snprintf(tmp, sizeof(tmp), "%d %d/%d/%d", rtp_code + 1, rtp_code, rtp_code, rtp_code); + attr = pjmedia_sdp_attr_create(pool, "fmtp", pj_cstr(&stmp, tmp)); + media->attr[media->attr_count++] = attr; + + snprintf(tmp, sizeof(tmp), "%d", rtp_code); + pj_strdup2(pool, &media->desc.fmt[media->desc.fmt_count++], tmp); + + rtpmap.pt = media->desc.fmt[media->desc.fmt_count - 1]; + rtpmap.clock_rate = 1000; + pj_strdup2(pool, &rtpmap.enc_name, "t140"); + pj_cstr(&rtpmap.param, NULL); + + pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr); + media->attr[media->attr_count++] = attr; + + } else { + if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media)))) { + SCOPE_EXIT_RTN_VALUE(-1, "Pool alloc failure\n"); + } + + pj_strdup2(pool, &media->desc.media, ast_codec_media_type2str(sip_session_media->type)); + + media->desc.transport = STR_TCP_WSS; + media->desc.port = 8089; + media->desc.port_count = 1; + + media->desc.fmt[media->desc.fmt_count++] = STR_T140; + + snprintf(tmp, sizeof(tmp), "%s", "wss://dev56.dev.ives.fr:8089/ws_text/channel_demo" ); + attr = pjmedia_sdp_attr_create(pool, tmp, NULL); + media->attr[media->attr_count++] = attr; } - media->conn->net_type = STR_IN; - media->conn->addr_type = session->endpoint->media.websocket_text_configuration.ipv6 ? STR_IP6 : STR_IP4; - pj_strdup2(pool, &media->conn->addr, hostip); - media->desc.port = (pj_uint16_t) ast_sockaddr_port(&addr); - media->desc.port_count = 1; - media->desc.fmt[media->desc.fmt_count++] = STR_TEXT; - sdp->media[sdp->media_count++] = media; - return 1; + char szSdpBuffer[2048]; + int buf_size; + buf_size = pjmedia_sdp_print(sdp, szSdpBuffer, 2048); + if (buf_size >= 0) { + szSdpBuffer[buf_size] = '\0'; + replace_newline(szSdpBuffer, '#'); + ast_debug(3, "websocket create_outgoing_sdp_stream with sdp %s\n", szSdpBuffer); + } else { + ast_debug(3, "websocket create_outgoing_sdp_stream with sdp error %d\n", buf_size); + } + + SCOPE_EXIT_RTN_VALUE(1, "RC: 1\n"); } -static struct ast_frame *media_session_websocket_text_read_callback(struct ast_sip_session *session, struct ast_sip_session_media *session_media) +static struct ast_frame *media_sip_session_websocket_text_read_callback(struct ast_sip_session *sip_session, struct ast_sip_session_media *sip_session_media) { struct ast_frame *frame = NULL; - if (!session_media->websocket_text) { + if (!sip_session_media->websocket_text) { return &ast_null_frame; } - //frame = ast_udptl_read(session_media->udptl); + // On depile une frame dans une liste alimentée auparavant par le websocket. + struct websocket_session *ws_session = NULL; + + // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) + { + //ast_channel_name(sip_session->channel) + + if (strcmp(ws_session->channel_name, "channel_demo") == 0) { + frame = pop_frame(ws_session); + break; + } + } + AST_LIST_UNLOCK(&websocket_session_list); + if (!frame) { - return NULL; + return &ast_null_frame; } - frame->stream_num = session_media->stream_num; + //frame->frametype = AST_FRAME_TEXT; + frame->stream_num = sip_session_media->stream_num; return frame; } -static int media_session_websocket_text_write_callback(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct ast_frame *frame) +static int media_sip_session_websocket_text_write_callback(struct ast_sip_session *sip_session, struct ast_sip_session_media *sip_session_media, struct ast_frame *frame) { - if (!session_media->websocket_text) { + if (!sip_session_media->websocket_text) { return 0; } - //return ast_udptl_write(session_media->udptl, frame); + if (frame && frame->frametype == AST_FRAME_TEXT) { + struct ast_websocket *websocket = NULL; + struct websocket_session *ws_session = NULL; + + // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) + { + //ast_channel_name(sip_session->channel) + + if (strcmp(ws_session->channel_name, "channel_demo") == 0) { + websocket = ws_session->websocket; + break; + } + } + AST_LIST_UNLOCK(&websocket_session_list); + + if (websocket) { + char *payload = frame->data.ptr; + uint64_t payload_len = frame->datalen - 1; + enum ast_websocket_opcode opcode = AST_WEBSOCKET_OPCODE_TEXT; + + ast_websocket_write(websocket, opcode, payload, payload_len); + } + } + return 0; } /*! \brief Function which applies a negotiated stream */ -static int apply_negotiated_sdp_stream(struct ast_sip_session *session, - struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *local, +static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, + struct ast_sip_session_media *sip_session_media, const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_session *remote, int index, struct ast_stream *asterisk_stream) { RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); pjmedia_sdp_media *remote_stream = remote->media[index]; char host[NI_MAXHOST]; + SCOPE_ENTER(1, "%s Stream: %s\n", ast_sip_session_get_name(sip_session), + ast_str_tmp(128, ast_stream_to_str(asterisk_stream, &STR_TMP))); - if (!session_media->websocket_text) { - ast_debug(3, "Not applying negotiated SDP stream: no Websocket Text session\n"); - return 0; + if (!sip_session->channel) { + SCOPE_EXIT_RTN_VALUE(1, "No channel\n"); + } + + ast_debug(3, "websocket apply_negotiated_sdp_stream for media type '%s' with channel name %s direction output %d\n" + , ast_codec_media_type2str(sip_session_media->type) + , ast_channel_name(sip_session->channel) + , sip_session->call_direction + ); + + char szSdpBuffer[2048]; + int buf_size; + buf_size = pjmedia_sdp_print(local, szSdpBuffer, 2048); + if (buf_size >= 0) { + szSdpBuffer[buf_size] = '\0'; + replace_newline(szSdpBuffer, '#'); + ast_debug(3, "websocket apply_negotiated_sdp_stream with local sdp %s\n", szSdpBuffer); + } else { + ast_debug(3, "websocket apply_negotiated_sdp_stream with local sdp error %d\n", buf_size); + } + buf_size = pjmedia_sdp_print(remote, szSdpBuffer, 2048); + if (buf_size >= 0) { + szSdpBuffer[buf_size] = '\0'; + replace_newline(szSdpBuffer, '#'); + ast_debug(3, "websocket apply_negotiated_sdp_stream with remote sdp %s\n", szSdpBuffer); + } else { + ast_debug(3, "websocket apply_negotiated_sdp_stream with remote sdp error %d (remote %d)\n", buf_size, remote ? 1 : 0); } ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host)); @@ -226,71 +583,112 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, /* Ensure that the address provided is valid */ if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) { /* The provided host was actually invalid so we error out this negotiation */ - ast_debug(3, "Not applying negotiated SDP stream: failed to resolve remote stream host\n"); - return -1; + SCOPE_EXIT_RTN_VALUE(-1, "Not applying negotiated SDP stream: failed to resolve remote stream host\n"); } - ast_sip_session_media_set_write_callback(session, session_media, media_session_websocket_text_write_callback); - ast_sip_session_media_add_read_callback(session - , session_media - , ast_websocket_text_fd(session_media->websocket_text) - , media_session_websocket_text_read_callback - ); + struct websocket_session *ws_session = NULL; - return 0; -} + if (!sip_session_media->websocket_text) { + sip_session_media->websocket_text = ast_calloc(1, sizeof(*sip_session_media->websocket_text)); + if (!sip_session_media->websocket_text) { + SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create rtp\n"); + } + ast_debug(3, "websocket apply_negotiated_sdp_stream created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); + } else { + ast_debug(3, "websocket apply_negotiated_sdp_stream already created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); + } -/*! \brief Function which updates the media stream with external media address, if applicable */ -static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struct pjmedia_sdp_media *stream, struct ast_sip_transport *transport) -{ - RAII_VAR(struct ast_sip_transport_state *, transport_state, ast_sip_get_transport_state(ast_sorcery_object_get_id(transport)), ao2_cleanup); - char host[NI_MAXHOST]; - struct ast_sockaddr our_sdp_addr = {{0, }}; + if (sip_session->channel && sip_session_media->websocket_text->fd != 0xDEAD) { + sip_session_media->websocket_text->fd = 0xDEAD; - /* If the stream has been rejected there will be no connection line */ - if (!stream->conn || !transport_state) { - return; - } + ws_session = ast_calloc(1, sizeof(*ws_session)); + if (!ws_session) { + SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); + } + + strcpy(ws_session->channel_name, "channel_demo"); - ast_copy_pj_str(host, &stream->conn->addr, sizeof(host)); - ast_sockaddr_parse(&our_sdp_addr, host, PARSE_PORT_FORBID); + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); + AST_LIST_UNLOCK(&websocket_session_list); - /* Reversed check here. We don't check the remote endpoint being - * in our local net, but whether our outgoing session IP is - * local. If it is not, we won't do rewriting. No localnet - * configured? Always rewrite. */ - if (ast_sip_transport_is_nonlocal(transport_state, &our_sdp_addr) && transport_state->localnet) { - return; + // ast_free(ws_session); + + ast_debug(3, "websocket apply_negotiated_sdp_stream linked with sip session channel name %s\n", ast_channel_name(sip_session->channel)); + } else { + ast_debug(3, "websocket apply_negotiated_sdp_stream already linked with sip session channel name %s\n", ast_channel_name(sip_session->channel)); } - ast_debug(5, "Setting media address to %s\n", ast_sockaddr_stringify_addr_remote(&transport_state->external_media_address)); - pj_strdup2(tdata->pool, &stream->conn->addr, ast_sockaddr_stringify_addr_remote(&transport_state->external_media_address)); + + ast_sip_session_media_set_write_callback(sip_session, sip_session_media, media_sip_session_websocket_text_write_callback); + ast_sip_session_media_add_read_callback(sip_session + , sip_session_media + , ast_websocket_text_fd(sip_session_media->websocket_text) + , media_sip_session_websocket_text_read_callback + ); + + SCOPE_EXIT_RTN_VALUE(1, "Handled\n"); } -/*! \brief Function which destroys the Websocket Text instance when session ends */ -static void stream_destroy(struct ast_sip_session_media *session_media) +/*! \brief Function which destroys the Websocket Text instance when sip_session ends */ +static void stream_destroy(struct ast_sip_session_media *sip_session_media) { - if (session_media->websocket_text) { - ast_websocket_text_destroy(session_media->websocket_text); + if (sip_session_media->websocket_text) { + ast_websocket_text_destroy(sip_session_media->websocket_text); } - session_media->websocket_text = NULL; + sip_session_media->websocket_text = NULL; } /*! \brief SDP handler for 'application' media stream */ static struct ast_sip_session_sdp_handler text_sdp_handler = { - .id = STR_TEXT, + .id = "test_ws", //STR_TEXT, + .defer_incoming_sdp_stream = NULL, .negotiate_incoming_sdp_stream = negotiate_incoming_sdp_stream, .create_outgoing_sdp_stream = create_outgoing_sdp_stream, .apply_negotiated_sdp_stream = apply_negotiated_sdp_stream, - .change_outgoing_sdp_stream_media_address = change_outgoing_sdp_stream_media_address, + .change_outgoing_sdp_stream_media_address = NULL, + .stream_stop = NULL, .stream_destroy = stream_destroy, }; /*! \brief Unloads the SIP Websocket Text module from Asterisk */ static int unload_module(void) { + struct websocket_session *ws_session = NULL; + ast_sip_session_unregister_sdp_handler(&text_sdp_handler, STR_TEXT); ast_sip_session_unregister_supplement(&websocket_text_supplement); + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_list, ws_session, entry) + { + struct websocket_frame *frame_wrapper = NULL; + //struct ast_frame *frame = NULL; + + AST_LIST_REMOVE_CURRENT(entry); + if (ws_session->websocket) { + ast_websocket_unref(ws_session->websocket); + } + /**/ + AST_LIST_TRAVERSE_SAFE_BEGIN(&ws_session->frame_stack, frame_wrapper, entry) + { + ast_frfree(frame_wrapper->frame); + ast_free(frame_wrapper); + } + AST_LIST_TRAVERSE_SAFE_END; + /**/ + /* + frame = pop_frame(ws_session); + while (frame != NULL) { + ast_frfree(frame); + frame = pop_frame(ws_session); + } + */ + + ast_free(ws_session); + } + AST_LIST_TRAVERSE_SAFE_END; + AST_LIST_UNLOCK(&websocket_session_list); + return 0; } @@ -307,9 +705,9 @@ static int unload_module(void) static int load_module(void) { if (ast_check_ipv6()) { - ast_sockaddr_parse(&address, "::", 0); + ast_sockaddr_parse(&address_ws_text, "::", 0); } else { - ast_sockaddr_parse(&address, "0.0.0.0", 0); + ast_sockaddr_parse(&address_ws_text, "0.0.0.0", 0); } ast_sip_session_register_supplement(&websocket_text_supplement); From e57dce93d221212667afc494e91fcebdd11c7ec2 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Wed, 12 Jun 2024 09:34:34 +0200 Subject: [PATCH 08/31] =?UTF-8?q?Augmenter=20le=20nombre=20de=20m=C3=A9dia?= =?UTF-8?q?=20pour=20gerer=20le=20media=20'texte'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- res/res_pjsip_session.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c index 66e492dc1a8..ff3c4d182da 100644 --- a/res/res_pjsip_session.c +++ b/res/res_pjsip_session.c @@ -59,8 +59,8 @@ #define MOD_DATA_ON_RESPONSE "on_response" #define MOD_DATA_NAT_HOOK "nat_hook" -/* Most common case is one audio and one video stream */ -#define DEFAULT_NUM_SESSION_MEDIA 2 +/* Most common case is one audio, one video stream and one text stream */ +#define DEFAULT_NUM_SESSION_MEDIA 3 /* Some forward declarations */ static void handle_session_begin(struct ast_sip_session *session); From 8ab9bd820ddf59790905a49641f381321cfcf5aa Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Wed, 19 Jun 2024 10:57:56 +0200 Subject: [PATCH 09/31] Fixe orthographe --- bridges/bridge_native_rtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bridges/bridge_native_rtp.c b/bridges/bridge_native_rtp.c index efe476ecb22..b6702da7514 100644 --- a/bridges/bridge_native_rtp.c +++ b/bridges/bridge_native_rtp.c @@ -643,7 +643,7 @@ static int native_rtp_bridge_compatible_check(struct ast_bridge *bridge, struct RAII_VAR(struct rtp_glue_data *, glue0, NULL, rtp_glue_data_destroy); RAII_VAR(struct rtp_glue_data *, glue1, NULL, rtp_glue_data_destroy); - ast_debug(1, "Bridge '%s'. Checking compatability for channels '%s' and '%s'\n", + ast_debug(1, "Bridge '%s'. Checking compatibility for channels '%s' and '%s'\n", bridge->uniqueid, ast_channel_name(bc0->chan), ast_channel_name(bc1->chan)); if (!native_rtp_bridge_capable(bc0->chan)) { From 17495c9f75194c813ec64267f372696ca014061b Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Wed, 19 Jun 2024 11:01:52 +0200 Subject: [PATCH 10/31] =?UTF-8?q?ajout=20des=20capacit=C3=A9s=20quelques?= =?UTF-8?q?=20traces=20temporaires=20pour=20le=20debug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- res/res_pjsip_websocket_text.c | 164 ++++++++++++++++++++++++++++----- 1 file changed, 140 insertions(+), 24 deletions(-) diff --git a/res/res_pjsip_websocket_text.c b/res/res_pjsip_websocket_text.c index 2d425df8290..6d5b5033f51 100644 --- a/res/res_pjsip_websocket_text.c +++ b/res/res_pjsip_websocket_text.c @@ -91,7 +91,7 @@ static void replace_newline(char *buffer, char replacement) } -static int push_frame(struct websocket_session *session, struct ast_frame *frame) +static int push_frame(struct websocket_session *ws_session, struct ast_frame *frame) { struct websocket_frame *new_frame; @@ -103,29 +103,29 @@ static int push_frame(struct websocket_session *session, struct ast_frame *frame new_frame->frame = ast_frdup(frame); - AST_LIST_LOCK(&session->frame_stack); - AST_LIST_INSERT_HEAD(&session->frame_stack, new_frame, entry); - AST_LIST_UNLOCK(&session->frame_stack); + AST_LIST_LOCK(&ws_session->frame_stack); + AST_LIST_INSERT_HEAD(&ws_session->frame_stack, new_frame, entry); + AST_LIST_UNLOCK(&ws_session->frame_stack); - ast_log(LOG_DEBUG, "Frame pushed to stack for session with channel name: %s\n", session->channel_name); + ast_log(LOG_DEBUG, "Frame pushed to stack for websocket session with channel name: %s\n", ws_session->channel_name); return 0; } -static struct ast_frame *pop_frame(struct websocket_session *session) +static struct ast_frame *pop_frame(struct websocket_session *ws_session) { struct websocket_frame *frame_wrapper; struct ast_frame *frame = NULL; - AST_LIST_LOCK(&session->frame_stack); - frame_wrapper = AST_LIST_REMOVE_HEAD(&session->frame_stack, entry); - AST_LIST_UNLOCK(&session->frame_stack); + AST_LIST_LOCK(&ws_session->frame_stack); + frame_wrapper = AST_LIST_REMOVE_HEAD(&ws_session->frame_stack, entry); + AST_LIST_UNLOCK(&ws_session->frame_stack); if (frame_wrapper) { frame = frame_wrapper->frame; ast_free(frame_wrapper); - ast_log(LOG_DEBUG, "Frame popped from stack for session with channel name: %s\n", session->channel_name); + ast_log(LOG_DEBUG, "Frame popped from stack for websocket session with channel name: %s\n", ws_session->channel_name); } else { - ast_log(LOG_DEBUG, "No frame in stack for session with with channel name: %s\n", session->channel_name); + ast_log(LOG_DEBUG, "No frame in stack for websocket session with with channel name: %s\n", ws_session->channel_name); } return frame; @@ -166,7 +166,11 @@ static struct ast_format_cap *set_incoming_call_offer_cap( SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't allocate caps\n"); } - /* Get the peer's capabilities*/ + // Ajouter le format T.140 + if (ast_format_cap_append(remote, ast_format_t140, 0) != 0) { + ast_log(LOG_ERROR, "Failed to add T.140 format\n"); + SCOPE_EXIT_RTN_VALUE(NULL, "Impossible to add t140 in call offer caps\n"); + } incoming_call_offer_cap = ast_sip_session_create_joint_call_cap( session, session_media->type, remote); @@ -189,7 +193,6 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *sip_session, char host[NI_MAXHOST]; pjmedia_sdp_media *stream = sdp->media[index]; struct ast_format_cap *joint; - struct ast_sip_session_media *session_media_transport; int res; RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(sip_session)); @@ -215,10 +218,17 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *sip_session, SCOPE_EXIT_RTN_VALUE(0, "Declining: provided host does not match configured address family\n"); } + /* If no type formats have been configured reject this stream */ + if (!ast_format_cap_has_type(sip_session->endpoint->media.codecs, sip_session_media->type)) { + ast_debug(3, "Endpoint has no codecs for media type '%s', declining stream\n", + ast_codec_media_type2str(sip_session_media->type)); + SCOPE_EXIT_RTN_VALUE(0, "Endpoint has no codecs\n"); + } + RAII_VAR(char *, transport_str, ast_strndup(stream->desc.transport.ptr, stream->desc.transport.slen), ast_free); if (!transport_str || !strstr(transport_str, "TCP/WSS")) { - SCOPE_EXIT_RTN_VALUE(-1, "Incompatible transport\n"); + SCOPE_EXIT_RTN_VALUE(0, "Incompatible transport\n"); } struct websocket_session *ws_session = NULL; @@ -255,20 +265,18 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *sip_session, ast_debug(3, "websocket negotiate_incoming_sdp_stream without sip session channel\n"); } - session_media_transport = ast_sip_session_media_get_transport(sip_session, sip_session_media); - - if (session_media_transport == sip_session_media || !sip_session_media->bundled) { - } - joint = set_incoming_call_offer_cap(sip_session, sip_session_media, stream); + ast_stream_set_formats(asterisk_stream, joint); ao2_cleanup(joint); + ast_stream_set_state(asterisk_stream, AST_STREAM_STATE_SENDRECV); + SCOPE_EXIT_RTN_VALUE(1); } /*! \brief Function which creates an outgoing stream */ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struct ast_sip_session_media *sip_session_media, - struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_session *remote, struct ast_stream *stream) + struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_session *remote, struct ast_stream *asterisk_stream) { pj_pool_t *pool = sip_session->inv_session->pool_prov; static const pj_str_t STR_IN = {"IN", 2}; @@ -277,7 +285,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc static const pj_str_t STR_TCP_WSS = {"TCP/WSS", 7}; static const pj_str_t STR_T140 = {"t140", 4}; static const pj_str_t STR_RTP_AVP = {"RTP/AVPF", 8}; - + //struct pjmedia_sdp_media *remote_stream = remote->media[index]; pjmedia_sdp_media *media; const char *hostip = NULL; struct ast_sockaddr addr; @@ -285,13 +293,21 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc char tmp[512]; pj_str_t stmp; SCOPE_ENTER(1, "%s Type: %s %s\n", ast_sip_session_get_name(sip_session), - ast_codec_media_type2str(sip_session_media->type), ast_str_tmp(128, ast_stream_to_str(stream, &STR_TMP))); + ast_codec_media_type2str(sip_session_media->type), ast_str_tmp(128, ast_stream_to_str(asterisk_stream, &STR_TMP))); ast_debug(3, "websocket create_outgoing_sdp_stream for media type '%s' direction output %d\n", ast_codec_media_type2str(sip_session_media->type), sip_session->call_direction); + /* + RAII_VAR(char *, transport_str, ast_strndup(stream->desc.transport.ptr, stream->desc.transport.slen), ast_free); + + if (!transport_str || !strstr(transport_str, "TCP/WSS")) { + SCOPE_EXIT_RTN_VALUE(0, "Incompatible transport\n"); + } + */ if (!sip_session->endpoint->media.websocket_text_configuration.enabled) { SCOPE_EXIT_RTN_VALUE(1, "Not creating outgoing SDP stream: websocket text not enabled\n"); } else { + /**/ struct ast_sockaddr temp_media_address; struct ast_sockaddr *media_address = &address_ws_text; @@ -332,6 +348,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc ao2_ref(transport, -1); } } + /**/ } struct websocket_session *ws_session = NULL; @@ -339,7 +356,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc if (!sip_session_media->websocket_text) { sip_session_media->websocket_text = ast_calloc(1, sizeof(*sip_session_media->websocket_text)); if (!sip_session_media->websocket_text) { - SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create rtp\n"); + SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create websocket text\n"); } ast_debug(3, "websocket create_outgoing_sdp_stream created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); } else { @@ -465,6 +482,16 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc } else { ast_debug(3, "websocket create_outgoing_sdp_stream with sdp error %d\n", buf_size); } + if (remote) { + buf_size = pjmedia_sdp_print(remote, szSdpBuffer, 2048); + if (buf_size >= 0) { + szSdpBuffer[buf_size] = '\0'; + replace_newline(szSdpBuffer, '#'); + ast_debug(3, "websocket create_outgoing_sdp_stream with remote sdp %s\n", szSdpBuffer); + } else { + ast_debug(3, "websocket create_outgoing_sdp_stream with remote sdp error %d (remote %d)\n", buf_size, remote ? 1 : 0); + } + } SCOPE_EXIT_RTN_VALUE(1, "RC: 1\n"); } @@ -473,6 +500,8 @@ static struct ast_frame *media_sip_session_websocket_text_read_callback(struct a { struct ast_frame *frame = NULL; + ast_log(LOG_DEBUG, "Frame 1 popped from stack for session with channel name: %s\n", ast_channel_name(sip_session->channel)); + if (!sip_session_media->websocket_text) { return &ast_null_frame; } @@ -480,6 +509,8 @@ static struct ast_frame *media_sip_session_websocket_text_read_callback(struct a // On depile une frame dans une liste alimentée auparavant par le websocket. struct websocket_session *ws_session = NULL; + ast_log(LOG_DEBUG, "Frame 2 popped from stack for session with channel name: %s\n", ast_channel_name(sip_session->channel)); + // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. AST_LIST_LOCK(&websocket_session_list); AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) @@ -505,14 +536,20 @@ static struct ast_frame *media_sip_session_websocket_text_read_callback(struct a static int media_sip_session_websocket_text_write_callback(struct ast_sip_session *sip_session, struct ast_sip_session_media *sip_session_media, struct ast_frame *frame) { + ast_log(LOG_DEBUG, "Frame 1 pushed to stack for session with channel name: %s\n", ast_channel_name(sip_session->channel)); + if (!sip_session_media->websocket_text) { return 0; } + ast_log(LOG_DEBUG, "Frame 2 pushed to stack for session with channel name: %s\n", ast_channel_name(sip_session->channel)); + if (frame && frame->frametype == AST_FRAME_TEXT) { struct ast_websocket *websocket = NULL; struct websocket_session *ws_session = NULL; + ast_log(LOG_DEBUG, "Frame 3 pushed to stack for session with channel name: %s\n", ast_channel_name(sip_session->channel)); + // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. AST_LIST_LOCK(&websocket_session_list); AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) @@ -538,6 +575,73 @@ static int media_sip_session_websocket_text_write_callback(struct ast_sip_sessio return 0; } +static int set_caps(struct ast_sip_session *session, + struct ast_sip_session_media *session_media, + const struct pjmedia_sdp_media *stream, + int is_offer, struct ast_stream *asterisk_stream) +{ + RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup); + RAII_VAR(struct ast_format_cap *, peer, NULL, ao2_cleanup); + RAII_VAR(struct ast_format_cap *, joint, NULL, ao2_cleanup); + enum ast_media_type media_type = session_media->type; + int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) && + ast_format_cap_count(session->direct_media_cap); + int dsp_features = 0; + SCOPE_ENTER(1, "%s %s\n", ast_sip_session_get_name(session), is_offer ? "OFFER" : "ANSWER"); + + if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) || + !(peer = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) || + !(joint = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) { + ast_log(LOG_ERROR, "Failed to allocate %s capabilities\n", + ast_codec_media_type2str(session_media->type)); + SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create %s capabilities\n", + ast_codec_media_type2str(session_media->type)); + } + + /* get the endpoint capabilities */ + if (direct_media_enabled) { + ast_format_cap_get_compatible(session->endpoint->media.codecs, session->direct_media_cap, caps); + } else { + ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, media_type); + } + + // Ajouter le format T.140 + if (ast_format_cap_append(peer, ast_format_t140, 0) != 0) { + ast_log(LOG_ERROR, "Failed to add T.140 format\n"); + SCOPE_EXIT_RTN_VALUE(-1, "Impossible to add t140 in call offer caps\n"); + } + + /* get the joint capabilities between peer and endpoint */ + ast_format_cap_get_compatible(caps, peer, joint); + + ast_stream_set_formats(asterisk_stream, joint); + + if (session->channel && ast_sip_session_is_pending_stream_default(session, asterisk_stream)) { + ast_channel_lock(session->channel); + + ast_format_cap_remove_by_type(caps, AST_MEDIA_TYPE_UNKNOWN); + ast_format_cap_append_from_cap(caps, ast_channel_nativeformats(session->channel), + AST_MEDIA_TYPE_UNKNOWN); + ast_format_cap_remove_by_type(caps, media_type); + + ast_format_cap_append_from_cap(caps, joint, media_type); + + /* + * Apply the new formats to the channel, potentially changing + * raw read/write formats and translation path while doing so. + */ + ast_channel_nativeformats_set(session->channel, caps); + + if (ast_channel_is_bridged(session->channel)) { + ast_channel_set_unbridged_nolock(session->channel, 1); + } + + ast_channel_unlock(session->channel); + } + + SCOPE_EXIT_RTN_VALUE(0); +} + /*! \brief Function which applies a negotiated stream */ static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, struct ast_sip_session_media *sip_session_media, const struct pjmedia_sdp_session *local, @@ -559,6 +663,12 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, , sip_session->call_direction ); + RAII_VAR(char *, transport_str, ast_strndup(remote_stream->desc.transport.ptr, remote_stream->desc.transport.slen), ast_free); + + if (!transport_str || !strstr(transport_str, "TCP/WSS")) { + SCOPE_EXIT_RTN_VALUE(0, "Incompatible transport\n"); + } + char szSdpBuffer[2048]; int buf_size; buf_size = pjmedia_sdp_print(local, szSdpBuffer, 2048); @@ -620,11 +730,17 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, } ast_sip_session_media_set_write_callback(sip_session, sip_session_media, media_sip_session_websocket_text_write_callback); + /* ast_sip_session_media_add_read_callback(sip_session , sip_session_media , ast_websocket_text_fd(sip_session_media->websocket_text) , media_sip_session_websocket_text_read_callback ); + */ + + if (set_caps(sip_session, sip_session_media, remote_stream, 0, asterisk_stream)) { + SCOPE_EXIT_RTN_VALUE(-1, "set_caps failed\n"); + } SCOPE_EXIT_RTN_VALUE(1, "Handled\n"); } @@ -729,5 +845,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Websocket Text .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_CHANNEL_DRIVER, - .requires = "res_pjsip,res_pjsip_session", + .requires = "res_pjsip,res_pjsip_session,res_pjsip_sdp_rtp", ); From 16b7390bc90d6c1725490e0937ad8a958fd2565e Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Thu, 20 Jun 2024 14:46:47 +0200 Subject: [PATCH 11/31] Deactive le TEXTE en mode sendtext via SIP/MESSAGE --- main/bridge_channel.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/main/bridge_channel.c b/main/bridge_channel.c index dfd19d28fe1..68267c4871c 100644 --- a/main/bridge_channel.c +++ b/main/bridge_channel.c @@ -1060,7 +1060,8 @@ int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, st if (DEBUG_ATLEAST(1)) { if (fr->frametype == AST_FRAME_TEXT) { - ast_log(LOG_DEBUG, "Queuing TEXT frame to '%s': %*.s\n", ast_channel_name(bridge_channel->chan), + ast_log(LOG_DEBUG, "Queuing TEXT frame to '%s', stream %d: %*.s\n", ast_channel_name(bridge_channel->chan), + dup->stream_num, fr->datalen, (char *)fr->data.ptr); } else if (fr->frametype == AST_FRAME_TEXT_DATA) { struct ast_msg_data *msg = fr->data.ptr; From 71bb958c305f04722f6c84d9118e538fc8c770ad Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Thu, 20 Jun 2024 14:50:23 +0200 Subject: [PATCH 12/31] Ajout temporaire d'infos sur les traces --- res/res_pjsip_websocket_text.c | 35 +++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/res/res_pjsip_websocket_text.c b/res/res_pjsip_websocket_text.c index 6d5b5033f51..084c2ddfc43 100644 --- a/res/res_pjsip_websocket_text.c +++ b/res/res_pjsip_websocket_text.c @@ -197,7 +197,11 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *sip_session, RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(sip_session)); - ast_debug(3, "websocket negotiate_incoming_sdp_stream for media type '%s' direction output %d\n", ast_codec_media_type2str(sip_session_media->type), sip_session->call_direction); + ast_debug(3, "websocket negotiate_incoming_sdp_stream for media type '%s' direction output %d stream %d\n" + , ast_codec_media_type2str(sip_session_media->type) + , sip_session->call_direction + , sip_session_media->stream_num + ); if (!sip_session->endpoint->media.websocket_text_configuration.enabled) { SCOPE_EXIT_RTN_VALUE(0, "Declining: websocket text configuration not enabled on sip_session\n"); @@ -295,7 +299,11 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc SCOPE_ENTER(1, "%s Type: %s %s\n", ast_sip_session_get_name(sip_session), ast_codec_media_type2str(sip_session_media->type), ast_str_tmp(128, ast_stream_to_str(asterisk_stream, &STR_TMP))); - ast_debug(3, "websocket create_outgoing_sdp_stream for media type '%s' direction output %d\n", ast_codec_media_type2str(sip_session_media->type), sip_session->call_direction); + ast_debug(3, "websocket create_outgoing_sdp_stream for media type '%s' direction output %d stream %d\n" + , ast_codec_media_type2str(sip_session_media->type) + , sip_session->call_direction + , sip_session_media->stream_num + ); /* RAII_VAR(char *, transport_str, ast_strndup(stream->desc.transport.ptr, stream->desc.transport.slen), ast_free); @@ -548,8 +556,6 @@ static int media_sip_session_websocket_text_write_callback(struct ast_sip_sessio struct ast_websocket *websocket = NULL; struct websocket_session *ws_session = NULL; - ast_log(LOG_DEBUG, "Frame 3 pushed to stack for session with channel name: %s\n", ast_channel_name(sip_session->channel)); - // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. AST_LIST_LOCK(&websocket_session_list); AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) @@ -563,6 +569,24 @@ static int media_sip_session_websocket_text_write_callback(struct ast_sip_sessio } AST_LIST_UNLOCK(&websocket_session_list); + if (frame->datalen > 0) { + char *text = frame->data.ptr; + + if (text[frame->datalen - 1] != '\0') { + /* Not zero terminated, we need to allocate */ + text = ast_strndup(text, frame->datalen); + } + + if (text) { + ast_log(LOG_DEBUG, "Frame 3 pushed to stack for session with channel name: %s, msg '%s'\n", ast_channel_name(sip_session->channel), text); + + if (text != frame->data.ptr) { + /* Only free if we allocated */ + ast_free(text); + } + } + } + if (websocket) { char *payload = frame->data.ptr; uint64_t payload_len = frame->datalen - 1; @@ -657,10 +681,11 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, SCOPE_EXIT_RTN_VALUE(1, "No channel\n"); } - ast_debug(3, "websocket apply_negotiated_sdp_stream for media type '%s' with channel name %s direction output %d\n" + ast_debug(3, "websocket apply_negotiated_sdp_stream for media type '%s' with channel name %s direction output %d stream %d\n" , ast_codec_media_type2str(sip_session_media->type) , ast_channel_name(sip_session->channel) , sip_session->call_direction + , sip_session_media->stream_num ); RAII_VAR(char *, transport_str, ast_strndup(remote_stream->desc.transport.ptr, remote_stream->desc.transport.slen), ast_free); From 575acc17c8eeea9a9223a0b60f31147744a19877 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Tue, 25 Jun 2024 10:37:54 +0200 Subject: [PATCH 13/31] Ajout et gestion du flag pour gerer le T.140/RED --- include/asterisk/res_pjsip.h | 2 +- res/res_pjsip_sdp_rtp.c | 104 +++++++++++++++++++++++++++++++++-- 2 files changed, 100 insertions(+), 6 deletions(-) diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index 265b8212f5a..fb46b47954b 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -947,7 +947,7 @@ struct ast_sip_endpoint_media_configuration { unsigned int tos_text; /*! Priority for text streams */ unsigned int cos_text; - /*! Indicate if text stream supports RED */ + /*! Flag indicate if text stream supports RED */ unsigned int red_enabled; /*! Is g.726 packed in a non standard way */ unsigned int g726_non_standard; diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index 604c927cf94..49d89394787 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -69,6 +69,19 @@ static const char STR_AUDIO[] = "audio"; static const char STR_VIDEO[] = "video"; static const char STR_TEXT[] = "text"; + +static void replace_newline(char *buffer, char replacement) +{ + // Parcours du buffer jusqu'� la fin de la cha�ne + for (int i = 0; i < strlen(buffer); i++) { + // Si le caract�re courant est un retour � la ligne + if (buffer[i] == '\r' || buffer[i] == '\n') { + // Remplacement par le caract�re sp�cifi� + buffer[i] = replacement; + } + } +} + static int send_keepalive(const void *data) { struct ast_sip_session_media *session_media = (struct ast_sip_session_media *) data; @@ -975,7 +988,7 @@ static enum ast_sip_session_media_encryption get_media_encryption_type(pj_str_t *optimistic = 0; - if (!transport_str) { + if (!transport_str || !strstr(transport_str, "AVP")) { return AST_SIP_MEDIA_TRANSPORT_INVALID; } if (strstr(transport_str, "UDP/TLS")) { @@ -1529,6 +1542,21 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, int res; SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session)); + ast_debug(3, "rtp negotiate_incoming_sdp_stream for media type '%s' direction output %d stream %d\n" + , ast_codec_media_type2str(session_media->type) + , session->call_direction + , session_media->stream_num + ); + + char szSdpBuffer[1024]; + int buf_size; + buf_size = pjmedia_sdp_print(sdp, szSdpBuffer, 1024); + if (buf_size > 0) { + szSdpBuffer[buf_size] = '\0'; + replace_newline(szSdpBuffer, '#'); + ast_debug(3, "negotiate_incoming_sdp_stream with sdp %s\n", szSdpBuffer); + } + /* If no type formats have been configured reject this stream */ if (!ast_format_cap_has_type(session->endpoint->media.codecs, media_type)) { ast_debug(3, "Endpoint has no codecs for media type '%s', declining stream\n", @@ -1536,6 +1564,12 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, SCOPE_EXIT_RTN_VALUE(0, "Endpoint has no codecs\n"); } + RAII_VAR(char *, transport_str, ast_strndup(stream->desc.transport.ptr, stream->desc.transport.slen), ast_free); + + if (!transport_str || !strstr(transport_str, "AVP")) { + SCOPE_EXIT_RTN_VALUE(0, "Incompatible transport\n"); + } + /* Ensure incoming transport is compatible with the endpoint's configuration */ if (!session->endpoint->media.rtp.use_received_transport) { encryption = check_endpoint_media_transport(session->endpoint, stream); @@ -1764,6 +1798,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as static const pj_str_t STR_SENDONLY = { "sendonly", 8 }; static const pj_str_t STR_INACTIVE = { "inactive", 8 }; static const pj_str_t STR_RECVONLY = { "recvonly", 8 }; + //struct pjmedia_sdp_media *remote_stream = remote->media[index]; pjmedia_sdp_media *media; const char *hostip = NULL; struct ast_sockaddr addr; @@ -1974,6 +2009,10 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as media->attr[media->attr_count++] = attr; } + if (media_type == AST_MEDIA_TYPE_TEXT) { + ast_log(LOG_DEBUG, "SDP generate RTP frame text %d %s\n", rtp_code, ast_format_get_codec_name(format)); + } + if (media_type == AST_MEDIA_TYPE_TEXT && !strcasecmp(ast_format_get_codec_name(format), "red")) { // TODO jpb: En attendant de faire mieux. if (rtp_code == 105 || rtp_code == 96) { @@ -2085,6 +2124,27 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as /* Add the media stream to the SDP */ sdp->media[sdp->media_count++] = media; + char szSdpBuffer[2048]; + int buf_size; + buf_size = pjmedia_sdp_print(sdp, szSdpBuffer, 2048); + if (buf_size >= 0) { + szSdpBuffer[buf_size] = '\0'; + replace_newline(szSdpBuffer, '#'); + ast_debug(3, "rtp create_outgoing_sdp_stream with sdp %s\n", szSdpBuffer); + } else { + ast_debug(3, "rtp create_outgoing_sdp_stream with sdp error %d\n", buf_size); + } + if (remote) { + buf_size = pjmedia_sdp_print(remote, szSdpBuffer, 2048); + if (buf_size >= 0) { + szSdpBuffer[buf_size] = '\0'; + replace_newline(szSdpBuffer, '#'); + ast_debug(3, "rtp create_outgoing_sdp_stream with remote sdp %s\n", szSdpBuffer); + } else { + ast_debug(3, "rtp create_outgoing_sdp_stream with remote sdp error %d (remote %d)\n", buf_size, remote ? 1 : 0); + } + } + SCOPE_EXIT_RTN_VALUE(1, "RC: 1\n"); } @@ -2150,6 +2210,38 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, SCOPE_EXIT_RTN_VALUE(1, "No channel\n"); } + ast_debug(3, "rtp apply_negotiated_sdp_stream for media type '%s' with channel name %s direction output %d stream %d\n" + , ast_codec_media_type2str(session_media->type) + , ast_channel_name(session->channel) + , session->call_direction + , session_media->stream_num + ); + + RAII_VAR(char *, transport_str, ast_strndup(remote_stream->desc.transport.ptr, remote_stream->desc.transport.slen), ast_free); + + if (!transport_str || !strstr(transport_str, "AVP")) { + SCOPE_EXIT_RTN_VALUE(0, "Incompatible transport\n"); + } + + char szSdpBuffer[2048]; + int buf_size; + buf_size = pjmedia_sdp_print(local, szSdpBuffer, 2048); + if (buf_size >= 0) { + szSdpBuffer[buf_size] = '\0'; + replace_newline(szSdpBuffer, '#'); + ast_debug(3, "rtp apply_negotiated_sdp_stream with local sdp %s\n", szSdpBuffer); + } else { + ast_debug(3, "rtp apply_negotiated_sdp_stream with local sdp error %d\n", buf_size); + } + buf_size = pjmedia_sdp_print(remote, szSdpBuffer, 2048); + if (buf_size >= 0) { + szSdpBuffer[buf_size] = '\0'; + replace_newline(szSdpBuffer, '#'); + ast_debug(3, "rtp apply_negotiated_sdp_stream with remote sdp %s\n", szSdpBuffer); + } else { + ast_debug(3, "rtp apply_negotiated_sdp_stream with remote sdp error %d (remote %d)\n", buf_size, remote ? 1 : 0); + } + /* Ensure incoming transport is compatible with the endpoint's configuration */ if (!session->endpoint->media.rtp.use_received_transport && check_endpoint_media_transport(session->endpoint, remote_stream) == AST_SIP_MEDIA_TRANSPORT_INVALID) { @@ -2304,6 +2396,8 @@ static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struc char host[NI_MAXHOST]; struct ast_sockaddr our_sdp_addr = { { 0, } }; + ast_debug(3, "change_outgoing_sdp_stream_media_address\n"); + /* If the stream has been rejected there will be no connection line */ if (!stream->conn || !transport_state) { return; @@ -2347,7 +2441,7 @@ static void stream_destroy(struct ast_sip_session_media *session_media) /*! \brief SDP handler for 'audio' media stream */ static struct ast_sip_session_sdp_handler audio_sdp_handler = { - .id = STR_AUDIO, + .id = "audio_rtp", //STR_AUDIO, .negotiate_incoming_sdp_stream = negotiate_incoming_sdp_stream, .create_outgoing_sdp_stream = create_outgoing_sdp_stream, .apply_negotiated_sdp_stream = apply_negotiated_sdp_stream, @@ -2358,7 +2452,7 @@ static struct ast_sip_session_sdp_handler audio_sdp_handler = { /*! \brief SDP handler for 'video' media stream */ static struct ast_sip_session_sdp_handler video_sdp_handler = { - .id = STR_VIDEO, + .id = "video_rtp", //STR_VIDEO, .negotiate_incoming_sdp_stream = negotiate_incoming_sdp_stream, .create_outgoing_sdp_stream = create_outgoing_sdp_stream, .apply_negotiated_sdp_stream = apply_negotiated_sdp_stream, @@ -2370,7 +2464,7 @@ static struct ast_sip_session_sdp_handler video_sdp_handler = { /*! \brief SDP handler for 'text' media stream */ static struct ast_sip_session_sdp_handler text_sdp_handler = { - .id = STR_TEXT, + .id = "test_rtp", //STR_TEXT, .negotiate_incoming_sdp_stream = negotiate_incoming_sdp_stream, .create_outgoing_sdp_stream = create_outgoing_sdp_stream, .apply_negotiated_sdp_stream = apply_negotiated_sdp_stream, @@ -2458,7 +2552,7 @@ static int load_module(void) ast_log(LOG_ERROR, "Unable to register SDP handler for %s stream type\n", STR_VIDEO); goto end; } - + if (ast_sip_session_register_sdp_handler(&text_sdp_handler, STR_TEXT)) { ast_log(LOG_ERROR, "Unable to register SDP handler for %s stream type\n", STR_TEXT); goto end; From fb50275f918d3076f49412716f80aa6f5a2c1732 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Tue, 25 Jun 2024 11:30:52 +0200 Subject: [PATCH 14/31] Debug end on T.140/RED Removing traces --- main/bridge_channel.c | 3 +-- res/res_rtp_asterisk.c | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/main/bridge_channel.c b/main/bridge_channel.c index 68267c4871c..dfd19d28fe1 100644 --- a/main/bridge_channel.c +++ b/main/bridge_channel.c @@ -1060,8 +1060,7 @@ int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, st if (DEBUG_ATLEAST(1)) { if (fr->frametype == AST_FRAME_TEXT) { - ast_log(LOG_DEBUG, "Queuing TEXT frame to '%s', stream %d: %*.s\n", ast_channel_name(bridge_channel->chan), - dup->stream_num, + ast_log(LOG_DEBUG, "Queuing TEXT frame to '%s': %*.s\n", ast_channel_name(bridge_channel->chan), fr->datalen, (char *)fr->data.ptr); } else if (fr->frametype == AST_FRAME_TEXT_DATA) { struct ast_msg_data *msg = fr->data.ptr; diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index ce1183aa072..0ce91322f0e 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -5307,6 +5307,7 @@ static struct ast_frame *red_t140_to_red(struct rtp_red *red) /* no primary data and no generations to send */ if (len == red->hdrlen && !red->t140.datalen) { + ast_log(LOG_DEBUG, "RTP frame texte len %d, red->hdrlen %d, red->t140.datalen %d\n", len, red->hdrlen, red->t140.datalen); return NULL; } @@ -7924,7 +7925,7 @@ static struct ast_frame *ast_rtp_interpret(struct ast_rtp_instance *instance, st /* format ast_format_t140_red became ast_format_t140 */ ao2_replace(rtp->f.subclass.format, ast_format_t140); - /* RFC 2198 - §3 ) |F| block PT | timestamp offset | block length | + /* RFC 2198 - �3 ) |F| block PT | timestamp offset | block length | * Bit F is zero for the last header block, search F==0 : */ while (header_end < data_end && (*header_end & 0x80)) { From 7da0a949d845d394fcc1d41efbc2ecdf37583aec Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Tue, 25 Jun 2024 11:31:42 +0200 Subject: [PATCH 15/31] Removing datachannel impl --- include/asterisk/res_pjsip_session.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h index 331ef50ceb4..191c3622d84 100644 --- a/include/asterisk/res_pjsip_session.h +++ b/include/asterisk/res_pjsip_session.h @@ -51,7 +51,6 @@ struct pjmedia_sdp_media; struct pjmedia_sdp_session; struct ast_dsp; struct ast_udptl; -struct ast_webrtc_datachannel; struct ast_websocket_text; /*! \brief T.38 states for a session */ @@ -130,8 +129,6 @@ struct ast_sip_session_media { /*! \brief Stream name */ char *stream_name; - /*! \brief WebRTC Datachannel instance itself */ - struct ast_webrtc_datachannel *webrtc_datachannel; /*! \brief Websocket text instance itself */ struct ast_websocket_text *websocket_text; }; From 3e20f1346677fbb32d858e4bb1786ea9443c15e0 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Tue, 25 Jun 2024 11:33:09 +0200 Subject: [PATCH 16/31] Version before deletion of implementation with list of frames --- res/res_pjsip_websocket_text.c | 380 +++++++++++++++++++++++++++------ 1 file changed, 312 insertions(+), 68 deletions(-) diff --git a/res/res_pjsip_websocket_text.c b/res/res_pjsip_websocket_text.c index 084c2ddfc43..a0c9c8c1a59 100644 --- a/res/res_pjsip_websocket_text.c +++ b/res/res_pjsip_websocket_text.c @@ -27,6 +27,8 @@ pjproject res_pjsip res_pjsip_session + res_pjsip_sdp_rtp + res_http_websocket core ***/ @@ -37,13 +39,17 @@ #include #include -#include "asterisk/utils.h" +//#include "asterisk/utils.h" #include "asterisk/module.h" -#include "asterisk/netsock2.h" +//#include "asterisk/netsock2.h" +#include "asterisk/astobj2.h" +#include "asterisk/strings.h" + #include "asterisk/channel.h" -#include "asterisk/acl.h" +//#include "asterisk/acl.h" #include "asterisk/stream.h" #include "asterisk/format_cache.h" +#include "asterisk/http.h" #include "asterisk/http_websocket.h" #include "asterisk/res_pjsip.h" @@ -59,6 +65,7 @@ static struct ast_sockaddr address_ws_text; struct ast_websocket_text { int fd; + int pipe_fds[2]; }; struct websocket_frame @@ -71,6 +78,8 @@ struct websocket_session { struct ast_websocket *websocket; char channel_name[256]; + int fd; + AST_LIST_HEAD(frame_stack, websocket_frame) frame_stack; AST_LIST_ENTRY(websocket_session) entry; }; @@ -93,18 +102,18 @@ static void replace_newline(char *buffer, char replacement) static int push_frame(struct websocket_session *ws_session, struct ast_frame *frame) { - struct websocket_frame *new_frame; + struct websocket_frame *frame_wrapper; - new_frame = ast_calloc(1, sizeof(*new_frame)); - if (!new_frame) { + frame_wrapper = ast_calloc(1, sizeof(*frame_wrapper)); + if (!frame_wrapper) { ast_log(LOG_ERROR, "Failed to allocate memory for new frame\n"); return -1; } - new_frame->frame = ast_frdup(frame); + frame_wrapper->frame = ast_frdup(frame); AST_LIST_LOCK(&ws_session->frame_stack); - AST_LIST_INSERT_HEAD(&ws_session->frame_stack, new_frame, entry); + AST_LIST_INSERT_HEAD(&ws_session->frame_stack, frame_wrapper, entry); AST_LIST_UNLOCK(&ws_session->frame_stack); ast_log(LOG_DEBUG, "Frame pushed to stack for websocket session with channel name: %s\n", ws_session->channel_name); @@ -141,7 +150,7 @@ static struct ast_sip_session_supplement websocket_text_supplement = { static int ast_websocket_text_fd(const struct ast_websocket_text *ws_text) { - return ws_text->fd; + return ws_text->pipe_fds[0]; } /*! \brief Destructor for T.38 state information */ @@ -150,7 +159,6 @@ static void ast_websocket_text_destroy(void *obj) ast_free(obj); } - static struct ast_format_cap *set_incoming_call_offer_cap( struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_media *stream) @@ -166,6 +174,12 @@ static struct ast_format_cap *set_incoming_call_offer_cap( SCOPE_EXIT_RTN_VALUE(NULL, "Couldn't allocate caps\n"); } + // Ajouter le format T.140/RED + if (ast_format_cap_append(remote, ast_format_t140_red, 0) != 0) { + ast_log(LOG_ERROR, "Failed to add T.140/RED format\n"); + SCOPE_EXIT_RTN_VALUE(NULL, "Impossible to add T.140/RED in call offer caps\n"); + } + // Ajouter le format T.140 if (ast_format_cap_append(remote, ast_format_t140, 0) != 0) { ast_log(LOG_ERROR, "Failed to add T.140 format\n"); @@ -255,16 +269,15 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *sip_session, SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); } - strcpy(ws_session->channel_name, "channel_demo"); + //strcpy(ws_session->channel_name, "channel_demo"); + //strcpy(ws_session->channel_name, ast_channel_name(sip_session->channel)); + strcpy(ws_session->channel_name, sip_session->inv_session->obj_name + strlen("inv0x")); AST_LIST_LOCK(&websocket_session_list); AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); AST_LIST_UNLOCK(&websocket_session_list); - // ast_free(ws_session); - ast_debug(3, "websocket negotiate_incoming_sdp_stream linked with sip session channel name %s\n", ast_channel_name(sip_session->channel)); - } else { ast_debug(3, "websocket negotiate_incoming_sdp_stream without sip session channel\n"); } @@ -371,8 +384,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc ast_debug(3, "websocket create_outgoing_sdp_stream already created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); } - if (sip_session->channel && sip_session_media->websocket_text->fd != 0xDEAD ) - { + if (sip_session->channel && sip_session_media->websocket_text->fd != 0xDEAD) { sip_session_media->websocket_text->fd = 0xDEAD; ws_session = ast_calloc(1, sizeof(*ws_session)); @@ -380,14 +392,14 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); } - strcpy(ws_session->channel_name, "channel_demo"); + //strcpy(ws_session->channel_name, "channel_demo"); + //strcpy(ws_session->channel_name, ast_channel_name(sip_session->channel)); + strcpy(ws_session->channel_name, sip_session->inv_session->obj_name + strlen("inv0x")); AST_LIST_LOCK(&websocket_session_list); AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); AST_LIST_UNLOCK(&websocket_session_list); - // ast_free(ws_session); - ast_debug(3, "websocket create_outgoing_sdp_stream linked with sip session channel name %s\n", ast_channel_name(sip_session->channel)); } else { ast_debug(3, "websocket create_outgoing_sdp_stream without sip session channel\n"); @@ -430,7 +442,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc media->desc.port = 16922; // (pj_uint16_t)ast_sockaddr_port(&addr); media->desc.port_count = 1; - rtp_code = 99; + rtp_code = 107; // 99; snprintf(tmp, sizeof(tmp), "%d", rtp_code); pj_strdup2(pool, &media->desc.fmt[media->desc.fmt_count++], tmp); @@ -443,9 +455,10 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr); media->attr[media->attr_count++] = attr; - rtp_code = rtp_code - 1; - - snprintf(tmp, sizeof(tmp), "%d %d/%d/%d", rtp_code + 1, rtp_code, rtp_code, rtp_code); + //rtp_code = rtp_code - 1; + //snprintf(tmp, sizeof(tmp), "%d %d/%d/%d", rtp_code + 1, rtp_code, rtp_code, rtp_code); + rtp_code = rtp_code + 1; + snprintf(tmp, sizeof(tmp), "%d %d/%d/%d", rtp_code - 1, rtp_code, rtp_code, rtp_code); attr = pjmedia_sdp_attr_create(pool, "fmtp", pj_cstr(&stmp, tmp)); media->attr[media->attr_count++] = attr; @@ -473,7 +486,14 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc media->desc.fmt[media->desc.fmt_count++] = STR_T140; - snprintf(tmp, sizeof(tmp), "%s", "wss://dev56.dev.ives.fr:8089/ws_text/channel_demo" ); + const pj_str_t* hostname = pj_gethostname(); + + if (sip_session->inv_session) { + snprintf(tmp, sizeof(tmp), "wss://%s:%d/ws_text/%s", hostname ? hostname->ptr : "localhost", media->desc.port, sip_session->inv_session->obj_name + strlen( "inv0x")); + } else { + snprintf(tmp, sizeof(tmp), "wss://%s:%d/ws_text/%s", hostname ? hostname->ptr : "localhost", media->desc.port, "channel_demo"); + } + attr = pjmedia_sdp_attr_create(pool, tmp, NULL); media->attr[media->attr_count++] = attr; } @@ -508,35 +528,48 @@ static struct ast_frame *media_sip_session_websocket_text_read_callback(struct a { struct ast_frame *frame = NULL; - ast_log(LOG_DEBUG, "Frame 1 popped from stack for session with channel name: %s\n", ast_channel_name(sip_session->channel)); - if (!sip_session_media->websocket_text) { return &ast_null_frame; } - // On depile une frame dans une liste alimentée auparavant par le websocket. - struct websocket_session *ws_session = NULL; - - ast_log(LOG_DEBUG, "Frame 2 popped from stack for session with channel name: %s\n", ast_channel_name(sip_session->channel)); + char buffer[1024]; + int bytes_read = read(ast_websocket_text_fd(sip_session_media->websocket_text), buffer, sizeof(buffer)); + if (bytes_read > 0) { + // Traiter les données lues + buffer[bytes_read] = '\0'; - // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) - { - //ast_channel_name(sip_session->channel) + // On depile une frame dans une liste alimentée auparavant par le websocket. + struct websocket_session *ws_session = NULL; - if (strcmp(ws_session->channel_name, "channel_demo") == 0) { - frame = pop_frame(ws_session); - break; + // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) + { + //if (strcmp(ws_session->channel_name, "channel_demo") == 0) { + //if (strcmp(ws_session->channel_name, ast_channel_name(sip_session->channel)) == 0) { + if (strcmp(ws_session->channel_name, sip_session->inv_session->obj_name + strlen("inv0x")) == 0) { + //frame = pop_frame(ws_session); + + struct ast_frame f_in; + memset(&f_in, 0, sizeof(f_in)); + f_in.frametype = AST_FRAME_TEXT; + //f_in.subclass.format = ast_format_none; + f_in.subclass.format = ast_format_t140; + //f_in.subclass.format = ast_format_t140_red; + f_in.datalen = bytes_read; + f_in.data.ptr = (void *)buffer; + + frame = ast_frdup(&f_in); + break; + } } + AST_LIST_UNLOCK(&websocket_session_list); } - AST_LIST_UNLOCK(&websocket_session_list); if (!frame) { return &ast_null_frame; } - //frame->frametype = AST_FRAME_TEXT; frame->stream_num = sip_session_media->stream_num; return frame; @@ -544,14 +577,10 @@ static struct ast_frame *media_sip_session_websocket_text_read_callback(struct a static int media_sip_session_websocket_text_write_callback(struct ast_sip_session *sip_session, struct ast_sip_session_media *sip_session_media, struct ast_frame *frame) { - ast_log(LOG_DEBUG, "Frame 1 pushed to stack for session with channel name: %s\n", ast_channel_name(sip_session->channel)); - if (!sip_session_media->websocket_text) { return 0; } - ast_log(LOG_DEBUG, "Frame 2 pushed to stack for session with channel name: %s\n", ast_channel_name(sip_session->channel)); - if (frame && frame->frametype == AST_FRAME_TEXT) { struct ast_websocket *websocket = NULL; struct websocket_session *ws_session = NULL; @@ -560,9 +589,9 @@ static int media_sip_session_websocket_text_write_callback(struct ast_sip_sessio AST_LIST_LOCK(&websocket_session_list); AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) { - //ast_channel_name(sip_session->channel) - - if (strcmp(ws_session->channel_name, "channel_demo") == 0) { + //if (strcmp(ws_session->channel_name, "channel_demo") == 0) { + //if (strcmp(ws_session->channel_name, ast_channel_name(sip_session->channel)) == 0) { + if (strcmp(ws_session->channel_name, sip_session->inv_session->obj_name + strlen("inv0x")) == 0) { websocket = ws_session->websocket; break; } @@ -578,7 +607,17 @@ static int media_sip_session_websocket_text_write_callback(struct ast_sip_sessio } if (text) { - ast_log(LOG_DEBUG, "Frame 3 pushed to stack for session with channel name: %s, msg '%s'\n", ast_channel_name(sip_session->channel), text); + if (websocket) { + //char *payload = frame->data.ptr; + //uint64_t payload_len = frame->datalen - 1; + char *payload = text; + uint64_t payload_len = strlen(text); + enum ast_websocket_opcode opcode = AST_WEBSOCKET_OPCODE_TEXT; + + ast_websocket_write(websocket, opcode, payload, payload_len); + } else { + ast_log(LOG_ERROR, "Frame websocket not found\n"); + } if (text != frame->data.ptr) { /* Only free if we allocated */ @@ -586,14 +625,6 @@ static int media_sip_session_websocket_text_write_callback(struct ast_sip_sessio } } } - - if (websocket) { - char *payload = frame->data.ptr; - uint64_t payload_len = frame->datalen - 1; - enum ast_websocket_opcode opcode = AST_WEBSOCKET_OPCODE_TEXT; - - ast_websocket_write(websocket, opcode, payload, payload_len); - } } return 0; @@ -629,6 +660,12 @@ static int set_caps(struct ast_sip_session *session, ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, media_type); } + // Ajouter le format T.140/RED + if (ast_format_cap_append(peer, ast_format_t140_red, 0) != 0) { + ast_log(LOG_ERROR, "Failed to add T.140/RED format\n"); + SCOPE_EXIT_RTN_VALUE(-1, "Impossible to add T.140/RED in call offer caps\n"); + } + // Ajouter le format T.140 if (ast_format_cap_append(peer, ast_format_t140, 0) != 0) { ast_log(LOG_ERROR, "Failed to add T.140 format\n"); @@ -741,27 +778,32 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); } - strcpy(ws_session->channel_name, "channel_demo"); + //strcpy(ws_session->channel_name, "channel_demo"); + //strcpy(ws_session->channel_name, ast_channel_name(sip_session->channel)); + strcpy(ws_session->channel_name, sip_session->inv_session->obj_name + strlen("inv0x")); AST_LIST_LOCK(&websocket_session_list); AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); AST_LIST_UNLOCK(&websocket_session_list); - // ast_free(ws_session); - ast_debug(3, "websocket apply_negotiated_sdp_stream linked with sip session channel name %s\n", ast_channel_name(sip_session->channel)); } else { - ast_debug(3, "websocket apply_negotiated_sdp_stream already linked with sip session channel name %s\n", ast_channel_name(sip_session->channel)); + ast_log(LOG_ERROR, "websocket apply_negotiated_sdp_stream already linked with sip session channel name %s\n", ast_channel_name(sip_session->channel)); } ast_sip_session_media_set_write_callback(sip_session, sip_session_media, media_sip_session_websocket_text_write_callback); - /* - ast_sip_session_media_add_read_callback(sip_session - , sip_session_media - , ast_websocket_text_fd(sip_session_media->websocket_text) - , media_sip_session_websocket_text_read_callback - ); - */ + + if (pipe(sip_session_media->websocket_text->pipe_fds) == -1 || sip_session_media->websocket_text == NULL || ws_session == NULL) { + SCOPE_EXIT_RTN_VALUE(-1, "pipe create to exchange frames failed\n"); + } else { + ws_session->fd = sip_session_media->websocket_text->pipe_fds[1]; + + ast_sip_session_media_add_read_callback(sip_session + , sip_session_media + , ast_websocket_text_fd(sip_session_media->websocket_text) + , media_sip_session_websocket_text_read_callback + ); + } if (set_caps(sip_session, sip_session_media, remote_stream, 0, asterisk_stream)) { SCOPE_EXIT_RTN_VALUE(-1, "set_caps failed\n"); @@ -791,6 +833,196 @@ static struct ast_sip_session_sdp_handler text_sdp_handler = { .stream_destroy = stream_destroy, }; +//========== WEBSOCKET SERVER ========== +//====================================== + +/* Function to add a variable to a list */ +static void add_variable_to_list(struct ast_variable **head, const char *name, const char *value) +{ + struct ast_variable *new_var, *last; + + new_var = ast_variable_new(name, value, ""); + if (!new_var) { + ast_log(LOG_ERROR, "Failed to create new variable\n"); + return; + } + + if (*head == NULL) { + *head = new_var; + } else { + last = *head; + while (last->next) { + last = last->next; + } + last->next = new_var; + } + + ast_log(LOG_NOTICE, "Variable added: %s=%s\n", name, value); +} + +static int mywebsocket_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_params + , struct ast_variable *headers +) +{ + ast_debug(1, "Entering WebSocket echo2 loop method %s uri %s\n", ast_get_http_method(method), uri); +/**/ + struct websocket_session *ws_session = NULL; + + // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) + { + if (!strcmp(ws_session->channel_name, uri)) { + break; + } + } + AST_LIST_UNLOCK(&websocket_session_list); + + if (!ws_session) { + ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL."); + return 0; + } +/**/ + add_variable_to_list(&get_params, "uri", uri); + + return ast_websocket_uri_cb(ser, urih, uri, method, get_params, headers); +} + +static struct ast_http_uri mywebsocketuri = { + .callback = mywebsocket_uri_cb, + .description = "Asterisk my HTTP WebSocket", + .uri = "ws_text", + .has_subtree = 1, + .data = NULL, + .key = __FILE__, +}; + +/*! \brief Simple echo implementation which echoes received text and binary frames */ +static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct ast_variable *parameters, struct ast_variable *headers) +{ + int res; + struct websocket_session *ws_session = NULL; + + ast_debug(1, "Entering WebSocket echo2 loop %s, addr remote %s and local %s\n" + , ast_websocket_session_id(websocket) + , ast_sockaddr_stringify(ast_websocket_remote_address(websocket)) + , ast_sockaddr_stringify(ast_websocket_local_address(websocket)) + ); + + struct ast_variable *i; + for (i = parameters; i; i = i->next) { + ast_debug(1, "Entering WebSocket echo2 loop %s parameters %s = %s\n", ast_websocket_session_id(websocket), i->name, i->value); + if (!strcmp(i->name, "uri")) { + // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) + { + if (!strcmp(ws_session->channel_name, i->value)) { + break; + } + } + AST_LIST_UNLOCK(&websocket_session_list); + } + } + for (i = headers; i; i = i->next) { + ast_debug(1, "Entering WebSocket echo2 loop %s headers %s = %s\n", ast_websocket_session_id(websocket), i->name, i->value); + } + + //ws_session = ast_calloc(1, sizeof(*ws_session)); + if (!ws_session) { + ast_log(LOG_ERROR, "Failed to find uri or allocate memory for WebSocket client\n"); + //ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL."); + goto end; + } + + if (ast_fd_set_flags(ast_websocket_fd(websocket), SOCK_NONBLOCK)) { + goto end; + } + + if (!ws_session->websocket) { + ast_websocket_ref(websocket); + ws_session->websocket = websocket; + } + + ast_log(LOG_DEBUG, "media_sip_session_websocket_text_read_callback fd %d with channel name: %s\n", ast_websocket_fd(ws_session->websocket), ws_session->channel_name); + + while ((res = ast_websocket_wait_for_input(websocket, -1)) > 0) { + char *payload; + uint64_t payload_len; + enum ast_websocket_opcode opcode; + int fragmented; + + if (ast_websocket_read(websocket, &payload, &payload_len, &opcode, &fragmented)) { + // We err on the side of caution and terminate the ws_session if any error occurs. + ast_log(LOG_WARNING, "Read failure during WebSocket echo2 loop\n"); + break; + } +/**/ + if (opcode == AST_WEBSOCKET_OPCODE_TEXT && payload_len > 0) { + // On empile une ast_frame avec le texte recu dans une liste + struct ast_frame f_in; + memset(&f_in, 0, sizeof(f_in)); + f_in.frametype = AST_FRAME_TEXT; + //f_in.subclass.format = ast_format_t140_red; + f_in.subclass.format = ast_format_t140; + //f_in.subclass.format = ast_format_none; + f_in.datalen = payload_len; + f_in.data.ptr = (void *)payload; + + ast_log(LOG_DEBUG, "Frame %d '%*.s' pre-push to stack for session with channel name: %s\n", (int)payload_len, (int)payload_len, (const char *)payload, ws_session->channel_name); + +// push_frame(ws_session, &f_in); + write(ws_session->fd, payload, payload_len); + + } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) { + break; + } else { + ast_debug(1, "Ignored WebSocket opcode %u\n", opcode); + } + } + +end: + ast_debug(1, "Exiting WebSocket echo2 loop %s\n", ast_websocket_session_id(websocket)); + + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_list, ws_session, entry) + { + if (ws_session->websocket == websocket) { + struct websocket_frame *frame_wrapper = NULL; + //struct ast_frame *frame = NULL; + + AST_LIST_REMOVE_CURRENT(entry); + ast_websocket_unref(ws_session->websocket); + + AST_LIST_TRAVERSE_SAFE_BEGIN(&ws_session->frame_stack, frame_wrapper, entry) + { + ast_frfree(frame_wrapper->frame); + ast_free(frame_wrapper); + } + AST_LIST_TRAVERSE_SAFE_END; + /**/ + /* + frame = pop_frame(ws_session); + while (frame != NULL) { + ast_frfree(frame); + frame = pop_frame(ws_session); + } + */ + + ast_free(ws_session); + break; + } + } + AST_LIST_TRAVERSE_SAFE_END; + AST_LIST_UNLOCK(&websocket_session_list); + + //ast_websocket_unref(websocket); +} + /*! \brief Unloads the SIP Websocket Text module from Asterisk */ static int unload_module(void) { @@ -799,6 +1031,11 @@ static int unload_module(void) ast_sip_session_unregister_sdp_handler(&text_sdp_handler, STR_TEXT); ast_sip_session_unregister_supplement(&websocket_text_supplement); + ast_websocket_server_remove_protocol(mywebsocketuri.data, "echo2", mywebsocket_echo_callback); + ast_http_uri_unlink(&mywebsocketuri); + ao2_ref(mywebsocketuri.data, -1); + mywebsocketuri.data = NULL; + AST_LIST_LOCK(&websocket_session_list); AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_list, ws_session, entry) { @@ -851,6 +1088,13 @@ static int load_module(void) ast_sockaddr_parse(&address_ws_text, "0.0.0.0", 0); } + mywebsocketuri.data = ast_websocket_server_create(); + if (!mywebsocketuri.data) { + return AST_MODULE_LOAD_DECLINE; + } + ast_http_uri_link(&mywebsocketuri); + ast_websocket_server_add_protocol(mywebsocketuri.data, "echo2", mywebsocket_echo_callback); + ast_sip_session_register_supplement(&websocket_text_supplement); if (ast_sip_session_register_sdp_handler(&text_sdp_handler, STR_TEXT)) { @@ -870,5 +1114,5 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Websocket Text .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_CHANNEL_DRIVER, - .requires = "res_pjsip,res_pjsip_session,res_pjsip_sdp_rtp", + .requires = "res_pjsip,res_pjsip_session,res_pjsip_sdp_rtp,res_http_websocket", ); From 6e1afc7bea3b0bcfef8f18f05498ac31e89fe32a Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Tue, 25 Jun 2024 14:39:57 +0200 Subject: [PATCH 17/31] Removing unnecessary files --- res/res_my_http_websocket.c | 359 ---------------------------- res/res_pjsip_webrtc_datachannel.c | 372 ----------------------------- 2 files changed, 731 deletions(-) delete mode 100644 res/res_my_http_websocket.c delete mode 100644 res/res_pjsip_webrtc_datachannel.c diff --git a/res/res_my_http_websocket.c b/res/res_my_http_websocket.c deleted file mode 100644 index 1682c117e74..00000000000 --- a/res/res_my_http_websocket.c +++ /dev/null @@ -1,359 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2024, IVèS. - * - * Jean-Pierre BROCHET - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \author Jean-Pierre BROCHET - * - * \brief Websocket Text handling - */ - -/*** MODULEINFO - core - res_http_websocket - ***/ - -#include "asterisk.h" - -#include "asterisk/module.h" -#include "asterisk/http.h" -#include "asterisk/astobj2.h" -#include "asterisk/strings.h" -#include "asterisk/file.h" -#include "asterisk/unaligned.h" -#include "asterisk/uri.h" -#include "asterisk/uuid.h" -#include "asterisk/lock.h" -#include "asterisk/channel.h" -#include "asterisk/stream.h" -#include "asterisk/frame.h" -#include "asterisk/http_websocket.h" - -struct websocket_frame -{ - struct ast_frame *frame; - AST_LIST_ENTRY(websocket_frame) entry; -}; - -struct websocket_session -{ - struct ast_websocket *websocket; - char channel_name[256]; - AST_LIST_HEAD(frame_stack, websocket_frame) frame_stack; - AST_LIST_ENTRY(websocket_session) entry; -}; - -static AST_LIST_HEAD(websocket_session_list, websocket_session) websocket_session_list; - -static int push_frame(struct websocket_session *ws_session, struct ast_frame *frame) -{ - struct websocket_frame *new_frame; - - new_frame = ast_calloc(1, sizeof(*new_frame)); - if (!new_frame) { - ast_log(LOG_ERROR, "Failed to allocate memory for new frame\n"); - return -1; - } - - new_frame->frame = ast_frdup(frame); - - AST_LIST_LOCK(&ws_session->frame_stack); - AST_LIST_INSERT_HEAD(&ws_session->frame_stack, new_frame, entry); - AST_LIST_UNLOCK(&ws_session->frame_stack); - - ast_log(LOG_DEBUG, "Frame pushed to stack for websocket session with channel name: %s\n", ws_session->channel_name); - return 0; -} - -static struct ast_frame *pop_frame(struct websocket_session *ws_session) -{ - struct websocket_frame *frame_wrapper; - struct ast_frame *frame = NULL; - - AST_LIST_LOCK(&ws_session->frame_stack); - frame_wrapper = AST_LIST_REMOVE_HEAD(&ws_session->frame_stack, entry); - AST_LIST_UNLOCK(&ws_session->frame_stack); - - if (frame_wrapper) { - frame = frame_wrapper->frame; - ast_free(frame_wrapper); - ast_log(LOG_DEBUG, "Frame popped from stack for websocket session with channel name: %s\n", ws_session->channel_name); - } else { - ast_log(LOG_DEBUG, "No frame in stack for websocket session with with channel name: %s\n", ws_session->channel_name); - } - - return frame; -} - -/* Function to add a variable to a list */ -static void add_variable_to_list(struct ast_variable **head, const char *name, const char *value) -{ - struct ast_variable *new_var, *last; - - new_var = ast_variable_new(name, value, ""); - if (!new_var) { - ast_log(LOG_ERROR, "Failed to create new variable\n"); - return; - } - - if (*head == NULL) { - *head = new_var; - } else { - last = *head; - while (last->next) { - last = last->next; - } - last->next = new_var; - } - - ast_log(LOG_NOTICE, "Variable added: %s=%s\n", name, value); -} - -static int mywebsocket_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_params - , struct ast_variable *headers - ) -{ - ast_debug(1, "Entering WebSocket echo2 loop method %s uri %s\n", ast_get_http_method(method), uri); -/**/ - struct websocket_session *ws_session = NULL; - - // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) - { - if (!strcmp(ws_session->channel_name, uri)) { - break; - } - } - AST_LIST_UNLOCK(&websocket_session_list); - - if (!ws_session) { - ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL."); - return 0; - } -/**/ - add_variable_to_list(&get_params, "uri", uri); - - return ast_websocket_uri_cb(ser, urih, uri, method, get_params, headers); -} - -static struct ast_http_uri mywebsocketuri = { - .callback = mywebsocket_uri_cb, - .description = "Asterisk my HTTP WebSocket", - .uri = "ws_text", - .has_subtree = 1, - .data = NULL, - .key = __FILE__, -}; - -/*! \brief Simple echo implementation which echoes received text and binary frames */ -static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct ast_variable *parameters, struct ast_variable *headers) -{ - int res; - struct websocket_session *ws_session = NULL; - - ast_debug(1, "Entering WebSocket echo2 loop %s, addr remote %s and local %s\n" - , ast_websocket_session_id(websocket) - , ast_sockaddr_stringify(ast_websocket_remote_address(websocket)) - , ast_sockaddr_stringify(ast_websocket_local_address(websocket)) - ); - - struct ast_variable *i; - for (i = parameters; i; i = i->next) { - ast_debug(1, "Entering WebSocket echo2 loop %s parameters %s = %s\n", ast_websocket_session_id(websocket), i->name, i->value); - if (!strcmp(i->name, "uri")) { - // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) - { - if (!strcmp(ws_session->channel_name, i->value)) { - break; - } - } - AST_LIST_UNLOCK(&websocket_session_list); - } - } - for (i = headers; i; i = i->next) { - ast_debug(1, "Entering WebSocket echo2 loop %s headers %s = %s\n", ast_websocket_session_id(websocket), i->name, i->value); - } - - //ws_session = ast_calloc(1, sizeof(*ws_session)); - if (!ws_session) { - ast_log(LOG_ERROR, "Failed to find uri or allocate memory for WebSocket client\n"); - //ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL."); - goto end; - } - - ast_websocket_ref(websocket); - ws_session->websocket = websocket; -/* - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); - AST_LIST_UNLOCK(&websocket_session_list); -*/ - if (ast_fd_set_flags(ast_websocket_fd(websocket), O_NONBLOCK)) { - goto end; - } - - while ((res = ast_websocket_wait_for_input(websocket, -1)) > 0) { - char *payload; - uint64_t payload_len; - enum ast_websocket_opcode opcode; - int fragmented; - - if (ast_websocket_read(websocket, &payload, &payload_len, &opcode, &fragmented)) { - // We err on the side of caution and terminate the ws_session if any error occurs. - ast_log(LOG_WARNING, "Read failure during WebSocket echo2 loop\n"); - break; - } -/**/ - if (opcode == AST_WEBSOCKET_OPCODE_TEXT && payload_len > 0) { - // On empile une ast_frame avec le texte recu dans une liste - struct ast_frame f_in; - memset(&f_in, 0, sizeof(f_in)); - f_in.frametype = AST_FRAME_TEXT; - f_in.datalen = payload_len + 1; - f_in.data.ptr = (void *)payload; - f_in.subclass.integer = 0; - f_in.offset = 0; - - ast_log(LOG_DEBUG, "Frame %d %*.s pre-push to stack for session with channel name: %s\n", (int)payload_len, (int)payload_len, (const char *)payload, ws_session->channel_name); - - push_frame(ws_session, &f_in); - } -/**/ - if (opcode == AST_WEBSOCKET_OPCODE_TEXT) { - - //ast_websocket_write(websocket, AST_WEBSOCKET_OPCODE_TEXT, payload, payload_len); -/**/ - struct ast_frame *f_out = pop_frame(ws_session); - if (f_out && f_out->frametype == AST_FRAME_TEXT) { - payload = f_out->data.ptr; - /* - if (payload[f_out->datalen - 1]) { - // Not zero terminated, we need to allocate. - payload = ast_strndup(payload, f_out->datalen); - if (!payload) { - ast_log(LOG_ERROR, "Failed to allocate memory for WebSocket client frame\n"); - break; - } - } - */ - - payload_len = f_out->datalen-1; - opcode = AST_WEBSOCKET_OPCODE_TEXT; - - ast_websocket_write(websocket, AST_WEBSOCKET_OPCODE_TEXT, payload, payload_len); - - if (payload != f_out->data.ptr) { - // Only free if we allocated. - ast_free(payload); - } - ast_frfree(f_out); - } -/**/ - } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) { - break; - } else { - ast_debug(1, "Ignored WebSocket opcode %u\n", opcode); - } - } - -end: - ast_debug(1, "Exiting WebSocket echo2 loop %s\n", ast_websocket_session_id(websocket)); - ast_websocket_unref(websocket); -} - - -static int load_module(void) -{ - mywebsocketuri.data = ast_websocket_server_create(); - if (!mywebsocketuri.data) { - return AST_MODULE_LOAD_DECLINE; - } - ast_http_uri_link(&mywebsocketuri); - ast_websocket_server_add_protocol(mywebsocketuri.data, "echo2", mywebsocket_echo_callback); - - struct websocket_session *ws_session = NULL; - - ws_session = ast_calloc(1, sizeof(*ws_session)); - if (!ws_session) { - ast_log(LOG_ERROR, "Failed to allocate memory for WebSocket client\n"); - return -1; - } - - strcpy(ws_session->channel_name, "channel_demo"); - - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); - AST_LIST_UNLOCK(&websocket_session_list); - - return 0; -} - -static int unload_module(void) -{ - struct websocket_session *ws_session = NULL; - - ast_websocket_server_remove_protocol(mywebsocketuri.data, "echo2", mywebsocket_echo_callback); - ast_http_uri_unlink(&mywebsocketuri); - ao2_ref(mywebsocketuri.data, -1); - mywebsocketuri.data = NULL; - - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_list, ws_session, entry) - { - //struct websocket_frame *frame_wrapper = NULL; - struct ast_frame *frame = NULL; - - AST_LIST_REMOVE_CURRENT(entry); - if (ws_session->websocket) { - ast_websocket_unref(ws_session->websocket); - } - /* - AST_LIST_TRAVERSE_SAFE_BEGIN(&ws_session->frame_stack, frame_wrapper, entry) - { - ast_frfree(frame_wrapper->frame); - ast_free(frame_wrapper); - } - AST_LIST_TRAVERSE_SAFE_END; - */ - frame = pop_frame(ws_session); - while (frame != NULL) { - ast_frfree(frame); - frame = pop_frame(ws_session); - } - - ast_free(ws_session); - } - AST_LIST_TRAVERSE_SAFE_END; - AST_LIST_UNLOCK(&websocket_session_list); - - return 0; -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "my HTTP WebSocket Support", - .support_level = AST_MODULE_SUPPORT_CORE, - .load = load_module, - .unload = unload_module, - .requires = "res_http_websocket", -); diff --git a/res/res_pjsip_webrtc_datachannel.c b/res/res_pjsip_webrtc_datachannel.c deleted file mode 100644 index 724b843ec6f..00000000000 --- a/res/res_pjsip_webrtc_datachannel.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * Asterisk -- An open source telephony toolkit. - * - * Copyright (C) 2024, IVèS. - * - * Jean-Pierre BROCHET - * - * See http://www.asterisk.org for more information about - * the Asterisk project. Please do not directly contact - * any of the maintainers of this project for assistance; - * the project provides a web site, mailing lists and IRC - * channels for your use. - * - * This program is free software, distributed under the terms of - * the GNU General Public License Version 2. See the LICENSE file - * at the top of the source tree. - */ - -/*! \file - * - * \author Jean-Pierre BROCHET - * - * \brief WebRTC Datachannel handling - */ - -/*** MODULEINFO - pjproject - res_pjsip - res_pjsip_session - core - ***/ - -#include "asterisk.h" - -#include -#include -#include -#include - -#include "asterisk/utils.h" -#include "asterisk/module.h" -//#include "asterisk/udptl.h" -#include "asterisk/netsock2.h" -#include "asterisk/channel.h" -#include "asterisk/acl.h" -#include "asterisk/stream.h" -#include "asterisk/format_cache.h" - -#include "asterisk/res_pjsip.h" -#include "asterisk/res_pjsip_session.h" - -static const char STR_APPLICATION[] = "application"; - -/*! \brief Address for webRTC datachannel */ -static struct ast_sockaddr address; - -/*! \brief webrtc datachannel information */ -struct ast_webrtc_datachannel -{ - int fd; -}; - -/*! \brief Supplement for adding framehook to session channel */ -static struct ast_sip_session_supplement webrtc_datachannel_supplement = { - .method = "INVITE", - .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1, -// .incoming_request = t38_incoming_invite_request, -// .outgoing_request = t38_outgoing_invite_request, -}; - -static int ast_webrtc_datachannel_fd(const struct ast_webrtc_datachannel *datachannel) -{ - return datachannel->fd; -} - -/*! \brief Destructor for T.38 state information */ -static void ast_webrtc_datachannel_destroy(void *obj) -{ - ast_free(obj); -} - -/*! \brief Function which negotiates an incoming media stream */ -static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, - struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *sdp, - int index, struct ast_stream *asterisk_stream) -{ - char host[NI_MAXHOST]; - pjmedia_sdp_media *stream = sdp->media[index]; - RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); - - if (!session->endpoint->media.webrtc_datachannel_configuration.enabled) { - ast_debug(3, "Declining; WebRTC Datachannel_configuration not enabled on session\n"); - return 0; - } - - ast_copy_pj_str(host, stream->conn ? &stream->conn->addr : &sdp->conn->addr, sizeof(host)); - - /* Ensure that the address provided is valid */ - if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) { - /* The provided host was actually invalid so we error out this negotiation */ - ast_debug(3, "Declining; provided host is invalid\n"); - return 0; - } - - /* Check the address family to make sure it matches configured */ - if ((ast_sockaddr_is_ipv6(addrs) && !session->endpoint->media.webrtc_datachannel_configuration.ipv6) || - (ast_sockaddr_is_ipv4(addrs) && session->endpoint->media.webrtc_datachannel_configuration.ipv6)) { - /* The address does not match configured */ - ast_debug(3, "Declining, provided host does not match configured address family\n"); - return 0; - } - - return 1; -} - -/*! \brief Function which creates an outgoing stream */ -static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, - struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_session *remote, struct ast_stream *stream) -{ - pj_pool_t *pool = session->inv_session->pool_prov; - static const pj_str_t STR_IN = {"IN", 2}; - static const pj_str_t STR_IP4 = {"IP4", 3}; - static const pj_str_t STR_IP6 = {"IP6", 3}; - struct t38_state *state; - pjmedia_sdp_media *media; - const char *hostip = NULL; - struct ast_sockaddr addr; - char tmp[512]; - pj_str_t stmp; - - - if (!session->endpoint->media.webrtc_datachannel_configuration.enabled) { - ast_debug(3, "Not creating outgoing SDP stream: WebRTC Datachannel not enabled\n"); - return 1; - } else - - /* - if ((session->t38state != T38_LOCAL_REINVITE) && (session->t38state != T38_PEER_REINVITE) && - (session->t38state != T38_ENABLED)) { - ast_debug(3, "Not creating outgoing SDP stream: T.38 not enabled\n"); - return 1; - } else if (!(state = t38_state_get_or_alloc(session))) { - return -1; - } else if (t38_initialize_session(session, session_media)) { - ast_debug(3, "Not creating outgoing SDP stream: Failed to initialize T.38 session\n"); - return -1; - } - - if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) || - !(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) { - return -1; - } - - pj_strdup2(pool, &media->desc.media, ast_codec_media_type2str(session_media->type)); - media->desc.transport = STR_UDPTL; - - if (ast_strlen_zero(session->endpoint->media.address)) { - hostip = ast_sip_get_host_ip_string(session->endpoint->media.t38.ipv6 ? pj_AF_INET6() : pj_AF_INET()); - } else { - hostip = session->endpoint->media.address; - } - - if (ast_strlen_zero(hostip)) { - ast_debug(3, "Not creating outgoing SDP stream: no known host IP\n"); - return -1; - } - - media->conn->net_type = STR_IN; - media->conn->addr_type = session->endpoint->media.t38.ipv6 ? STR_IP6 : STR_IP4; - pj_strdup2(pool, &media->conn->addr, hostip); - ast_udptl_get_us(session_media->udptl, &addr); - media->desc.port = (pj_uint16_t) ast_sockaddr_port(&addr); - media->desc.port_count = 1; - media->desc.fmt[media->desc.fmt_count++] = STR_T38; - - snprintf(tmp, sizeof(tmp), "%u", state->our_parms.version); - media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxVersion", pj_cstr(&stmp, tmp)); - - snprintf(tmp, sizeof(tmp), "%u", t38_get_rate(state->our_parms.rate)); - media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38MaxBitRate", pj_cstr(&stmp, tmp)); - - if (state->our_parms.fill_bit_removal) { - media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxFillBitRemoval", NULL); - } - - if (state->our_parms.transcoding_mmr) { - media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxTranscodingMMR", NULL); - } - - if (state->our_parms.transcoding_jbig) { - media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxTranscodingJBIG", NULL); - } - - switch (state->our_parms.rate_management) { - case AST_T38_RATE_MANAGEMENT_TRANSFERRED_TCF: - media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxRateManagement", &STR_TRANSFERREDTCF); - break; - case AST_T38_RATE_MANAGEMENT_LOCAL_TCF: - media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxRateManagement", &STR_LOCALTCF); - break; - } - - snprintf(tmp, sizeof(tmp), "%u", ast_udptl_get_local_max_datagram(session_media->udptl)); - media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxMaxDatagram", pj_cstr(&stmp, tmp)); - - switch (ast_udptl_get_error_correction_scheme(session_media->udptl)) { - case UDPTL_ERROR_CORRECTION_NONE: - break; - case UDPTL_ERROR_CORRECTION_FEC: - media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxUdpEC", &STR_T38UDPFEC); - break; - case UDPTL_ERROR_CORRECTION_REDUNDANCY: - media->attr[media->attr_count++] = pjmedia_sdp_attr_create(pool, "T38FaxUdpEC", &STR_T38UDPREDUNDANCY); - break; - } - - sdp->media[sdp->media_count++] = media; - */ - return 1; -} - -static struct ast_frame *media_session_webrtc_datachannel_read_callback(struct ast_sip_session *session, struct ast_sip_session_media *session_media) -{ - struct ast_frame *frame = NULL; - - if (!session_media->webrtc_datachannel) { - return &ast_null_frame; - } - - //frame = ast_udptl_read(session_media->udptl); - if (!frame) { - return NULL; - } - - frame->stream_num = session_media->stream_num; - - return frame; -} - -static int media_session_webrtc_datachannel_write_callback(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct ast_frame *frame) -{ - if (!session_media->webrtc_datachannel) { - return 0; - } - - //return ast_udptl_write(session_media->udptl, frame); - return 0; -} - -/*! \brief Function which applies a negotiated stream */ -static int apply_negotiated_sdp_stream(struct ast_sip_session *session, - struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *local, - const struct pjmedia_sdp_session *remote, int index, struct ast_stream *asterisk_stream) -{ - RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); - pjmedia_sdp_media *remote_stream = remote->media[index]; - char host[NI_MAXHOST]; - - if (!session_media->webrtc_datachannel) { - ast_debug(3, "Not applying negotiated SDP stream: no WebRTC Datachannel session\n"); - return 0; - } - - ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host)); - - /* Ensure that the address provided is valid */ - if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) { - /* The provided host was actually invalid so we error out this negotiation */ - ast_debug(3, "Not applying negotiated SDP stream: failed to resolve remote stream host\n"); - return -1; - } - - ast_sip_session_media_set_write_callback(session, session_media, media_session_webrtc_datachannel_write_callback); - ast_sip_session_media_add_read_callback(session, session_media, ast_webrtc_datachannel_fd(session_media->webrtc_datachannel), - media_session_webrtc_datachannel_read_callback); - - return 0; -} - -/*! \brief Function which updates the media stream with external media address, if applicable */ -static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struct pjmedia_sdp_media *stream, struct ast_sip_transport *transport) -{ - RAII_VAR(struct ast_sip_transport_state *, transport_state, ast_sip_get_transport_state(ast_sorcery_object_get_id(transport)), ao2_cleanup); - char host[NI_MAXHOST]; - struct ast_sockaddr our_sdp_addr = {{0, }}; - - /* If the stream has been rejected there will be no connection line */ - if (!stream->conn || !transport_state) { - return; - } - - ast_copy_pj_str(host, &stream->conn->addr, sizeof(host)); - ast_sockaddr_parse(&our_sdp_addr, host, PARSE_PORT_FORBID); - - /* Reversed check here. We don't check the remote endpoint being - * in our local net, but whether our outgoing session IP is - * local. If it is not, we won't do rewriting. No localnet - * configured? Always rewrite. */ - if (ast_sip_transport_is_nonlocal(transport_state, &our_sdp_addr) && transport_state->localnet) { - return; - } - ast_debug(5, "Setting media address to %s\n", ast_sockaddr_stringify_addr_remote(&transport_state->external_media_address)); - pj_strdup2(tdata->pool, &stream->conn->addr, ast_sockaddr_stringify_addr_remote(&transport_state->external_media_address)); -} - -/*! \brief Function which destroys the Webrtc Datachannel instance when session ends */ -static void stream_destroy(struct ast_sip_session_media *session_media) -{ - if (session_media->webrtc_datachannel) { - ast_webrtc_datachannel_destroy(session_media->webrtc_datachannel); - } - session_media->webrtc_datachannel = NULL; -} - -/*! \brief SDP handler for 'application' media stream */ -static struct ast_sip_session_sdp_handler application_sdp_handler = { - .id = STR_APPLICATION, - .negotiate_incoming_sdp_stream = negotiate_incoming_sdp_stream, - .create_outgoing_sdp_stream = create_outgoing_sdp_stream, - .apply_negotiated_sdp_stream = apply_negotiated_sdp_stream, - .change_outgoing_sdp_stream_media_address = change_outgoing_sdp_stream_media_address, - .stream_destroy = stream_destroy, -}; - -/*! \brief Unloads the SIP WebRTC Datachannel module from Asterisk */ -static int unload_module(void) -{ - ast_sip_session_unregister_sdp_handler(&application_sdp_handler, STR_APPLICATION); - ast_sip_session_unregister_supplement(&webrtc_datachannel_supplement); - - return 0; -} - -/*! - * \brief Load the module - * - * Module loading including tests for configuration or dependencies. - * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE, - * or AST_MODULE_LOAD_SUCCESS. If a dependency or environment variable fails - * tests return AST_MODULE_LOAD_FAILURE. If the module can not load the - * configuration file or other non-critical problem return - * AST_MODULE_LOAD_DECLINE. On success return AST_MODULE_LOAD_SUCCESS. - */ -static int load_module(void) -{ - if (ast_check_ipv6()) { - ast_sockaddr_parse(&address, "::", 0); - } else { - ast_sockaddr_parse(&address, "0.0.0.0", 0); - } - - ast_sip_session_register_supplement(&webrtc_datachannel_supplement); - - if (ast_sip_session_register_sdp_handler(&application_sdp_handler, STR_APPLICATION)) { - ast_log(LOG_ERROR, "Unable to register SDP handler for %s stream type\n", STR_APPLICATION); - goto end; - } - - return AST_MODULE_LOAD_SUCCESS; -end: - unload_module(); - - return AST_MODULE_LOAD_DECLINE; -} - -AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP WebRTC Datachannel Support", - .support_level = AST_MODULE_SUPPORT_CORE, - .load = load_module, - .unload = unload_module, - .load_pri = AST_MODPRI_CHANNEL_DRIVER, - .requires = "res_pjsip,res_pjsip_session", - ); From 4a0c6702f6d26067f11f81293f04a3688c1f0248 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Tue, 25 Jun 2024 16:12:18 +0200 Subject: [PATCH 18/31] Improving logs --- res/res_rtp_asterisk.c | 1 - 1 file changed, 1 deletion(-) diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c index 0ce91322f0e..74fdd78de6c 100644 --- a/res/res_rtp_asterisk.c +++ b/res/res_rtp_asterisk.c @@ -5307,7 +5307,6 @@ static struct ast_frame *red_t140_to_red(struct rtp_red *red) /* no primary data and no generations to send */ if (len == red->hdrlen && !red->t140.datalen) { - ast_log(LOG_DEBUG, "RTP frame texte len %d, red->hdrlen %d, red->t140.datalen %d\n", len, red->hdrlen, red->t140.datalen); return NULL; } From 61bc8e948541ab1e788ed2c114014c74009b1662 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Tue, 25 Jun 2024 16:14:07 +0200 Subject: [PATCH 19/31] Improved communication via websocket exchange pipe with the asterisk core --- res/res_pjsip/pjsip_configuration.c | 4 + res/res_pjsip_websocket_text.c | 120 +++++++++++++++++++++------- 2 files changed, 96 insertions(+), 28 deletions(-) diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index 655c23bf3d7..fb131c76273 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -2217,6 +2217,10 @@ int ast_res_pjsip_initialize_configuration(void) ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "named_call_group", "", named_groups_handler, named_callgroups_to_str, NULL, 0, 0); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "named_pickup_group", "", named_groups_handler, named_pickupgroups_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "device_state_busy_at", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, devicestate_busy_at)); + + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "ws_text", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.websocket_text_configuration.enabled)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "ws_text_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.websocket_text_configuration.ipv6)); + ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.enabled)); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "t38_udptl_ec", "none", t38udptl_ec_handler, t38udptl_ec_to_str, NULL, 0, 0); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl_maxdatagram", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, media.t38.maxdatagram)); diff --git a/res/res_pjsip_websocket_text.c b/res/res_pjsip_websocket_text.c index a0c9c8c1a59..a061d90551e 100644 --- a/res/res_pjsip_websocket_text.c +++ b/res/res_pjsip_websocket_text.c @@ -86,7 +86,6 @@ struct websocket_session static AST_LIST_HEAD(websocket_session_list, websocket_session) websocket_session_list; - static void replace_newline(char *buffer, char replacement) { // Parcours du buffer jusqu'à la fin de la chaîne @@ -99,6 +98,30 @@ static void replace_newline(char *buffer, char replacement) } } +static int get_websocket_tls_port(void) +{ + struct ast_config *cfg; + struct ast_flags config_flags = {0}; + int websocket_tls_port = 8089; + + // Charger le fichier de configuration http.conf + cfg = ast_config_load("http.conf", config_flags); + if (cfg) { + const char *port_str = ast_variable_retrieve(cfg, "general", "tlsbindaddr"); + if (port_str) { + const char *port_start = strrchr(port_str, ':'); + if (port_start) { + websocket_tls_port = atoi(port_start + 1); + } + } + + ast_config_destroy(cfg); + } else { + ast_log(LOG_ERROR, "Failed to load http.conf\n"); + } + + return websocket_tls_port; +} static int push_frame(struct websocket_session *ws_session, struct ast_frame *frame) { @@ -481,7 +504,8 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc pj_strdup2(pool, &media->desc.media, ast_codec_media_type2str(sip_session_media->type)); media->desc.transport = STR_TCP_WSS; - media->desc.port = 8089; + media->desc.port = get_websocket_tls_port(); + ; media->desc.port_count = 1; media->desc.fmt[media->desc.fmt_count++] = STR_T140; @@ -532,42 +556,84 @@ static struct ast_frame *media_sip_session_websocket_text_read_callback(struct a return &ast_null_frame; } - char buffer[1024]; - int bytes_read = read(ast_websocket_text_fd(sip_session_media->websocket_text), buffer, sizeof(buffer)); - if (bytes_read > 0) { - // Traiter les données lues - buffer[bytes_read] = '\0'; + ssize_t bytes_read; + size_t buffer_capacity = 1024; + char *final_buffer = NULL; + size_t total_length = 0; + int done = 0; - // On depile une frame dans une liste alimentée auparavant par le websocket. + final_buffer = (char *)ast_malloc(buffer_capacity); + if (final_buffer == NULL) { + ast_log(LOG_ERROR, "ast_malloc failed\n"); + } + + while (!done) { + if (total_length >= buffer_capacity) { + buffer_capacity *= 2; + final_buffer = (char *)ast_realloc(final_buffer, buffer_capacity); + if (final_buffer == NULL) { + ast_log(LOG_ERROR, "websocket text realloc failed\n"); + total_length = 0; + done = 1; + } + } + + bytes_read = read(ast_websocket_text_fd(sip_session_media->websocket_text), final_buffer + total_length, buffer_capacity - total_length); + if (bytes_read > 0) { + ast_log(LOG_DEBUG, "websocket text read length %ld\n", bytes_read); + total_length += bytes_read; + if (bytes_read < (ssize_t)(buffer_capacity - total_length)) { + done = 1; + } + } else if (bytes_read == 0) { + ast_log(LOG_WARNING, "websocket text read EOF\n"); + done = 1; + } else { + // Gérer les erreurs + if (errno == EAGAIN || errno == EWOULDBLOCK) { + // Aucune donnée supplémentaire disponible pour l'instant + ast_log(LOG_WARNING, "websocket text read no additional data available\n"); + done = 1; + } else { + ast_log(LOG_ERROR, "websocket text read failed: %s\n", strerror(errno)); + done = 1; + } + } + } + + if (total_length > 0) { struct websocket_session *ws_session = NULL; - // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. AST_LIST_LOCK(&websocket_session_list); AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) { //if (strcmp(ws_session->channel_name, "channel_demo") == 0) { //if (strcmp(ws_session->channel_name, ast_channel_name(sip_session->channel)) == 0) { if (strcmp(ws_session->channel_name, sip_session->inv_session->obj_name + strlen("inv0x")) == 0) { + struct ast_frame f_in; + //frame = pop_frame(ws_session); - struct ast_frame f_in; memset(&f_in, 0, sizeof(f_in)); f_in.frametype = AST_FRAME_TEXT; //f_in.subclass.format = ast_format_none; f_in.subclass.format = ast_format_t140; //f_in.subclass.format = ast_format_t140_red; - f_in.datalen = bytes_read; - f_in.data.ptr = (void *)buffer; + f_in.datalen = total_length; + f_in.data.ptr = (void *)final_buffer; frame = ast_frdup(&f_in); break; } } AST_LIST_UNLOCK(&websocket_session_list); + + ast_free(final_buffer); } if (!frame) { - return &ast_null_frame; + ast_log(LOG_ERROR, "websocket text session and frame not found\n"); + return NULL; // Error. } frame->stream_num = sip_session_media->stream_num; @@ -616,7 +682,7 @@ static int media_sip_session_websocket_text_write_callback(struct ast_sip_sessio ast_websocket_write(websocket, opcode, payload, payload_len); } else { - ast_log(LOG_ERROR, "Frame websocket not found\n"); + ast_log(LOG_ERROR, "websocket text session not found\n"); } if (text != frame->data.ptr) { @@ -798,6 +864,8 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, } else { ws_session->fd = sip_session_media->websocket_text->pipe_fds[1]; + ast_fd_set_flags(ast_websocket_text_fd(sip_session_media->websocket_text), SOCK_NONBLOCK); + ast_sip_session_media_add_read_callback(sip_session , sip_session_media , ast_websocket_text_fd(sip_session_media->websocket_text) @@ -868,7 +936,7 @@ static int mywebsocket_uri_cb(struct ast_tcptls_session_instance *ser , struct ast_variable *headers ) { - ast_debug(1, "Entering WebSocket echo2 loop method %s uri %s\n", ast_get_http_method(method), uri); + ast_debug(1, "Entering WebSocket t140 loop method %s uri %s\n", ast_get_http_method(method), uri); /**/ struct websocket_session *ws_session = NULL; @@ -907,7 +975,7 @@ static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct as int res; struct websocket_session *ws_session = NULL; - ast_debug(1, "Entering WebSocket echo2 loop %s, addr remote %s and local %s\n" + ast_debug(1, "Entering WebSocket t140 loop %s, addr remote %s and local %s\n" , ast_websocket_session_id(websocket) , ast_sockaddr_stringify(ast_websocket_remote_address(websocket)) , ast_sockaddr_stringify(ast_websocket_local_address(websocket)) @@ -915,7 +983,7 @@ static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct as struct ast_variable *i; for (i = parameters; i; i = i->next) { - ast_debug(1, "Entering WebSocket echo2 loop %s parameters %s = %s\n", ast_websocket_session_id(websocket), i->name, i->value); + ast_debug(1, "Entering WebSocket t140 loop %s parameters %s = %s\n", ast_websocket_session_id(websocket), i->name, i->value); if (!strcmp(i->name, "uri")) { // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. AST_LIST_LOCK(&websocket_session_list); @@ -929,7 +997,7 @@ static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct as } } for (i = headers; i; i = i->next) { - ast_debug(1, "Entering WebSocket echo2 loop %s headers %s = %s\n", ast_websocket_session_id(websocket), i->name, i->value); + ast_debug(1, "Entering WebSocket t140 loop %s headers %s = %s\n", ast_websocket_session_id(websocket), i->name, i->value); } //ws_session = ast_calloc(1, sizeof(*ws_session)); @@ -958,10 +1026,10 @@ static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct as if (ast_websocket_read(websocket, &payload, &payload_len, &opcode, &fragmented)) { // We err on the side of caution and terminate the ws_session if any error occurs. - ast_log(LOG_WARNING, "Read failure during WebSocket echo2 loop\n"); + ast_log(LOG_WARNING, "Read failure during WebSocket t140 loop\n"); break; } -/**/ + if (opcode == AST_WEBSOCKET_OPCODE_TEXT && payload_len > 0) { // On empile une ast_frame avec le texte recu dans une liste struct ast_frame f_in; @@ -973,8 +1041,6 @@ static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct as f_in.datalen = payload_len; f_in.data.ptr = (void *)payload; - ast_log(LOG_DEBUG, "Frame %d '%*.s' pre-push to stack for session with channel name: %s\n", (int)payload_len, (int)payload_len, (const char *)payload, ws_session->channel_name); - // push_frame(ws_session, &f_in); write(ws_session->fd, payload, payload_len); @@ -986,7 +1052,7 @@ static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct as } end: - ast_debug(1, "Exiting WebSocket echo2 loop %s\n", ast_websocket_session_id(websocket)); + ast_debug(1, "Exiting WebSocket t140 loop %s\n", ast_websocket_session_id(websocket)); AST_LIST_LOCK(&websocket_session_list); AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_list, ws_session, entry) @@ -1004,7 +1070,6 @@ static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct as ast_free(frame_wrapper); } AST_LIST_TRAVERSE_SAFE_END; - /**/ /* frame = pop_frame(ws_session); while (frame != NULL) { @@ -1019,8 +1084,6 @@ static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct as } AST_LIST_TRAVERSE_SAFE_END; AST_LIST_UNLOCK(&websocket_session_list); - - //ast_websocket_unref(websocket); } /*! \brief Unloads the SIP Websocket Text module from Asterisk */ @@ -1031,7 +1094,7 @@ static int unload_module(void) ast_sip_session_unregister_sdp_handler(&text_sdp_handler, STR_TEXT); ast_sip_session_unregister_supplement(&websocket_text_supplement); - ast_websocket_server_remove_protocol(mywebsocketuri.data, "echo2", mywebsocket_echo_callback); + ast_websocket_server_remove_protocol(mywebsocketuri.data, "t140", mywebsocket_echo_callback); ast_http_uri_unlink(&mywebsocketuri); ao2_ref(mywebsocketuri.data, -1); mywebsocketuri.data = NULL; @@ -1093,7 +1156,7 @@ static int load_module(void) return AST_MODULE_LOAD_DECLINE; } ast_http_uri_link(&mywebsocketuri); - ast_websocket_server_add_protocol(mywebsocketuri.data, "echo2", mywebsocket_echo_callback); + ast_websocket_server_add_protocol(mywebsocketuri.data, "t140", mywebsocket_echo_callback); ast_sip_session_register_supplement(&websocket_text_supplement); @@ -1116,3 +1179,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "PJSIP Websocket Text .load_pri = AST_MODPRI_CHANNEL_DRIVER, .requires = "res_pjsip,res_pjsip_session,res_pjsip_sdp_rtp,res_http_websocket", ); + From e9f44d757b484d9a51ea39984487c0da3616ecf4 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Mon, 1 Jul 2024 08:42:22 +0200 Subject: [PATCH 20/31] Suppression de traces inutiles --- res/res_pjsip_sdp_rtp.c | 78 ----------------------------------------- 1 file changed, 78 deletions(-) diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index 49d89394787..2c5448e0b3f 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -69,19 +69,6 @@ static const char STR_AUDIO[] = "audio"; static const char STR_VIDEO[] = "video"; static const char STR_TEXT[] = "text"; - -static void replace_newline(char *buffer, char replacement) -{ - // Parcours du buffer jusqu'� la fin de la cha�ne - for (int i = 0; i < strlen(buffer); i++) { - // Si le caract�re courant est un retour � la ligne - if (buffer[i] == '\r' || buffer[i] == '\n') { - // Remplacement par le caract�re sp�cifi� - buffer[i] = replacement; - } - } -} - static int send_keepalive(const void *data) { struct ast_sip_session_media *session_media = (struct ast_sip_session_media *) data; @@ -1542,21 +1529,6 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *session, int res; SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(session)); - ast_debug(3, "rtp negotiate_incoming_sdp_stream for media type '%s' direction output %d stream %d\n" - , ast_codec_media_type2str(session_media->type) - , session->call_direction - , session_media->stream_num - ); - - char szSdpBuffer[1024]; - int buf_size; - buf_size = pjmedia_sdp_print(sdp, szSdpBuffer, 1024); - if (buf_size > 0) { - szSdpBuffer[buf_size] = '\0'; - replace_newline(szSdpBuffer, '#'); - ast_debug(3, "negotiate_incoming_sdp_stream with sdp %s\n", szSdpBuffer); - } - /* If no type formats have been configured reject this stream */ if (!ast_format_cap_has_type(session->endpoint->media.codecs, media_type)) { ast_debug(3, "Endpoint has no codecs for media type '%s', declining stream\n", @@ -1798,7 +1770,6 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as static const pj_str_t STR_SENDONLY = { "sendonly", 8 }; static const pj_str_t STR_INACTIVE = { "inactive", 8 }; static const pj_str_t STR_RECVONLY = { "recvonly", 8 }; - //struct pjmedia_sdp_media *remote_stream = remote->media[index]; pjmedia_sdp_media *media; const char *hostip = NULL; struct ast_sockaddr addr; @@ -2124,27 +2095,6 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as /* Add the media stream to the SDP */ sdp->media[sdp->media_count++] = media; - char szSdpBuffer[2048]; - int buf_size; - buf_size = pjmedia_sdp_print(sdp, szSdpBuffer, 2048); - if (buf_size >= 0) { - szSdpBuffer[buf_size] = '\0'; - replace_newline(szSdpBuffer, '#'); - ast_debug(3, "rtp create_outgoing_sdp_stream with sdp %s\n", szSdpBuffer); - } else { - ast_debug(3, "rtp create_outgoing_sdp_stream with sdp error %d\n", buf_size); - } - if (remote) { - buf_size = pjmedia_sdp_print(remote, szSdpBuffer, 2048); - if (buf_size >= 0) { - szSdpBuffer[buf_size] = '\0'; - replace_newline(szSdpBuffer, '#'); - ast_debug(3, "rtp create_outgoing_sdp_stream with remote sdp %s\n", szSdpBuffer); - } else { - ast_debug(3, "rtp create_outgoing_sdp_stream with remote sdp error %d (remote %d)\n", buf_size, remote ? 1 : 0); - } - } - SCOPE_EXIT_RTN_VALUE(1, "RC: 1\n"); } @@ -2210,38 +2160,12 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *session, SCOPE_EXIT_RTN_VALUE(1, "No channel\n"); } - ast_debug(3, "rtp apply_negotiated_sdp_stream for media type '%s' with channel name %s direction output %d stream %d\n" - , ast_codec_media_type2str(session_media->type) - , ast_channel_name(session->channel) - , session->call_direction - , session_media->stream_num - ); - RAII_VAR(char *, transport_str, ast_strndup(remote_stream->desc.transport.ptr, remote_stream->desc.transport.slen), ast_free); if (!transport_str || !strstr(transport_str, "AVP")) { SCOPE_EXIT_RTN_VALUE(0, "Incompatible transport\n"); } - char szSdpBuffer[2048]; - int buf_size; - buf_size = pjmedia_sdp_print(local, szSdpBuffer, 2048); - if (buf_size >= 0) { - szSdpBuffer[buf_size] = '\0'; - replace_newline(szSdpBuffer, '#'); - ast_debug(3, "rtp apply_negotiated_sdp_stream with local sdp %s\n", szSdpBuffer); - } else { - ast_debug(3, "rtp apply_negotiated_sdp_stream with local sdp error %d\n", buf_size); - } - buf_size = pjmedia_sdp_print(remote, szSdpBuffer, 2048); - if (buf_size >= 0) { - szSdpBuffer[buf_size] = '\0'; - replace_newline(szSdpBuffer, '#'); - ast_debug(3, "rtp apply_negotiated_sdp_stream with remote sdp %s\n", szSdpBuffer); - } else { - ast_debug(3, "rtp apply_negotiated_sdp_stream with remote sdp error %d (remote %d)\n", buf_size, remote ? 1 : 0); - } - /* Ensure incoming transport is compatible with the endpoint's configuration */ if (!session->endpoint->media.rtp.use_received_transport && check_endpoint_media_transport(session->endpoint, remote_stream) == AST_SIP_MEDIA_TRANSPORT_INVALID) { @@ -2396,8 +2320,6 @@ static void change_outgoing_sdp_stream_media_address(pjsip_tx_data *tdata, struc char host[NI_MAXHOST]; struct ast_sockaddr our_sdp_addr = { { 0, } }; - ast_debug(3, "change_outgoing_sdp_stream_media_address\n"); - /* If the stream has been rejected there will be no connection line */ if (!stream->conn || !transport_state) { return; From cd8cbc55691f815b2e2dcbd91c288003fde78f39 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Mon, 1 Jul 2024 08:42:54 +0200 Subject: [PATCH 21/31] =?UTF-8?q?Premiere=20=C3=A9tape=20d'optimisation=20?= =?UTF-8?q?et=20de=20rationalistion?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- res/res_pjsip_websocket_text.c | 436 ++++++--------------------------- 1 file changed, 74 insertions(+), 362 deletions(-) diff --git a/res/res_pjsip_websocket_text.c b/res/res_pjsip_websocket_text.c index a061d90551e..4986d4e2b2d 100644 --- a/res/res_pjsip_websocket_text.c +++ b/res/res_pjsip_websocket_text.c @@ -39,14 +39,11 @@ #include #include -//#include "asterisk/utils.h" #include "asterisk/module.h" -//#include "asterisk/netsock2.h" #include "asterisk/astobj2.h" #include "asterisk/strings.h" #include "asterisk/channel.h" -//#include "asterisk/acl.h" #include "asterisk/stream.h" #include "asterisk/format_cache.h" #include "asterisk/http.h" @@ -58,9 +55,6 @@ static const char STR_TEXT[] = "text"; -/*! \brief Address for ws_text */ -static struct ast_sockaddr address_ws_text; - /*! \brief Websocket Text information */ struct ast_websocket_text { @@ -68,19 +62,13 @@ struct ast_websocket_text int pipe_fds[2]; }; -struct websocket_frame -{ - struct ast_frame *frame; - AST_LIST_ENTRY(websocket_frame) entry; -}; struct websocket_session { - struct ast_websocket *websocket; - char channel_name[256]; + char id[256]; int fd; + struct ast_websocket *websocket; - AST_LIST_HEAD(frame_stack, websocket_frame) frame_stack; AST_LIST_ENTRY(websocket_session) entry; }; @@ -123,52 +111,10 @@ static int get_websocket_tls_port(void) return websocket_tls_port; } -static int push_frame(struct websocket_session *ws_session, struct ast_frame *frame) -{ - struct websocket_frame *frame_wrapper; - - frame_wrapper = ast_calloc(1, sizeof(*frame_wrapper)); - if (!frame_wrapper) { - ast_log(LOG_ERROR, "Failed to allocate memory for new frame\n"); - return -1; - } - - frame_wrapper->frame = ast_frdup(frame); - - AST_LIST_LOCK(&ws_session->frame_stack); - AST_LIST_INSERT_HEAD(&ws_session->frame_stack, frame_wrapper, entry); - AST_LIST_UNLOCK(&ws_session->frame_stack); - - ast_log(LOG_DEBUG, "Frame pushed to stack for websocket session with channel name: %s\n", ws_session->channel_name); - return 0; -} - -static struct ast_frame *pop_frame(struct websocket_session *ws_session) -{ - struct websocket_frame *frame_wrapper; - struct ast_frame *frame = NULL; - - AST_LIST_LOCK(&ws_session->frame_stack); - frame_wrapper = AST_LIST_REMOVE_HEAD(&ws_session->frame_stack, entry); - AST_LIST_UNLOCK(&ws_session->frame_stack); - - if (frame_wrapper) { - frame = frame_wrapper->frame; - ast_free(frame_wrapper); - ast_log(LOG_DEBUG, "Frame popped from stack for websocket session with channel name: %s\n", ws_session->channel_name); - } else { - ast_log(LOG_DEBUG, "No frame in stack for websocket session with with channel name: %s\n", ws_session->channel_name); - } - - return frame; -} - /*! \brief Supplement for adding framehook to sip_session channel */ static struct ast_sip_session_supplement websocket_text_supplement = { .method = "INVITE", .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1, -// .incoming_request = xx_incoming_invite_request, -// .outgoing_request = xx_outgoing_invite_request, }; static int ast_websocket_text_fd(const struct ast_websocket_text *ws_text) @@ -234,12 +180,6 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *sip_session, RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(sip_session)); - ast_debug(3, "websocket negotiate_incoming_sdp_stream for media type '%s' direction output %d stream %d\n" - , ast_codec_media_type2str(sip_session_media->type) - , sip_session->call_direction - , sip_session_media->stream_num - ); - if (!sip_session->endpoint->media.websocket_text_configuration.enabled) { SCOPE_EXIT_RTN_VALUE(0, "Declining: websocket text configuration not enabled on sip_session\n"); } @@ -277,14 +217,14 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *sip_session, if (!sip_session_media->websocket_text) { sip_session_media->websocket_text = ast_calloc(1, sizeof(*sip_session_media->websocket_text)); if (!sip_session_media->websocket_text) { - SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create rtp\n"); + SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create websocket text\n"); } ast_debug(3, "websocket negotiate_incoming_sdp_stream created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); } else { ast_debug(3, "websocket negotiate_incoming_sdp_stream already created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); } - if (sip_session->channel && sip_session_media->websocket_text->fd != 0xDEAD) { + if (sip_session->inv_session && sip_session_media->websocket_text->fd != 0xDEAD) { sip_session_media->websocket_text->fd = 0xDEAD; ws_session = ast_calloc(1, sizeof(*ws_session)); @@ -292,15 +232,13 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *sip_session, SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); } - //strcpy(ws_session->channel_name, "channel_demo"); - //strcpy(ws_session->channel_name, ast_channel_name(sip_session->channel)); - strcpy(ws_session->channel_name, sip_session->inv_session->obj_name + strlen("inv0x")); + strcpy(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x")); AST_LIST_LOCK(&websocket_session_list); AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); AST_LIST_UNLOCK(&websocket_session_list); - ast_debug(3, "websocket negotiate_incoming_sdp_stream linked with sip session channel name %s\n", ast_channel_name(sip_session->channel)); + ast_debug(3, "websocket negotiate_incoming_sdp_stream linked with sip session invite id %s\n", sip_session->inv_session->obj_name); } else { ast_debug(3, "websocket negotiate_incoming_sdp_stream without sip session channel\n"); } @@ -325,78 +263,20 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc static const pj_str_t STR_TCP_WSS = {"TCP/WSS", 7}; static const pj_str_t STR_T140 = {"t140", 4}; static const pj_str_t STR_RTP_AVP = {"RTP/AVPF", 8}; - //struct pjmedia_sdp_media *remote_stream = remote->media[index]; pjmedia_sdp_media *media; const char *hostip = NULL; struct ast_sockaddr addr; - + struct websocket_session *ws_session = NULL; char tmp[512]; pj_str_t stmp; + SCOPE_ENTER(1, "%s Type: %s %s\n", ast_sip_session_get_name(sip_session), ast_codec_media_type2str(sip_session_media->type), ast_str_tmp(128, ast_stream_to_str(asterisk_stream, &STR_TMP))); - ast_debug(3, "websocket create_outgoing_sdp_stream for media type '%s' direction output %d stream %d\n" - , ast_codec_media_type2str(sip_session_media->type) - , sip_session->call_direction - , sip_session_media->stream_num - ); - /* - RAII_VAR(char *, transport_str, ast_strndup(stream->desc.transport.ptr, stream->desc.transport.slen), ast_free); - - if (!transport_str || !strstr(transport_str, "TCP/WSS")) { - SCOPE_EXIT_RTN_VALUE(0, "Incompatible transport\n"); - } - */ - if (!sip_session->endpoint->media.websocket_text_configuration.enabled) { SCOPE_EXIT_RTN_VALUE(1, "Not creating outgoing SDP stream: websocket text not enabled\n"); - } else { - /**/ - struct ast_sockaddr temp_media_address; - struct ast_sockaddr *media_address = &address_ws_text; - - if (sip_session->endpoint->media.bind_rtp_to_media_address && !ast_strlen_zero(sip_session->endpoint->media.address)) { - if (ast_sockaddr_parse(&temp_media_address, sip_session->endpoint->media.address, 0)) { - ast_debug_rtp(1, "Endpoint %s: Binding Websocket text media to %s\n", - ast_sorcery_object_get_id(sip_session->endpoint), - sip_session->endpoint->media.address); - media_address = &temp_media_address; - } else { - ast_debug_rtp(1, "Endpoint %s: Websocket text media address invalid: %s\n", - ast_sorcery_object_get_id(sip_session->endpoint), - sip_session->endpoint->media.address); - } - } else { - struct ast_sip_transport *transport; - - transport = ast_sorcery_retrieve_by_id(ast_sip_get_sorcery(), "transport", - sip_session->endpoint->transport); - if (transport) { - struct ast_sip_transport_state *trans_state; - - trans_state = ast_sip_get_transport_state(ast_sorcery_object_get_id(transport)); - if (trans_state) { - char hoststr[PJ_INET6_ADDRSTRLEN]; - - pj_sockaddr_print(&trans_state->host, hoststr, sizeof(hoststr), 0); - if (ast_sockaddr_parse(&temp_media_address, hoststr, 0)) { - ast_debug_rtp(1, "Transport %s bound to %s: Using it for Websocket text media.\n", - sip_session->endpoint->transport, hoststr); - media_address = &temp_media_address; - } else { - ast_debug_rtp(1, "Transport %s bound to %s: Invalid for Websocket text media.\n", - sip_session->endpoint->transport, hoststr); - } - ao2_ref(trans_state, -1); - } - ao2_ref(transport, -1); - } - } - /**/ } - struct websocket_session *ws_session = NULL; - if (!sip_session_media->websocket_text) { sip_session_media->websocket_text = ast_calloc(1, sizeof(*sip_session_media->websocket_text)); if (!sip_session_media->websocket_text) { @@ -407,7 +287,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc ast_debug(3, "websocket create_outgoing_sdp_stream already created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); } - if (sip_session->channel && sip_session_media->websocket_text->fd != 0xDEAD) { + if (sip_session->inv_session && sip_session_media->websocket_text->fd != 0xDEAD) { sip_session_media->websocket_text->fd = 0xDEAD; ws_session = ast_calloc(1, sizeof(*ws_session)); @@ -415,20 +295,19 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); } - //strcpy(ws_session->channel_name, "channel_demo"); - //strcpy(ws_session->channel_name, ast_channel_name(sip_session->channel)); - strcpy(ws_session->channel_name, sip_session->inv_session->obj_name + strlen("inv0x")); + strcpy(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x")); AST_LIST_LOCK(&websocket_session_list); AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); AST_LIST_UNLOCK(&websocket_session_list); - ast_debug(3, "websocket create_outgoing_sdp_stream linked with sip session channel name %s\n", ast_channel_name(sip_session->channel)); + ast_debug(3, "websocket create_outgoing_sdp_stream linked with sip session invite id %s\n", sip_session->inv_session->obj_name); } else { ast_debug(3, "websocket create_outgoing_sdp_stream without sip session channel\n"); } pjmedia_sdp_attr *attr; + const pj_str_t *hostname = pj_gethostname(); #ifndef HAVE_PJSIP_ENDPOINT_COMPACT_FORM extern pj_bool_t pjsip_use_compact_form; @@ -436,114 +315,28 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc pj_bool_t pjsip_use_compact_form = pjsip_cfg()->endpt.use_compact_form; #endif - if (sip_session->call_direction == AST_SIP_SESSION_OUTGOING_CALL) { - int rtp_code; - pjmedia_sdp_rtpmap rtpmap; - - if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media))) || - !(media->conn = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_conn)))) { - SCOPE_EXIT_RTN_VALUE(-1, "Pool alloc failure\n"); - } - - pj_strdup2(pool, &media->desc.media, ast_codec_media_type2str(sip_session_media->type)); - - media->desc.transport = STR_RTP_AVP; - - if (ast_strlen_zero(sip_session->endpoint->media.address)) { - hostip = ast_sip_get_host_ip_string(sip_session->endpoint->media.websocket_text_configuration.ipv6 ? pj_AF_INET6() : pj_AF_INET()); - } else { - hostip = sip_session->endpoint->media.address; - } - - if (ast_strlen_zero(hostip)) { - SCOPE_EXIT_RTN_VALUE(-1, "No local host ip\n"); - } - - media->conn->net_type = STR_IN; - media->conn->addr_type = sip_session->endpoint->media.websocket_text_configuration.ipv6 ? STR_IP6 : STR_IP4; - pj_strdup2(pool, &media->conn->addr, hostip); - media->desc.port = 16922; // (pj_uint16_t)ast_sockaddr_port(&addr); - media->desc.port_count = 1; - - rtp_code = 107; // 99; - snprintf(tmp, sizeof(tmp), "%d", rtp_code); - pj_strdup2(pool, &media->desc.fmt[media->desc.fmt_count++], tmp); - - rtpmap.pt = media->desc.fmt[media->desc.fmt_count - 1]; - - rtpmap.clock_rate = 1000; - pj_strdup2(pool, &rtpmap.enc_name, "red"); - pj_cstr(&rtpmap.param, NULL); - - pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr); - media->attr[media->attr_count++] = attr; - - //rtp_code = rtp_code - 1; - //snprintf(tmp, sizeof(tmp), "%d %d/%d/%d", rtp_code + 1, rtp_code, rtp_code, rtp_code); - rtp_code = rtp_code + 1; - snprintf(tmp, sizeof(tmp), "%d %d/%d/%d", rtp_code - 1, rtp_code, rtp_code, rtp_code); - attr = pjmedia_sdp_attr_create(pool, "fmtp", pj_cstr(&stmp, tmp)); - media->attr[media->attr_count++] = attr; + if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media)))) { + SCOPE_EXIT_RTN_VALUE(-1, "Pool alloc failure\n"); + } - snprintf(tmp, sizeof(tmp), "%d", rtp_code); - pj_strdup2(pool, &media->desc.fmt[media->desc.fmt_count++], tmp); + pj_strdup2(pool, &media->desc.media, ast_codec_media_type2str(sip_session_media->type)); - rtpmap.pt = media->desc.fmt[media->desc.fmt_count - 1]; - rtpmap.clock_rate = 1000; - pj_strdup2(pool, &rtpmap.enc_name, "t140"); - pj_cstr(&rtpmap.param, NULL); + media->desc.transport = STR_TCP_WSS; + media->desc.port = get_websocket_tls_port(); + media->desc.port_count = 1; - pjmedia_sdp_rtpmap_to_attr(pool, &rtpmap, &attr); - media->attr[media->attr_count++] = attr; + media->desc.fmt[media->desc.fmt_count++] = STR_T140; + if (sip_session->inv_session) { + snprintf(tmp, sizeof(tmp), "wss://%s:%d/ws_text/%s", hostname ? hostname->ptr : "localhost", media->desc.port, sip_session->inv_session->obj_name + strlen( "inv0x")); } else { - if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media)))) { - SCOPE_EXIT_RTN_VALUE(-1, "Pool alloc failure\n"); - } - - pj_strdup2(pool, &media->desc.media, ast_codec_media_type2str(sip_session_media->type)); - - media->desc.transport = STR_TCP_WSS; - media->desc.port = get_websocket_tls_port(); - ; - media->desc.port_count = 1; - - media->desc.fmt[media->desc.fmt_count++] = STR_T140; - - const pj_str_t* hostname = pj_gethostname(); - - if (sip_session->inv_session) { - snprintf(tmp, sizeof(tmp), "wss://%s:%d/ws_text/%s", hostname ? hostname->ptr : "localhost", media->desc.port, sip_session->inv_session->obj_name + strlen( "inv0x")); - } else { - snprintf(tmp, sizeof(tmp), "wss://%s:%d/ws_text/%s", hostname ? hostname->ptr : "localhost", media->desc.port, "channel_demo"); - } - - attr = pjmedia_sdp_attr_create(pool, tmp, NULL); - media->attr[media->attr_count++] = attr; + snprintf(tmp, sizeof(tmp), "wss://%s:%d/ws_text/%s", hostname ? hostname->ptr : "localhost", media->desc.port, "channel_demo"); } - sdp->media[sdp->media_count++] = media; + attr = pjmedia_sdp_attr_create(pool, tmp, NULL); + media->attr[media->attr_count++] = attr; - char szSdpBuffer[2048]; - int buf_size; - buf_size = pjmedia_sdp_print(sdp, szSdpBuffer, 2048); - if (buf_size >= 0) { - szSdpBuffer[buf_size] = '\0'; - replace_newline(szSdpBuffer, '#'); - ast_debug(3, "websocket create_outgoing_sdp_stream with sdp %s\n", szSdpBuffer); - } else { - ast_debug(3, "websocket create_outgoing_sdp_stream with sdp error %d\n", buf_size); - } - if (remote) { - buf_size = pjmedia_sdp_print(remote, szSdpBuffer, 2048); - if (buf_size >= 0) { - szSdpBuffer[buf_size] = '\0'; - replace_newline(szSdpBuffer, '#'); - ast_debug(3, "websocket create_outgoing_sdp_stream with remote sdp %s\n", szSdpBuffer); - } else { - ast_debug(3, "websocket create_outgoing_sdp_stream with remote sdp error %d (remote %d)\n", buf_size, remote ? 1 : 0); - } - } + sdp->media[sdp->media_count++] = media; SCOPE_EXIT_RTN_VALUE(1, "RC: 1\n"); } @@ -589,9 +382,7 @@ static struct ast_frame *media_sip_session_websocket_text_read_callback(struct a ast_log(LOG_WARNING, "websocket text read EOF\n"); done = 1; } else { - // Gérer les erreurs if (errno == EAGAIN || errno == EWOULDBLOCK) { - // Aucune donnée supplémentaire disponible pour l'instant ast_log(LOG_WARNING, "websocket text read no additional data available\n"); done = 1; } else { @@ -607,13 +398,9 @@ static struct ast_frame *media_sip_session_websocket_text_read_callback(struct a AST_LIST_LOCK(&websocket_session_list); AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) { - //if (strcmp(ws_session->channel_name, "channel_demo") == 0) { - //if (strcmp(ws_session->channel_name, ast_channel_name(sip_session->channel)) == 0) { - if (strcmp(ws_session->channel_name, sip_session->inv_session->obj_name + strlen("inv0x")) == 0) { + if (strcmp(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x")) == 0) { struct ast_frame f_in; - //frame = pop_frame(ws_session); - memset(&f_in, 0, sizeof(f_in)); f_in.frametype = AST_FRAME_TEXT; //f_in.subclass.format = ast_format_none; @@ -655,9 +442,7 @@ static int media_sip_session_websocket_text_write_callback(struct ast_sip_sessio AST_LIST_LOCK(&websocket_session_list); AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) { - //if (strcmp(ws_session->channel_name, "channel_demo") == 0) { - //if (strcmp(ws_session->channel_name, ast_channel_name(sip_session->channel)) == 0) { - if (strcmp(ws_session->channel_name, sip_session->inv_session->obj_name + strlen("inv0x")) == 0) { + if (strcmp(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x")) == 0) { websocket = ws_session->websocket; break; } @@ -674,8 +459,6 @@ static int media_sip_session_websocket_text_write_callback(struct ast_sip_sessio if (text) { if (websocket) { - //char *payload = frame->data.ptr; - //uint64_t payload_len = frame->datalen - 1; char *payload = text; uint64_t payload_len = strlen(text); enum ast_websocket_opcode opcode = AST_WEBSOCKET_OPCODE_TEXT; @@ -784,38 +567,12 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, SCOPE_EXIT_RTN_VALUE(1, "No channel\n"); } - ast_debug(3, "websocket apply_negotiated_sdp_stream for media type '%s' with channel name %s direction output %d stream %d\n" - , ast_codec_media_type2str(sip_session_media->type) - , ast_channel_name(sip_session->channel) - , sip_session->call_direction - , sip_session_media->stream_num - ); - RAII_VAR(char *, transport_str, ast_strndup(remote_stream->desc.transport.ptr, remote_stream->desc.transport.slen), ast_free); if (!transport_str || !strstr(transport_str, "TCP/WSS")) { SCOPE_EXIT_RTN_VALUE(0, "Incompatible transport\n"); } - char szSdpBuffer[2048]; - int buf_size; - buf_size = pjmedia_sdp_print(local, szSdpBuffer, 2048); - if (buf_size >= 0) { - szSdpBuffer[buf_size] = '\0'; - replace_newline(szSdpBuffer, '#'); - ast_debug(3, "websocket apply_negotiated_sdp_stream with local sdp %s\n", szSdpBuffer); - } else { - ast_debug(3, "websocket apply_negotiated_sdp_stream with local sdp error %d\n", buf_size); - } - buf_size = pjmedia_sdp_print(remote, szSdpBuffer, 2048); - if (buf_size >= 0) { - szSdpBuffer[buf_size] = '\0'; - replace_newline(szSdpBuffer, '#'); - ast_debug(3, "websocket apply_negotiated_sdp_stream with remote sdp %s\n", szSdpBuffer); - } else { - ast_debug(3, "websocket apply_negotiated_sdp_stream with remote sdp error %d (remote %d)\n", buf_size, remote ? 1 : 0); - } - ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host)); /* Ensure that the address provided is valid */ @@ -829,14 +586,14 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, if (!sip_session_media->websocket_text) { sip_session_media->websocket_text = ast_calloc(1, sizeof(*sip_session_media->websocket_text)); if (!sip_session_media->websocket_text) { - SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create rtp\n"); + SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create websocket text\n"); } ast_debug(3, "websocket apply_negotiated_sdp_stream created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); } else { ast_debug(3, "websocket apply_negotiated_sdp_stream already created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); } - if (sip_session->channel && sip_session_media->websocket_text->fd != 0xDEAD) { + if (sip_session->inv_session && sip_session_media->websocket_text->fd != 0xDEAD) { sip_session_media->websocket_text->fd = 0xDEAD; ws_session = ast_calloc(1, sizeof(*ws_session)); @@ -844,17 +601,24 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); } - //strcpy(ws_session->channel_name, "channel_demo"); - //strcpy(ws_session->channel_name, ast_channel_name(sip_session->channel)); - strcpy(ws_session->channel_name, sip_session->inv_session->obj_name + strlen("inv0x")); + strcpy(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x")); AST_LIST_LOCK(&websocket_session_list); AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); AST_LIST_UNLOCK(&websocket_session_list); - ast_debug(3, "websocket apply_negotiated_sdp_stream linked with sip session channel name %s\n", ast_channel_name(sip_session->channel)); + ast_debug(3, "websocket apply_negotiated_sdp_stream linked with sip session invite id %s\n", sip_session->inv_session->obj_name); } else { - ast_log(LOG_ERROR, "websocket apply_negotiated_sdp_stream already linked with sip session channel name %s\n", ast_channel_name(sip_session->channel)); + AST_LIST_LOCK(&websocket_session_list); + AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) + { + if (strcmp(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x")) == 0) { + break; + } + } + AST_LIST_UNLOCK(&websocket_session_list); + + ast_log(LOG_ERROR, "websocket apply_negotiated_sdp_stream already linked with sip session invite id %s\n", sip_session->inv_session->obj_name); } ast_sip_session_media_set_write_callback(sip_session, sip_session_media, media_sip_session_websocket_text_write_callback); @@ -884,9 +648,20 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, static void stream_destroy(struct ast_sip_session_media *sip_session_media) { if (sip_session_media->websocket_text) { + if (sip_session_media->websocket_text->pipe_fds[0] > 0) { + close(sip_session_media->websocket_text->pipe_fds[0]); + sip_session_media->websocket_text->pipe_fds[0] = -1; + } + if (sip_session_media->websocket_text->pipe_fds[1] > 0) { + close(sip_session_media->websocket_text->pipe_fds[1]); + sip_session_media->websocket_text->pipe_fds[1] = -1; + } + ast_websocket_text_destroy(sip_session_media->websocket_text); } + sip_session_media->websocket_text = NULL; + } /*! \brief SDP handler for 'application' media stream */ @@ -928,7 +703,7 @@ static void add_variable_to_list(struct ast_variable **head, const char *name, c ast_log(LOG_NOTICE, "Variable added: %s=%s\n", name, value); } -static int mywebsocket_uri_cb(struct ast_tcptls_session_instance *ser +static int websocket_text_t140_uri_cb(struct ast_tcptls_session_instance *ser , const struct ast_http_uri *urih , const char *uri , enum ast_http_method method @@ -936,15 +711,15 @@ static int mywebsocket_uri_cb(struct ast_tcptls_session_instance *ser , struct ast_variable *headers ) { - ast_debug(1, "Entering WebSocket t140 loop method %s uri %s\n", ast_get_http_method(method), uri); -/**/ struct websocket_session *ws_session = NULL; + ast_debug(1, "Entering webSocket text t140 loop method %s uri %s\n", ast_get_http_method(method), uri); + // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. AST_LIST_LOCK(&websocket_session_list); AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) { - if (!strcmp(ws_session->channel_name, uri)) { + if (!strcmp(ws_session->id, uri)) { break; } } @@ -954,15 +729,15 @@ static int mywebsocket_uri_cb(struct ast_tcptls_session_instance *ser ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL."); return 0; } -/**/ + add_variable_to_list(&get_params, "uri", uri); return ast_websocket_uri_cb(ser, urih, uri, method, get_params, headers); } -static struct ast_http_uri mywebsocketuri = { - .callback = mywebsocket_uri_cb, - .description = "Asterisk my HTTP WebSocket", +static struct ast_http_uri websocket_text_t140_uri = { + .callback = websocket_text_t140_uri_cb, + .description = "Asterisk HTTP WebSocket text t140", .uri = "ws_text", .has_subtree = 1, .data = NULL, @@ -970,12 +745,12 @@ static struct ast_http_uri mywebsocketuri = { }; /*! \brief Simple echo implementation which echoes received text and binary frames */ -static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct ast_variable *parameters, struct ast_variable *headers) +static void websocket_text_t140_callback(struct ast_websocket *websocket, struct ast_variable *parameters, struct ast_variable *headers) { int res; struct websocket_session *ws_session = NULL; - ast_debug(1, "Entering WebSocket t140 loop %s, addr remote %s and local %s\n" + ast_debug(1, "Entering webSocket text t140 loop %s, addr remote %s and local %s\n" , ast_websocket_session_id(websocket) , ast_sockaddr_stringify(ast_websocket_remote_address(websocket)) , ast_sockaddr_stringify(ast_websocket_local_address(websocket)) @@ -983,27 +758,19 @@ static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct as struct ast_variable *i; for (i = parameters; i; i = i->next) { - ast_debug(1, "Entering WebSocket t140 loop %s parameters %s = %s\n", ast_websocket_session_id(websocket), i->name, i->value); if (!strcmp(i->name, "uri")) { - // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. AST_LIST_LOCK(&websocket_session_list); AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) { - if (!strcmp(ws_session->channel_name, i->value)) { + if (!strcmp(ws_session->id, i->value)) { break; } } AST_LIST_UNLOCK(&websocket_session_list); } } - for (i = headers; i; i = i->next) { - ast_debug(1, "Entering WebSocket t140 loop %s headers %s = %s\n", ast_websocket_session_id(websocket), i->name, i->value); - } - - //ws_session = ast_calloc(1, sizeof(*ws_session)); if (!ws_session) { ast_log(LOG_ERROR, "Failed to find uri or allocate memory for WebSocket client\n"); - //ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL."); goto end; } @@ -1016,8 +783,6 @@ static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct as ws_session->websocket = websocket; } - ast_log(LOG_DEBUG, "media_sip_session_websocket_text_read_callback fd %d with channel name: %s\n", ast_websocket_fd(ws_session->websocket), ws_session->channel_name); - while ((res = ast_websocket_wait_for_input(websocket, -1)) > 0) { char *payload; uint64_t payload_len; @@ -1031,53 +796,24 @@ static void mywebsocket_echo_callback(struct ast_websocket *websocket, struct as } if (opcode == AST_WEBSOCKET_OPCODE_TEXT && payload_len > 0) { - // On empile une ast_frame avec le texte recu dans une liste - struct ast_frame f_in; - memset(&f_in, 0, sizeof(f_in)); - f_in.frametype = AST_FRAME_TEXT; - //f_in.subclass.format = ast_format_t140_red; - f_in.subclass.format = ast_format_t140; - //f_in.subclass.format = ast_format_none; - f_in.datalen = payload_len; - f_in.data.ptr = (void *)payload; - -// push_frame(ws_session, &f_in); write(ws_session->fd, payload, payload_len); - } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) { break; } else { - ast_debug(1, "Ignored WebSocket opcode %u\n", opcode); + ast_debug(1, "Ignored webSocket text t140 opcode %u\n", opcode); } } end: - ast_debug(1, "Exiting WebSocket t140 loop %s\n", ast_websocket_session_id(websocket)); + ast_debug(1, "Exiting webSocket text t140 loop %s\n", ast_websocket_session_id(websocket)); AST_LIST_LOCK(&websocket_session_list); AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_list, ws_session, entry) { if (ws_session->websocket == websocket) { - struct websocket_frame *frame_wrapper = NULL; - //struct ast_frame *frame = NULL; - AST_LIST_REMOVE_CURRENT(entry); ast_websocket_unref(ws_session->websocket); - AST_LIST_TRAVERSE_SAFE_BEGIN(&ws_session->frame_stack, frame_wrapper, entry) - { - ast_frfree(frame_wrapper->frame); - ast_free(frame_wrapper); - } - AST_LIST_TRAVERSE_SAFE_END; - /* - frame = pop_frame(ws_session); - while (frame != NULL) { - ast_frfree(frame); - frame = pop_frame(ws_session); - } - */ - ast_free(ws_session); break; } @@ -1094,36 +830,18 @@ static int unload_module(void) ast_sip_session_unregister_sdp_handler(&text_sdp_handler, STR_TEXT); ast_sip_session_unregister_supplement(&websocket_text_supplement); - ast_websocket_server_remove_protocol(mywebsocketuri.data, "t140", mywebsocket_echo_callback); - ast_http_uri_unlink(&mywebsocketuri); - ao2_ref(mywebsocketuri.data, -1); - mywebsocketuri.data = NULL; + ast_websocket_server_remove_protocol(websocket_text_t140_uri.data, "t140", websocket_text_t140_callback); + ast_http_uri_unlink(&websocket_text_t140_uri); + ao2_ref(websocket_text_t140_uri.data, -1); + websocket_text_t140_uri.data = NULL; AST_LIST_LOCK(&websocket_session_list); AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_list, ws_session, entry) { - struct websocket_frame *frame_wrapper = NULL; - //struct ast_frame *frame = NULL; - AST_LIST_REMOVE_CURRENT(entry); if (ws_session->websocket) { ast_websocket_unref(ws_session->websocket); } - /**/ - AST_LIST_TRAVERSE_SAFE_BEGIN(&ws_session->frame_stack, frame_wrapper, entry) - { - ast_frfree(frame_wrapper->frame); - ast_free(frame_wrapper); - } - AST_LIST_TRAVERSE_SAFE_END; - /**/ - /* - frame = pop_frame(ws_session); - while (frame != NULL) { - ast_frfree(frame); - frame = pop_frame(ws_session); - } - */ ast_free(ws_session); } @@ -1145,18 +863,12 @@ static int unload_module(void) */ static int load_module(void) { - if (ast_check_ipv6()) { - ast_sockaddr_parse(&address_ws_text, "::", 0); - } else { - ast_sockaddr_parse(&address_ws_text, "0.0.0.0", 0); - } - - mywebsocketuri.data = ast_websocket_server_create(); - if (!mywebsocketuri.data) { + websocket_text_t140_uri.data = ast_websocket_server_create(); + if (!websocket_text_t140_uri.data) { return AST_MODULE_LOAD_DECLINE; } - ast_http_uri_link(&mywebsocketuri); - ast_websocket_server_add_protocol(mywebsocketuri.data, "t140", mywebsocket_echo_callback); + ast_http_uri_link(&websocket_text_t140_uri); + ast_websocket_server_add_protocol(websocket_text_t140_uri.data, "t140", websocket_text_t140_callback); ast_sip_session_register_supplement(&websocket_text_supplement); From 65e511acb985a233e3810308c8d5a64c3620ebed Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Fri, 12 Jul 2024 16:23:31 +0200 Subject: [PATCH 22/31] IPv6 flag removed in configuration --- include/asterisk/res_pjsip.h | 2 -- res/res_pjsip/pjsip_configuration.c | 1 - 2 files changed, 3 deletions(-) diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h index fb46b47954b..0910a7db84e 100644 --- a/include/asterisk/res_pjsip.h +++ b/include/asterisk/res_pjsip.h @@ -905,8 +905,6 @@ struct ast_sip_websocket_text_configuration { /*! Whether websocket text support is enabled or not */ unsigned int enabled; - /*! Whether to use IPv6 for websocket text or not */ - unsigned int ipv6; }; /*! diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c index fb131c76273..77033884525 100644 --- a/res/res_pjsip/pjsip_configuration.c +++ b/res/res_pjsip/pjsip_configuration.c @@ -2219,7 +2219,6 @@ int ast_res_pjsip_initialize_configuration(void) ast_sorcery_object_field_register(sip_sorcery, "endpoint", "device_state_busy_at", "0", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, devicestate_busy_at)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "ws_text", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.websocket_text_configuration.enabled)); - ast_sorcery_object_field_register(sip_sorcery, "endpoint", "ws_text_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.websocket_text_configuration.ipv6)); ast_sorcery_object_field_register(sip_sorcery, "endpoint", "t38_udptl", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.t38.enabled)); ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "t38_udptl_ec", "none", t38udptl_ec_handler, t38udptl_ec_to_str, NULL, 0, 0); From 24ee4a0710ee003ad60abf3caff00abc9afafb0c Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Fri, 12 Jul 2024 16:24:42 +0200 Subject: [PATCH 23/31] Consolidation of structures --- include/asterisk/res_pjsip_session.h | 6 +- res/res_pjsip_websocket_text.c | 289 ++++++++++++--------------- 2 files changed, 133 insertions(+), 162 deletions(-) diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h index 191c3622d84..2afe5622512 100644 --- a/include/asterisk/res_pjsip_session.h +++ b/include/asterisk/res_pjsip_session.h @@ -51,7 +51,7 @@ struct pjmedia_sdp_media; struct pjmedia_sdp_session; struct ast_dsp; struct ast_udptl; -struct ast_websocket_text; +struct ast_websocket_session_text; /*! \brief T.38 states for a session */ enum ast_sip_session_t38state { @@ -129,8 +129,8 @@ struct ast_sip_session_media { /*! \brief Stream name */ char *stream_name; - /*! \brief Websocket text instance itself */ - struct ast_websocket_text *websocket_text; + /*! \brief Websocket session text instance itself */ + struct ast_websocket_session_text *websocket_session_text; }; /*! diff --git a/res/res_pjsip_websocket_text.c b/res/res_pjsip_websocket_text.c index 4986d4e2b2d..fadc40ed688 100644 --- a/res/res_pjsip_websocket_text.c +++ b/res/res_pjsip_websocket_text.c @@ -56,23 +56,16 @@ static const char STR_TEXT[] = "text"; /*! \brief Websocket Text information */ -struct ast_websocket_text -{ - int fd; - int pipe_fds[2]; -}; - - -struct websocket_session +struct ast_websocket_session_text { char id[256]; - int fd; + int pipe_fds[2]; struct ast_websocket *websocket; - AST_LIST_ENTRY(websocket_session) entry; + AST_LIST_ENTRY(ast_websocket_session_text) entry; }; -static AST_LIST_HEAD(websocket_session_list, websocket_session) websocket_session_list; +static AST_LIST_HEAD(websocket_session_text_list, ast_websocket_session_text) websocket_session_text_list; static void replace_newline(char *buffer, char replacement) { @@ -112,18 +105,18 @@ static int get_websocket_tls_port(void) } /*! \brief Supplement for adding framehook to sip_session channel */ -static struct ast_sip_session_supplement websocket_text_supplement = { +static struct ast_sip_session_supplement websocket_session_text_supplement = { .method = "INVITE", .priority = AST_SIP_SUPPLEMENT_PRIORITY_CHANNEL + 1, }; -static int ast_websocket_text_fd(const struct ast_websocket_text *ws_text) +static int ast_websocket_session_text_fd(const struct ast_websocket_session_text *ws_text) { return ws_text->pipe_fds[0]; } /*! \brief Destructor for T.38 state information */ -static void ast_websocket_text_destroy(void *obj) +static void ast_websocket_session_text_destroy(void *obj) { ast_free(obj); } @@ -192,13 +185,6 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *sip_session, SCOPE_EXIT_RTN_VALUE(0, "Declining: provided host is invalid\n"); } - /* Check the address family to make sure it matches configured */ - if ((ast_sockaddr_is_ipv6(addrs) && !sip_session->endpoint->media.websocket_text_configuration.ipv6) || - (ast_sockaddr_is_ipv4(addrs) && sip_session->endpoint->media.websocket_text_configuration.ipv6)) { - /* The address does not match configured */ - SCOPE_EXIT_RTN_VALUE(0, "Declining: provided host does not match configured address family\n"); - } - /* If no type formats have been configured reject this stream */ if (!ast_format_cap_has_type(sip_session->endpoint->media.codecs, sip_session_media->type)) { ast_debug(3, "Endpoint has no codecs for media type '%s', declining stream\n", @@ -212,35 +198,25 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *sip_session, SCOPE_EXIT_RTN_VALUE(0, "Incompatible transport\n"); } - struct websocket_session *ws_session = NULL; + if (sip_session->inv_session) { + if (!sip_session_media->websocket_session_text) { + sip_session_media->websocket_session_text = ast_calloc(1, sizeof(*sip_session_media->websocket_session_text)); + if (!sip_session_media->websocket_session_text) { + SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); + } - if (!sip_session_media->websocket_text) { - sip_session_media->websocket_text = ast_calloc(1, sizeof(*sip_session_media->websocket_text)); - if (!sip_session_media->websocket_text) { - SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create websocket text\n"); - } - ast_debug(3, "websocket negotiate_incoming_sdp_stream created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); - } else { - ast_debug(3, "websocket negotiate_incoming_sdp_stream already created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); - } + strcpy(sip_session_media->websocket_session_text->id, sip_session->inv_session->obj_name + strlen("inv0x")); - if (sip_session->inv_session && sip_session_media->websocket_text->fd != 0xDEAD) { - sip_session_media->websocket_text->fd = 0xDEAD; + AST_LIST_LOCK(&websocket_session_text_list); + AST_LIST_INSERT_HEAD(&websocket_session_text_list, sip_session_media->websocket_session_text, entry); + AST_LIST_UNLOCK(&websocket_session_text_list); - ws_session = ast_calloc(1, sizeof(*ws_session)); - if (!ws_session) { - SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); + ast_debug(3, "websocket negotiate_incoming_sdp_stream created '%s' with id %s\n", ast_codec_media_type2str(sip_session_media->type), sip_session_media->websocket_session_text->id); + } else { + ast_debug(3, "websocket negotiate_incoming_sdp_stream already created '%s' with id %s\n", ast_codec_media_type2str(sip_session_media->type), sip_session_media->websocket_session_text->id); } - - strcpy(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x")); - - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); - AST_LIST_UNLOCK(&websocket_session_list); - - ast_debug(3, "websocket negotiate_incoming_sdp_stream linked with sip session invite id %s\n", sip_session->inv_session->obj_name); } else { - ast_debug(3, "websocket negotiate_incoming_sdp_stream without sip session channel\n"); + SCOPE_EXIT_RTN_VALUE(-1, "Failed to generate id for websocket session\n"); } joint = set_incoming_call_offer_cap(sip_session, sip_session_media, stream); @@ -266,7 +242,6 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc pjmedia_sdp_media *media; const char *hostip = NULL; struct ast_sockaddr addr; - struct websocket_session *ws_session = NULL; char tmp[512]; pj_str_t stmp; @@ -277,33 +252,25 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc SCOPE_EXIT_RTN_VALUE(1, "Not creating outgoing SDP stream: websocket text not enabled\n"); } - if (!sip_session_media->websocket_text) { - sip_session_media->websocket_text = ast_calloc(1, sizeof(*sip_session_media->websocket_text)); - if (!sip_session_media->websocket_text) { - SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create websocket text\n"); - } - ast_debug(3, "websocket create_outgoing_sdp_stream created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); - } else { - ast_debug(3, "websocket create_outgoing_sdp_stream already created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); - } - - if (sip_session->inv_session && sip_session_media->websocket_text->fd != 0xDEAD) { - sip_session_media->websocket_text->fd = 0xDEAD; - - ws_session = ast_calloc(1, sizeof(*ws_session)); - if (!ws_session) { - SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); - } + if (sip_session->inv_session) { + if (!sip_session_media->websocket_session_text) { + sip_session_media->websocket_session_text = ast_calloc(1, sizeof(*sip_session_media->websocket_session_text)); + if (!sip_session_media->websocket_session_text) { + SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); + } - strcpy(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x")); + strcpy(sip_session_media->websocket_session_text->id, sip_session->inv_session->obj_name + strlen("inv0x")); - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); - AST_LIST_UNLOCK(&websocket_session_list); + AST_LIST_LOCK(&websocket_session_text_list); + AST_LIST_INSERT_HEAD(&websocket_session_text_list, sip_session_media->websocket_session_text, entry); + AST_LIST_UNLOCK(&websocket_session_text_list); - ast_debug(3, "websocket create_outgoing_sdp_stream linked with sip session invite id %s\n", sip_session->inv_session->obj_name); + ast_debug(3, "websocket create_outgoing_sdp_stream created '%s' with id %s\n", ast_codec_media_type2str(sip_session_media->type), sip_session_media->websocket_session_text->id); + } else { + ast_debug(3, "websocket create_outgoing_sdp_stream already created '%s' with id %s\n", ast_codec_media_type2str(sip_session_media->type), sip_session_media->websocket_session_text->id); + } } else { - ast_debug(3, "websocket create_outgoing_sdp_stream without sip session channel\n"); + SCOPE_EXIT_RTN_VALUE(-1, "Failed to generate id for websocket session\n"); } pjmedia_sdp_attr *attr; @@ -341,11 +308,11 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc SCOPE_EXIT_RTN_VALUE(1, "RC: 1\n"); } -static struct ast_frame *media_sip_session_websocket_text_read_callback(struct ast_sip_session *sip_session, struct ast_sip_session_media *sip_session_media) +static struct ast_frame *media_sip_session_websocket_session_text_read_callback(struct ast_sip_session *sip_session, struct ast_sip_session_media *sip_session_media) { struct ast_frame *frame = NULL; - if (!sip_session_media->websocket_text) { + if (!sip_session_media->websocket_session_text) { return &ast_null_frame; } @@ -371,15 +338,15 @@ static struct ast_frame *media_sip_session_websocket_text_read_callback(struct a } } - bytes_read = read(ast_websocket_text_fd(sip_session_media->websocket_text), final_buffer + total_length, buffer_capacity - total_length); + bytes_read = read(ast_websocket_session_text_fd(sip_session_media->websocket_session_text), final_buffer + total_length, buffer_capacity - total_length); if (bytes_read > 0) { - ast_log(LOG_DEBUG, "websocket text read length %ld\n", bytes_read); + ast_debug(3, "Reading websocket text raw, length %" PRIu64 "\n", bytes_read); total_length += bytes_read; if (bytes_read < (ssize_t)(buffer_capacity - total_length)) { done = 1; } } else if (bytes_read == 0) { - ast_log(LOG_WARNING, "websocket text read EOF\n"); + ast_log(LOG_WARNING, "Reading websocket text EOF\n"); done = 1; } else { if (errno == EAGAIN || errno == EWOULDBLOCK) { @@ -393,10 +360,10 @@ static struct ast_frame *media_sip_session_websocket_text_read_callback(struct a } if (total_length > 0) { - struct websocket_session *ws_session = NULL; + struct ast_websocket_session_text *ws_session = NULL; - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) + AST_LIST_LOCK(&websocket_session_text_list); + AST_LIST_TRAVERSE(&websocket_session_text_list, ws_session, entry) { if (strcmp(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x")) == 0) { struct ast_frame f_in; @@ -413,7 +380,7 @@ static struct ast_frame *media_sip_session_websocket_text_read_callback(struct a break; } } - AST_LIST_UNLOCK(&websocket_session_list); + AST_LIST_UNLOCK(&websocket_session_text_list); ast_free(final_buffer); } @@ -428,26 +395,26 @@ static struct ast_frame *media_sip_session_websocket_text_read_callback(struct a return frame; } -static int media_sip_session_websocket_text_write_callback(struct ast_sip_session *sip_session, struct ast_sip_session_media *sip_session_media, struct ast_frame *frame) +static int media_sip_session_websocket_session_text_write_callback(struct ast_sip_session *sip_session, struct ast_sip_session_media *sip_session_media, struct ast_frame *frame) { - if (!sip_session_media->websocket_text) { + if (!sip_session_media->websocket_session_text) { return 0; } if (frame && frame->frametype == AST_FRAME_TEXT) { struct ast_websocket *websocket = NULL; - struct websocket_session *ws_session = NULL; + struct ast_websocket_session_text *ws_session = NULL; // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) + AST_LIST_LOCK(&websocket_session_text_list); + AST_LIST_TRAVERSE(&websocket_session_text_list, ws_session, entry) { if (strcmp(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x")) == 0) { websocket = ws_session->websocket; break; } } - AST_LIST_UNLOCK(&websocket_session_list); + AST_LIST_UNLOCK(&websocket_session_text_list); if (frame->datalen > 0) { char *text = frame->data.ptr; @@ -581,59 +548,38 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, SCOPE_EXIT_RTN_VALUE(-1, "Not applying negotiated SDP stream: failed to resolve remote stream host\n"); } - struct websocket_session *ws_session = NULL; + if (sip_session->inv_session) { + if (!sip_session_media->websocket_session_text) { + sip_session_media->websocket_session_text = ast_calloc(1, sizeof(*sip_session_media->websocket_session_text)); + if (!sip_session_media->websocket_session_text) { + SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); + } - if (!sip_session_media->websocket_text) { - sip_session_media->websocket_text = ast_calloc(1, sizeof(*sip_session_media->websocket_text)); - if (!sip_session_media->websocket_text) { - SCOPE_EXIT_RTN_VALUE(-1, "Couldn't create websocket text\n"); - } - ast_debug(3, "websocket apply_negotiated_sdp_stream created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); - } else { - ast_debug(3, "websocket apply_negotiated_sdp_stream already created '%s'\n", ast_codec_media_type2str(sip_session_media->type)); - } + strcpy(sip_session_media->websocket_session_text->id, sip_session->inv_session->obj_name + strlen("inv0x")); - if (sip_session->inv_session && sip_session_media->websocket_text->fd != 0xDEAD) { - sip_session_media->websocket_text->fd = 0xDEAD; + AST_LIST_LOCK(&websocket_session_text_list); + AST_LIST_INSERT_HEAD(&websocket_session_text_list, sip_session_media->websocket_session_text, entry); + AST_LIST_UNLOCK(&websocket_session_text_list); - ws_session = ast_calloc(1, sizeof(*ws_session)); - if (!ws_session) { - SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for websocket session\n"); + ast_debug(3, "websocket apply_negotiated_sdp_stream created '%s' with id %s\n", ast_codec_media_type2str(sip_session_media->type), sip_session_media->websocket_session_text->id); + } else { + ast_debug(3, "websocket apply_negotiated_sdp_stream already created '%s' with id %s\n", ast_codec_media_type2str(sip_session_media->type), sip_session_media->websocket_session_text->id); } - - strcpy(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x")); - - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_INSERT_HEAD(&websocket_session_list, ws_session, entry); - AST_LIST_UNLOCK(&websocket_session_list); - - ast_debug(3, "websocket apply_negotiated_sdp_stream linked with sip session invite id %s\n", sip_session->inv_session->obj_name); } else { - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) - { - if (strcmp(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x")) == 0) { - break; - } - } - AST_LIST_UNLOCK(&websocket_session_list); - - ast_log(LOG_ERROR, "websocket apply_negotiated_sdp_stream already linked with sip session invite id %s\n", sip_session->inv_session->obj_name); + SCOPE_EXIT_RTN_VALUE(-1, "Failed to generate id for websocket session\n"); } - ast_sip_session_media_set_write_callback(sip_session, sip_session_media, media_sip_session_websocket_text_write_callback); + ast_sip_session_media_set_write_callback(sip_session, sip_session_media, media_sip_session_websocket_session_text_write_callback); - if (pipe(sip_session_media->websocket_text->pipe_fds) == -1 || sip_session_media->websocket_text == NULL || ws_session == NULL) { + if (pipe(sip_session_media->websocket_session_text->pipe_fds) == -1 || sip_session_media->websocket_session_text == NULL) { SCOPE_EXIT_RTN_VALUE(-1, "pipe create to exchange frames failed\n"); } else { - ws_session->fd = sip_session_media->websocket_text->pipe_fds[1]; - - ast_fd_set_flags(ast_websocket_text_fd(sip_session_media->websocket_text), SOCK_NONBLOCK); + ast_fd_set_flags(ast_websocket_session_text_fd(sip_session_media->websocket_session_text), SOCK_NONBLOCK); ast_sip_session_media_add_read_callback(sip_session , sip_session_media - , ast_websocket_text_fd(sip_session_media->websocket_text) - , media_sip_session_websocket_text_read_callback + , ast_websocket_session_text_fd(sip_session_media->websocket_session_text) + , media_sip_session_websocket_session_text_read_callback ); } @@ -647,21 +593,41 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, /*! \brief Function which destroys the Websocket Text instance when sip_session ends */ static void stream_destroy(struct ast_sip_session_media *sip_session_media) { - if (sip_session_media->websocket_text) { - if (sip_session_media->websocket_text->pipe_fds[0] > 0) { - close(sip_session_media->websocket_text->pipe_fds[0]); - sip_session_media->websocket_text->pipe_fds[0] = -1; + if (sip_session_media->websocket_session_text) { + struct ast_websocket_session_text *ws_session = NULL; + + AST_LIST_LOCK(&websocket_session_text_list); + AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_text_list, ws_session, entry) + { + if (sip_session_media->websocket_session_text == ws_session) { + AST_LIST_REMOVE_CURRENT(entry); + + if (ws_session->websocket) { + ast_debug(3, "websocket text unref websocket with id %s\n", sip_session_media->websocket_session_text->id); + + ast_websocket_unref(ws_session->websocket); + ws_session->websocket = NULL; + } + ast_debug(3, "websocket text deleted with id %s\n", sip_session_media->websocket_session_text->id); + break; + } } - if (sip_session_media->websocket_text->pipe_fds[1] > 0) { - close(sip_session_media->websocket_text->pipe_fds[1]); - sip_session_media->websocket_text->pipe_fds[1] = -1; + AST_LIST_TRAVERSE_SAFE_END; + AST_LIST_UNLOCK(&websocket_session_text_list); + + if (sip_session_media->websocket_session_text->pipe_fds[0] > 0) { + close(sip_session_media->websocket_session_text->pipe_fds[0]); + sip_session_media->websocket_session_text->pipe_fds[0] = -1; + } + if (sip_session_media->websocket_session_text->pipe_fds[1] > 0) { + close(sip_session_media->websocket_session_text->pipe_fds[1]); + sip_session_media->websocket_session_text->pipe_fds[1] = -1; } - ast_websocket_text_destroy(sip_session_media->websocket_text); + ast_websocket_session_text_destroy(sip_session_media->websocket_session_text); } - sip_session_media->websocket_text = NULL; - + sip_session_media->websocket_session_text = NULL; } /*! \brief SDP handler for 'application' media stream */ @@ -709,21 +675,21 @@ static int websocket_text_t140_uri_cb(struct ast_tcptls_session_instance *ser , enum ast_http_method method , struct ast_variable *get_params , struct ast_variable *headers -) + ) { - struct websocket_session *ws_session = NULL; + struct ast_websocket_session_text *ws_session = NULL; ast_debug(1, "Entering webSocket text t140 loop method %s uri %s\n", ast_get_http_method(method), uri); // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) + AST_LIST_LOCK(&websocket_session_text_list); + AST_LIST_TRAVERSE(&websocket_session_text_list, ws_session, entry) { if (!strcmp(ws_session->id, uri)) { break; } } - AST_LIST_UNLOCK(&websocket_session_list); + AST_LIST_UNLOCK(&websocket_session_text_list); if (!ws_session) { ast_http_error(ser, 403, "Access Denied", "You do not have permission to access the requested URL."); @@ -745,10 +711,10 @@ static struct ast_http_uri websocket_text_t140_uri = { }; /*! \brief Simple echo implementation which echoes received text and binary frames */ -static void websocket_text_t140_callback(struct ast_websocket *websocket, struct ast_variable *parameters, struct ast_variable *headers) +static void websocket_session_text_t140_callback(struct ast_websocket *websocket, struct ast_variable *parameters, struct ast_variable *headers) { int res; - struct websocket_session *ws_session = NULL; + struct ast_websocket_session_text *ws_session = NULL; ast_debug(1, "Entering webSocket text t140 loop %s, addr remote %s and local %s\n" , ast_websocket_session_id(websocket) @@ -759,14 +725,14 @@ static void websocket_text_t140_callback(struct ast_websocket *websocket, struct struct ast_variable *i; for (i = parameters; i; i = i->next) { if (!strcmp(i->name, "uri")) { - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_TRAVERSE(&websocket_session_list, ws_session, entry) + AST_LIST_LOCK(&websocket_session_text_list); + AST_LIST_TRAVERSE(&websocket_session_text_list, ws_session, entry) { if (!strcmp(ws_session->id, i->value)) { break; } } - AST_LIST_UNLOCK(&websocket_session_list); + AST_LIST_UNLOCK(&websocket_session_text_list); } } if (!ws_session) { @@ -796,7 +762,7 @@ static void websocket_text_t140_callback(struct ast_websocket *websocket, struct } if (opcode == AST_WEBSOCKET_OPCODE_TEXT && payload_len > 0) { - write(ws_session->fd, payload, payload_len); + write(ws_session->pipe_fds[1], payload, payload_len); } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) { break; } else { @@ -807,46 +773,51 @@ static void websocket_text_t140_callback(struct ast_websocket *websocket, struct end: ast_debug(1, "Exiting webSocket text t140 loop %s\n", ast_websocket_session_id(websocket)); - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_list, ws_session, entry) + AST_LIST_LOCK(&websocket_session_text_list); + AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_text_list, ws_session, entry) { if (ws_session->websocket == websocket) { - AST_LIST_REMOVE_CURRENT(entry); - ast_websocket_unref(ws_session->websocket); + ast_debug(3, "websocket text unref websocket (callback) with id %s\n", ws_session->id); - ast_free(ws_session); + ast_websocket_unref(ws_session->websocket); + ws_session->websocket = NULL; break; } } AST_LIST_TRAVERSE_SAFE_END; - AST_LIST_UNLOCK(&websocket_session_list); + AST_LIST_UNLOCK(&websocket_session_text_list); } /*! \brief Unloads the SIP Websocket Text module from Asterisk */ static int unload_module(void) { - struct websocket_session *ws_session = NULL; + struct ast_websocket_session_text *ws_session = NULL; ast_sip_session_unregister_sdp_handler(&text_sdp_handler, STR_TEXT); - ast_sip_session_unregister_supplement(&websocket_text_supplement); + ast_sip_session_unregister_supplement(&websocket_session_text_supplement); - ast_websocket_server_remove_protocol(websocket_text_t140_uri.data, "t140", websocket_text_t140_callback); + ast_websocket_server_remove_protocol(websocket_text_t140_uri.data, "t140", websocket_session_text_t140_callback); ast_http_uri_unlink(&websocket_text_t140_uri); ao2_ref(websocket_text_t140_uri.data, -1); websocket_text_t140_uri.data = NULL; - AST_LIST_LOCK(&websocket_session_list); - AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_list, ws_session, entry) + AST_LIST_LOCK(&websocket_session_text_list); + AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_text_list, ws_session, entry) { AST_LIST_REMOVE_CURRENT(entry); if (ws_session->websocket) { + ast_debug(3, "websocket text unref websocket (unload) with id %s\n", ws_session->id); + ast_websocket_unref(ws_session->websocket); + ws_session->websocket = NULL; } + ast_debug(3, "websocket text deleted (unload) with id %s\n", ws_session->id); + ast_free(ws_session); } AST_LIST_TRAVERSE_SAFE_END; - AST_LIST_UNLOCK(&websocket_session_list); + AST_LIST_UNLOCK(&websocket_session_text_list); return 0; } @@ -868,9 +839,9 @@ static int load_module(void) return AST_MODULE_LOAD_DECLINE; } ast_http_uri_link(&websocket_text_t140_uri); - ast_websocket_server_add_protocol(websocket_text_t140_uri.data, "t140", websocket_text_t140_callback); + ast_websocket_server_add_protocol(websocket_text_t140_uri.data, "t140", websocket_session_text_t140_callback); - ast_sip_session_register_supplement(&websocket_text_supplement); + ast_sip_session_register_supplement(&websocket_session_text_supplement); if (ast_sip_session_register_sdp_handler(&text_sdp_handler, STR_TEXT)) { ast_log(LOG_ERROR, "Unable to register SDP handler for %s stream type\n", STR_TEXT); From b0e827538b77b3809ffa843f7d414a2dac1fdda8 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Thu, 18 Jul 2024 11:23:06 +0200 Subject: [PATCH 24/31] For the text in the dialplan --- main/channel.c | 1 + 1 file changed, 1 insertion(+) diff --git a/main/channel.c b/main/channel.c index 796b07a259d..b305282eaf6 100644 --- a/main/channel.c +++ b/main/channel.c @@ -4818,6 +4818,7 @@ int ast_sendtext_data(struct ast_channel *chan, struct ast_msg_data *msg) f.datalen = body_len; f.mallocd = AST_MALLOCD_DATA; f.data.ptr = ast_strdup(body); + f.stream_num = ast_stream_get_position(ast_channel_get_default_stream(chan, AST_MEDIA_TYPE_TEXT)); if (f.data.ptr) { res = ast_channel_tech(chan)->write_text(chan, &f); } else { From 35c177f3a2100bf559266b5f265e01e0731b75d6 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Thu, 22 Aug 2024 14:19:38 +0200 Subject: [PATCH 25/31] Fix pour avoir le bon nombre de generations RED --- res/res_pjsip_sdp_rtp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index 2c5448e0b3f..36e5dc14c50 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -384,7 +384,7 @@ static void get_codecs(struct ast_sip_session *session, const struct pjmedia_sdp red_cp = strtok_r(NULL, "/", &rest); } - if (++red_num_gen > 0) { + if (red_num_gen > 0) { ast_log(AST_LOG_NOTICE, "T.140/RED enabled (pt=%d) with %d generations\n", num, red_num_gen); session->endpoint->media.red_enabled = 1; ast_rtp_red_init(session_media->rtp, 300, red_data_pt, red_num_gen); From 8a9cbc99d2a2fef1f31d50f99945fa47e27b53cf Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Thu, 22 Aug 2024 14:20:22 +0200 Subject: [PATCH 26/31] Doc du nouveau parametre ws_text d'un endpoint pjsip --- res/res_pjsip/pjsip_config.xml | 7 +++++-- res/res_pjsip/pjsip_manager.xml | 3 +++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/res/res_pjsip/pjsip_config.xml b/res/res_pjsip/pjsip_config.xml index d512f1f02db..dcfaee6cb4a 100644 --- a/res/res_pjsip/pjsip_config.xml +++ b/res/res_pjsip/pjsip_config.xml @@ -998,7 +998,7 @@ DSCP TOS bits for text streams - See https://wiki.asterisk.org/wiki/display/AST/IP+Quality+of+Service for more information about QoS settings + See https://docs.asterisk.org/Configuration/Channel-Drivers/IP-Quality-of-Service for more information about QoS settings @@ -1016,9 +1016,12 @@ Priority for text streams - See https://wiki.asterisk.org/wiki/display/AST/IP+Quality+of+Service for more information about QoS settings + See https://docs.asterisk.org/Configuration/Channel-Drivers/IP-Quality-of-Service for more information about QoS settings + + Allow use of websocket text for WebRTC traffic + Determines if endpoint is allowed to initiate subscriptions with Asterisk. diff --git a/res/res_pjsip/pjsip_manager.xml b/res/res_pjsip/pjsip_manager.xml index a66e7faaa28..2984ce2c490 100644 --- a/res/res_pjsip/pjsip_manager.xml +++ b/res/res_pjsip/pjsip_manager.xml @@ -252,6 +252,9 @@ + + + From 47ba6d33a6f14a4e006570b68b3013f4a51a2fba Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Thu, 22 Aug 2024 14:22:01 +0200 Subject: [PATCH 27/31] =?UTF-8?q?Optimise=20et=20limite=20=C3=A0=20une=20t?= =?UTF-8?q?aille=20de=20frame=20text=20de=20396=20octets?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- res/res_pjsip_websocket_text.c | 59 +++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/res/res_pjsip_websocket_text.c b/res/res_pjsip_websocket_text.c index fadc40ed688..801781c6438 100644 --- a/res/res_pjsip_websocket_text.c +++ b/res/res_pjsip_websocket_text.c @@ -42,6 +42,7 @@ #include "asterisk/module.h" #include "asterisk/astobj2.h" #include "asterisk/strings.h" +#include "asterisk/file.h" #include "asterisk/channel.h" #include "asterisk/stream.h" @@ -317,14 +318,14 @@ static struct ast_frame *media_sip_session_websocket_session_text_read_callback( } ssize_t bytes_read; - size_t buffer_capacity = 1024; + size_t buffer_capacity = 396; char *final_buffer = NULL; size_t total_length = 0; int done = 0; final_buffer = (char *)ast_malloc(buffer_capacity); if (final_buffer == NULL) { - ast_log(LOG_ERROR, "ast_malloc failed\n"); + ast_log(LOG_ERROR, "websocket text malloc failed\n"); } while (!done) { @@ -340,11 +341,13 @@ static struct ast_frame *media_sip_session_websocket_session_text_read_callback( bytes_read = read(ast_websocket_session_text_fd(sip_session_media->websocket_session_text), final_buffer + total_length, buffer_capacity - total_length); if (bytes_read > 0) { - ast_debug(3, "Reading websocket text raw, length %" PRIu64 "\n", bytes_read); + ast_debug(3, "Reading websocket text string, length %" PRIu64 "\n", bytes_read); total_length += bytes_read; if (bytes_read < (ssize_t)(buffer_capacity - total_length)) { done = 1; } + done = 1; + } else if (bytes_read == 0) { ast_log(LOG_WARNING, "Reading websocket text EOF\n"); done = 1; @@ -365,18 +368,21 @@ static struct ast_frame *media_sip_session_websocket_session_text_read_callback( AST_LIST_LOCK(&websocket_session_text_list); AST_LIST_TRAVERSE(&websocket_session_text_list, ws_session, entry) { - if (strcmp(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x")) == 0) { + if (!strcmp(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x"))) { struct ast_frame f_in; memset(&f_in, 0, sizeof(f_in)); f_in.frametype = AST_FRAME_TEXT; //f_in.subclass.format = ast_format_none; - f_in.subclass.format = ast_format_t140; - //f_in.subclass.format = ast_format_t140_red; + //f_in.subclass.format = ast_format_t140; + f_in.subclass.format = ast_format_t140_red; f_in.datalen = total_length; f_in.data.ptr = (void *)final_buffer; frame = ast_frdup(&f_in); + + ast_debug(3, "Reading websocket text string, total length %" PRIu64 "\n", total_length); + break; } } @@ -386,7 +392,7 @@ static struct ast_frame *media_sip_session_websocket_session_text_read_callback( } if (!frame) { - ast_log(LOG_ERROR, "websocket text session and frame not found\n"); + ast_log(LOG_ERROR, "websocket text session or frame not found, length %" PRIu64 " loss\n", total_length); return NULL; // Error. } @@ -409,7 +415,7 @@ static int media_sip_session_websocket_session_text_write_callback(struct ast_si AST_LIST_LOCK(&websocket_session_text_list); AST_LIST_TRAVERSE(&websocket_session_text_list, ws_session, entry) { - if (strcmp(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x")) == 0) { + if (!strcmp(ws_session->id, sip_session->inv_session->obj_name + strlen("inv0x"))) { websocket = ws_session->websocket; break; } @@ -425,14 +431,13 @@ static int media_sip_session_websocket_session_text_write_callback(struct ast_si } if (text) { + uint64_t text_len = strlen(text); if (websocket) { - char *payload = text; - uint64_t payload_len = strlen(text); enum ast_websocket_opcode opcode = AST_WEBSOCKET_OPCODE_TEXT; - ast_websocket_write(websocket, opcode, payload, payload_len); + ast_websocket_write(websocket, opcode, text, text_len); } else { - ast_log(LOG_ERROR, "websocket text session not found\n"); + ast_log(LOG_ERROR, "websocket text session not found, length %" PRIu64 " loss\n", text_len); } if (text != frame->data.ptr) { @@ -574,8 +579,8 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, if (pipe(sip_session_media->websocket_session_text->pipe_fds) == -1 || sip_session_media->websocket_session_text == NULL) { SCOPE_EXIT_RTN_VALUE(-1, "pipe create to exchange frames failed\n"); } else { - ast_fd_set_flags(ast_websocket_session_text_fd(sip_session_media->websocket_session_text), SOCK_NONBLOCK); - + //ast_fd_set_flags(ast_websocket_session_text_fd(sip_session_media->websocket_session_text), O_NONBLOCK); + ast_sip_session_media_add_read_callback(sip_session , sip_session_media , ast_websocket_session_text_fd(sip_session_media->websocket_session_text) @@ -679,7 +684,7 @@ static int websocket_text_t140_uri_cb(struct ast_tcptls_session_instance *ser { struct ast_websocket_session_text *ws_session = NULL; - ast_debug(1, "Entering webSocket text t140 loop method %s uri %s\n", ast_get_http_method(method), uri); + ast_debug(1, "Entering websocket text t140 loop method %s uri %s\n", ast_get_http_method(method), uri); // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. AST_LIST_LOCK(&websocket_session_text_list); @@ -716,11 +721,11 @@ static void websocket_session_text_t140_callback(struct ast_websocket *websocket int res; struct ast_websocket_session_text *ws_session = NULL; - ast_debug(1, "Entering webSocket text t140 loop %s, addr remote %s and local %s\n" + ast_debug(1, "Entering websocket text t140 loop %s, addr remote %s and local %s\n" , ast_websocket_session_id(websocket) , ast_sockaddr_stringify(ast_websocket_remote_address(websocket)) , ast_sockaddr_stringify(ast_websocket_local_address(websocket)) - ); + ); struct ast_variable *i; for (i = parameters; i; i = i->next) { @@ -736,11 +741,11 @@ static void websocket_session_text_t140_callback(struct ast_websocket *websocket } } if (!ws_session) { - ast_log(LOG_ERROR, "Failed to find uri or allocate memory for WebSocket client\n"); + ast_log(LOG_ERROR, "Failed to find uri or allocate memory for websocket client\n"); goto end; } - if (ast_fd_set_flags(ast_websocket_fd(websocket), SOCK_NONBLOCK)) { + if (ast_fd_set_flags(ast_websocket_fd(websocket), O_NONBLOCK)) { goto end; } @@ -757,21 +762,29 @@ static void websocket_session_text_t140_callback(struct ast_websocket *websocket if (ast_websocket_read(websocket, &payload, &payload_len, &opcode, &fragmented)) { // We err on the side of caution and terminate the ws_session if any error occurs. - ast_log(LOG_WARNING, "Read failure during WebSocket t140 loop\n"); + ast_log(LOG_WARNING, "Read failure during websocket t140 loop\n"); break; } if (opcode == AST_WEBSOCKET_OPCODE_TEXT && payload_len > 0) { - write(ws_session->pipe_fds[1], payload, payload_len); + //write(ws_session->pipe_fds[1], payload, payload_len); + size_t offset = 0; + while (offset < payload_len) { + size_t bytes_to_write = (payload_len - offset >= 396) ? 396 : (payload_len - offset); + + write(ws_session->pipe_fds[1], payload + offset, bytes_to_write); + + offset += bytes_to_write; + } } else if (opcode == AST_WEBSOCKET_OPCODE_CLOSE) { break; } else { - ast_debug(1, "Ignored webSocket text t140 opcode %u\n", opcode); + ast_debug(1, "Ignored websocket text t140 opcode %u\n", opcode ); } } end: - ast_debug(1, "Exiting webSocket text t140 loop %s\n", ast_websocket_session_id(websocket)); + ast_debug(1, "Exiting websocket text t140 loop %s\n", ast_websocket_session_id(websocket)); AST_LIST_LOCK(&websocket_session_text_list); AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_text_list, ws_session, entry) From 2c04e7f875f7bbbbd9137389d1e798e4970c0307 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Tue, 10 Sep 2024 10:32:09 +0200 Subject: [PATCH 28/31] Pouvoir personnaliser le nom hostname du service avec le fichier res_pjsip_websocket_text.conf --- res/res_pjsip_sdp_rtp.c | 2 +- res/res_pjsip_websocket_text.c | 97 +++++++++++++++++++++++++--------- 2 files changed, 73 insertions(+), 26 deletions(-) diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c index 36e5dc14c50..aa7f91f32e4 100644 --- a/res/res_pjsip_sdp_rtp.c +++ b/res/res_pjsip_sdp_rtp.c @@ -1981,7 +1981,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *session, struct as } if (media_type == AST_MEDIA_TYPE_TEXT) { - ast_log(LOG_DEBUG, "SDP generate RTP frame text %d %s\n", rtp_code, ast_format_get_codec_name(format)); + ast_debug(3, "SDP generate RTP frame text %d %s\n", rtp_code, ast_format_get_codec_name(format)); } if (media_type == AST_MEDIA_TYPE_TEXT && !strcasecmp(ast_format_get_codec_name(format), "red")) { diff --git a/res/res_pjsip_websocket_text.c b/res/res_pjsip_websocket_text.c index 801781c6438..aa66d45cef7 100644 --- a/res/res_pjsip_websocket_text.c +++ b/res/res_pjsip_websocket_text.c @@ -68,6 +68,15 @@ struct ast_websocket_session_text static AST_LIST_HEAD(websocket_session_text_list, ast_websocket_session_text) websocket_session_text_list; +#define WEBSOCKET_HOSTNAME_MAX_LENGTH 255 + +// Structure pour stocker les valeurs de configuration +struct websocket_session_text_config +{ + int port; + char hostname[WEBSOCKET_HOSTNAME_MAX_LENGTH + 1]; +}; + static void replace_newline(char *buffer, char replacement) { // Parcours du buffer jusqu'à la fin de la chaîne @@ -105,6 +114,52 @@ static int get_websocket_tls_port(void) return websocket_tls_port; } +// Function to load the module configuration +// 'config' is passed as a pointer to avoid dynamic memory allocation +static int load_websocket_session_text_config(struct websocket_session_text_config *config) { + struct ast_config *cfg; + const char *port_str, *hostname; + struct ast_flags config_flags = {0}; + + // Ensure the config pointer is not NULL to prevent crashes + if (config == NULL) { + ast_log(LOG_ERROR, "Null pointer passed to load res_pjsip_websocket_text.conf\n"); + return -2; // Return error if the pointer is NULL + } else { + const pj_str_t *pj_hostname = pj_gethostname(); + + // Default to "localhost" if no hostname is specified + strncpy(config->hostname, pj_hostname ? pj_hostname->ptr : "localhost", WEBSOCKET_HOSTNAME_MAX_LENGTH); + config->hostname[WEBSOCKET_HOSTNAME_MAX_LENGTH] = '\0'; // Ensure null termination + + config->port = get_websocket_tls_port(); // Default port if not specified + } + + // Load the configuration file res_pjsip_websocket_text.conf + cfg = ast_config_load("res_pjsip_websocket_text.conf", config_flags); + if (!cfg || cfg == CONFIG_STATUS_FILEINVALID) { + ast_log(LOG_ERROR, "Failed to load res_pjsip_websocket_text.conf\n"); + return 1; // Return warning if file loading fails + } + + // Retrieve the 'port' value from the [general] section of the config + if ((port_str = ast_variable_retrieve(cfg, "general", "port")) != NULL) { + config->port = atoi(port_str); // Convertir en entier + } + + // Retrieve the 'hostname' value from the [general] section of the config + if ((hostname = ast_variable_retrieve(cfg, "general", "hostname")) != NULL) { + // Copy the hostname into the structure, ensuring no buffer overflow + strncpy(config->hostname, hostname, WEBSOCKET_HOSTNAME_MAX_LENGTH); + config->hostname[WEBSOCKET_HOSTNAME_MAX_LENGTH] = '\0'; // Ensure null termination + } + + // Destroy the config structure after we're done processing + ast_config_destroy(cfg); + + return 0; // Return success +} + /*! \brief Supplement for adding framehook to sip_session channel */ static struct ast_sip_session_supplement websocket_session_text_supplement = { .method = "INVITE", @@ -170,7 +225,6 @@ static int negotiate_incoming_sdp_stream(struct ast_sip_session *sip_session, char host[NI_MAXHOST]; pjmedia_sdp_media *stream = sdp->media[index]; struct ast_format_cap *joint; - int res; RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free); SCOPE_ENTER(1, "%s\n", ast_sip_session_get_name(sip_session)); @@ -234,17 +288,11 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_session *remote, struct ast_stream *asterisk_stream) { pj_pool_t *pool = sip_session->inv_session->pool_prov; - static const pj_str_t STR_IN = {"IN", 2}; - static const pj_str_t STR_IP4 = {"IP4", 3}; - static const pj_str_t STR_IP6 = {"IP6", 3}; static const pj_str_t STR_TCP_WSS = {"TCP/WSS", 7}; static const pj_str_t STR_T140 = {"t140", 4}; - static const pj_str_t STR_RTP_AVP = {"RTP/AVPF", 8}; + //static const pj_str_t STR_RTP_AVP = {"RTP/AVPF", 8}; pjmedia_sdp_media *media; - const char *hostip = NULL; - struct ast_sockaddr addr; char tmp[512]; - pj_str_t stmp; SCOPE_ENTER(1, "%s Type: %s %s\n", ast_sip_session_get_name(sip_session), ast_codec_media_type2str(sip_session_media->type), ast_str_tmp(128, ast_stream_to_str(asterisk_stream, &STR_TMP))); @@ -275,13 +323,10 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc } pjmedia_sdp_attr *attr; - const pj_str_t *hostname = pj_gethostname(); + struct websocket_session_text_config config; -#ifndef HAVE_PJSIP_ENDPOINT_COMPACT_FORM - extern pj_bool_t pjsip_use_compact_form; -#else - pj_bool_t pjsip_use_compact_form = pjsip_cfg()->endpt.use_compact_form; -#endif + // Charger la configuration + load_websocket_session_text_config(&config); if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media)))) { SCOPE_EXIT_RTN_VALUE(-1, "Pool alloc failure\n"); @@ -290,15 +335,15 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc pj_strdup2(pool, &media->desc.media, ast_codec_media_type2str(sip_session_media->type)); media->desc.transport = STR_TCP_WSS; - media->desc.port = get_websocket_tls_port(); + media->desc.port = config.port; media->desc.port_count = 1; media->desc.fmt[media->desc.fmt_count++] = STR_T140; if (sip_session->inv_session) { - snprintf(tmp, sizeof(tmp), "wss://%s:%d/ws_text/%s", hostname ? hostname->ptr : "localhost", media->desc.port, sip_session->inv_session->obj_name + strlen( "inv0x")); + snprintf(tmp, sizeof(tmp), "wss://%s:%d/ws_text/%s", config.hostname, media->desc.port, sip_session->inv_session->obj_name + strlen( "inv0x")); } else { - snprintf(tmp, sizeof(tmp), "wss://%s:%d/ws_text/%s", hostname ? hostname->ptr : "localhost", media->desc.port, "channel_demo"); + snprintf(tmp, sizeof(tmp), "wss://%s:%d/ws_text/%s", config.hostname, media->desc.port, "channel_demo"); } attr = pjmedia_sdp_attr_create(pool, tmp, NULL); @@ -437,7 +482,7 @@ static int media_sip_session_websocket_session_text_write_callback(struct ast_si ast_websocket_write(websocket, opcode, text, text_len); } else { - ast_log(LOG_ERROR, "websocket text session not found, length %" PRIu64 " loss\n", text_len); + ast_log(LOG_ERROR, "websocket text session %s not found, length %" PRIu64 " loss\n", sip_session->inv_session->obj_name + strlen("inv0x"), text_len); } if (text != frame->data.ptr) { @@ -462,7 +507,6 @@ static int set_caps(struct ast_sip_session *session, enum ast_media_type media_type = session_media->type; int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) && ast_format_cap_count(session->direct_media_cap); - int dsp_features = 0; SCOPE_ENTER(1, "%s %s\n", ast_sip_session_get_name(session), is_offer ? "OFFER" : "ANSWER"); if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) || @@ -745,7 +789,8 @@ static void websocket_session_text_t140_callback(struct ast_websocket *websocket goto end; } - if (ast_fd_set_flags(ast_websocket_fd(websocket), O_NONBLOCK)) { + //if (ast_fd_set_flags(ast_websocket_fd(websocket), O_NONBLOCK)) { + if(ast_websocket_set_nonblock(websocket)) { goto end; } @@ -784,7 +829,7 @@ static void websocket_session_text_t140_callback(struct ast_websocket *websocket } end: - ast_debug(1, "Exiting websocket text t140 loop %s\n", ast_websocket_session_id(websocket)); + ast_debug(1, "Exiting websocket text t140 loop %s uri %s\n", ast_websocket_session_id(websocket), ws_session->id); AST_LIST_LOCK(&websocket_session_text_list); AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_text_list, ws_session, entry) @@ -809,10 +854,12 @@ static int unload_module(void) ast_sip_session_unregister_sdp_handler(&text_sdp_handler, STR_TEXT); ast_sip_session_unregister_supplement(&websocket_session_text_supplement); - ast_websocket_server_remove_protocol(websocket_text_t140_uri.data, "t140", websocket_session_text_t140_callback); - ast_http_uri_unlink(&websocket_text_t140_uri); - ao2_ref(websocket_text_t140_uri.data, -1); - websocket_text_t140_uri.data = NULL; + if (websocket_text_t140_uri.data) { + ast_websocket_server_remove_protocol(websocket_text_t140_uri.data, "t140", websocket_session_text_t140_callback); + ast_http_uri_unlink(&websocket_text_t140_uri); + ao2_ref(websocket_text_t140_uri.data, -1); + websocket_text_t140_uri.data = NULL; + } AST_LIST_LOCK(&websocket_session_text_list); AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_text_list, ws_session, entry) From a567f4460afd6385b70a1fd2cad4bad964351b0e Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Tue, 10 Sep 2024 16:31:32 +0200 Subject: [PATCH 29/31] Comments in English --- res/res_pjsip_websocket_text.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/res/res_pjsip_websocket_text.c b/res/res_pjsip_websocket_text.c index aa66d45cef7..9696dd8f1b2 100644 --- a/res/res_pjsip_websocket_text.c +++ b/res/res_pjsip_websocket_text.c @@ -290,7 +290,6 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc pj_pool_t *pool = sip_session->inv_session->pool_prov; static const pj_str_t STR_TCP_WSS = {"TCP/WSS", 7}; static const pj_str_t STR_T140 = {"t140", 4}; - //static const pj_str_t STR_RTP_AVP = {"RTP/AVPF", 8}; pjmedia_sdp_media *media; char tmp[512]; @@ -325,7 +324,7 @@ static int create_outgoing_sdp_stream(struct ast_sip_session *sip_session, struc pjmedia_sdp_attr *attr; struct websocket_session_text_config config; - // Charger la configuration + // Load the configuration into the 'config' structure load_websocket_session_text_config(&config); if (!(media = pj_pool_zalloc(pool, sizeof(struct pjmedia_sdp_media)))) { @@ -456,7 +455,7 @@ static int media_sip_session_websocket_session_text_write_callback(struct ast_si struct ast_websocket *websocket = NULL; struct ast_websocket_session_text *ws_session = NULL; - // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. + // Searching for the websocket session using the asterisk channel name retrieved from the sip session. AST_LIST_LOCK(&websocket_session_text_list); AST_LIST_TRAVERSE(&websocket_session_text_list, ws_session, entry) { @@ -525,13 +524,13 @@ static int set_caps(struct ast_sip_session *session, ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, media_type); } - // Ajouter le format T.140/RED + // Add T.140/RED format if (ast_format_cap_append(peer, ast_format_t140_red, 0) != 0) { ast_log(LOG_ERROR, "Failed to add T.140/RED format\n"); SCOPE_EXIT_RTN_VALUE(-1, "Impossible to add T.140/RED in call offer caps\n"); } - // Ajouter le format T.140 + // Add T.140 format if (ast_format_cap_append(peer, ast_format_t140, 0) != 0) { ast_log(LOG_ERROR, "Failed to add T.140 format\n"); SCOPE_EXIT_RTN_VALUE(-1, "Impossible to add t140 in call offer caps\n"); @@ -623,7 +622,7 @@ static int apply_negotiated_sdp_stream(struct ast_sip_session *sip_session, if (pipe(sip_session_media->websocket_session_text->pipe_fds) == -1 || sip_session_media->websocket_session_text == NULL) { SCOPE_EXIT_RTN_VALUE(-1, "pipe create to exchange frames failed\n"); } else { - //ast_fd_set_flags(ast_websocket_session_text_fd(sip_session_media->websocket_session_text), O_NONBLOCK); + ast_fd_set_flags(ast_websocket_session_text_fd(sip_session_media->websocket_session_text), O_NONBLOCK); ast_sip_session_media_add_read_callback(sip_session , sip_session_media @@ -730,7 +729,7 @@ static int websocket_text_t140_uri_cb(struct ast_tcptls_session_instance *ser ast_debug(1, "Entering websocket text t140 loop method %s uri %s\n", ast_get_http_method(method), uri); - // Recherche de la session websocket grace au nom du canal asterisk recupere sur la session sip. + // Searching for the websocket session using the asterisk channel name retrieved from the sip session. AST_LIST_LOCK(&websocket_session_text_list); AST_LIST_TRAVERSE(&websocket_session_text_list, ws_session, entry) { From ec096e9d67e04a45726d317bbe9781c3bd6a2076 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Wed, 11 Sep 2024 09:51:20 +0200 Subject: [PATCH 30/31] Delete unnecessary function --- res/res_pjsip_websocket_text.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/res/res_pjsip_websocket_text.c b/res/res_pjsip_websocket_text.c index 9696dd8f1b2..c079de59141 100644 --- a/res/res_pjsip_websocket_text.c +++ b/res/res_pjsip_websocket_text.c @@ -77,18 +77,6 @@ struct websocket_session_text_config char hostname[WEBSOCKET_HOSTNAME_MAX_LENGTH + 1]; }; -static void replace_newline(char *buffer, char replacement) -{ - // Parcours du buffer jusqu'à la fin de la chaîne - for (int i = 0; i < strlen(buffer); i++) { - // Si le caractère courant est un retour à la ligne - if (buffer[i] == '\r' || buffer[i] == '\n') { - // Remplacement par le caractère spécifié - buffer[i] = replacement; - } - } -} - static int get_websocket_tls_port(void) { struct ast_config *cfg; @@ -144,7 +132,7 @@ static int load_websocket_session_text_config(struct websocket_session_text_conf // Retrieve the 'port' value from the [general] section of the config if ((port_str = ast_variable_retrieve(cfg, "general", "port")) != NULL) { - config->port = atoi(port_str); // Convertir en entier + config->port = atoi(port_str); } // Retrieve the 'hostname' value from the [general] section of the config From 566bf24a3c67b9403a4b79b0586195ba3a5f1a68 Mon Sep 17 00:00:00 2001 From: Jean-Pierre BROCHET Date: Mon, 30 Sep 2024 16:32:42 +0200 Subject: [PATCH 31/31] Fix of a badly managed pointer --- res/res_pjsip_websocket_text.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/res_pjsip_websocket_text.c b/res/res_pjsip_websocket_text.c index c079de59141..f36704af1f0 100644 --- a/res/res_pjsip_websocket_text.c +++ b/res/res_pjsip_websocket_text.c @@ -769,6 +769,7 @@ static void websocket_session_text_t140_callback(struct ast_websocket *websocket } } AST_LIST_UNLOCK(&websocket_session_text_list); + break; } } if (!ws_session) { @@ -816,7 +817,7 @@ static void websocket_session_text_t140_callback(struct ast_websocket *websocket } end: - ast_debug(1, "Exiting websocket text t140 loop %s uri %s\n", ast_websocket_session_id(websocket), ws_session->id); + ast_debug(1, "Exiting websocket text t140 loop %s\n", ast_websocket_session_id(websocket)); AST_LIST_LOCK(&websocket_session_text_list); AST_LIST_TRAVERSE_SAFE_BEGIN(&websocket_session_text_list, ws_session, entry)