From 14edd623032bd9d23b181f338ec809613c33779e Mon Sep 17 00:00:00 2001 From: mirkobrankovic82 Date: Thu, 29 Jan 2026 17:46:22 +0100 Subject: [PATCH] joinandconfigure --- api.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++ api.h | 9 ++- mod_janus.c | 167 +++++++++++++++++++++++++++++++++------------------- 3 files changed, 275 insertions(+), 61 deletions(-) diff --git a/api.c b/api.c index 59de9dc..ac1dadc 100644 --- a/api.c +++ b/api.c @@ -706,6 +706,166 @@ switch_status_t apiJoin(const char *pUrl, const char *pSecret, return result; } +/* Join and configure in one request: join body + jsep offer (room id + SDP offer in same message) */ +switch_status_t apiJoinAndConfigure(const char *pUrl, const char *pSecret, + const janus_id_t serverId, const janus_id_t senderId, const janus_id_t roomId, const char *pRoomIdStr, + const char *pDisplay, const char *pPin, const char *pToken, const char *callId, + const switch_bool_t muted, switch_bool_t record, const char *pRecordingFile, + const char *pType, const char *pSdp) { + message_t request, *pResponse = NULL; + switch_status_t result = SWITCH_STATUS_SUCCESS; + + cJSON *pJsonRequest = NULL; + cJSON *pJsonResponse = NULL; + char *pTransactionId = generateTransactionId(); + char url[1024]; + + switch_assert(pUrl); + switch_assert(pType); + switch_assert(pSdp); + + (void) memset((void *) &request, 0, sizeof(request)); + request.pType = "message"; + request.serverId = serverId; + request.pTransactionId = pTransactionId; + request.pSecret = pSecret; + + request.pJsonBody = cJSON_CreateObject(); + if (request.pJsonBody == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create body\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + + if (cJSON_AddStringToObject(request.pJsonBody, "request", "join") == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.request)\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + + if (pRoomIdStr && *pRoomIdStr != '\0') { + if (cJSON_AddStringToObject(request.pJsonBody, "room", pRoomIdStr) == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.room)\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + } else if (cJSON_AddNumberToObject(request.pJsonBody, "room", roomId) == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create number (body.room)\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + + if (pPin) { + if (cJSON_AddStringToObject(request.pJsonBody, "pin", pPin) == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.pin)\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + } + + if (pDisplay) { + if (cJSON_AddStringToObject(request.pJsonBody, "display", pDisplay) == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.display)\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + } + + if (pToken) { + if (cJSON_AddStringToObject(request.pJsonBody, "token", pToken) == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.token)\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + } + + if (callId) { + if (cJSON_AddStringToObject(request.pJsonBody, "opaque_id", callId) == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.opaque_id)\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + } + + /* configure params in same body */ + if (cJSON_AddBoolToObject(request.pJsonBody, "muted", (cJSON_bool) muted) == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create boolean (body.muted)\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + if (cJSON_AddBoolToObject(request.pJsonBody, "record", (cJSON_bool) record) == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create boolean (body.record)\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + if (pRecordingFile) { + if (cJSON_AddStringToObject(request.pJsonBody, "filename", pRecordingFile) == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (body.filename)\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + } + + /* jsep: offer SDP */ + request.pJsonJsep = cJSON_CreateObject(); + if (request.pJsonJsep == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create jsep\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + if (cJSON_AddStringToObject(request.pJsonJsep, "type", pType) == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (jsep.type)\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + if (cJSON_AddFalseToObject(request.pJsonJsep, "trickle") == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create jsep.trickle\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + if (cJSON_AddStringToObject(request.pJsonJsep, "sdp", pSdp) == NULL) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create string (jsep.sdp)\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + + if (!(pJsonRequest = encode(request))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Cannot create request\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + + if (snprintf(url, sizeof(url), "%s/%" SWITCH_UINT64_T_FMT "/%" SWITCH_UINT64_T_FMT, pUrl, serverId, senderId) < 0) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Could not generate URL\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + + DEBUG(SWITCH_CHANNEL_LOG, "Sending join-and-configure HTTP request\n"); + pJsonResponse = httpPost(url, HTTP_POST_TIMEOUT, pJsonRequest); + + if (!(pResponse = decode(pJsonResponse))) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid response\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + + if (!pResponse->pType || strcmp("ack", pResponse->pType) || + !pResponse->pTransactionId || strcmp(pTransactionId, pResponse->pTransactionId)) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Value mismatch\n"); + result = SWITCH_STATUS_FALSE; + goto done; + } + +done: + cJSON_Delete(pJsonRequest); + cJSON_Delete(pJsonResponse); + switch_safe_free(pResponse); + switch_safe_free(pTransactionId); + + return result; +} + switch_status_t apiConfigure(const char *pUrl, const char *pSecret, const janus_id_t serverId, const janus_id_t senderId, const switch_bool_t muted, switch_bool_t record, const char *pRecordingFile, diff --git a/api.h b/api.h index 0ea5f31..8f809af 100644 --- a/api.h +++ b/api.h @@ -38,8 +38,13 @@ janus_id_t apiGetServerId(const char *pUrl, const char *pSecret); switch_status_t apiClaimServerId(const char *pUrl, const char *pSecret, const janus_id_t serverId); janus_id_t apiGetSenderId(const char *pUrl, const char *pSecret, const janus_id_t serverId, const char *callId); -janus_id_t apiCreateRoom(const char *pUrl, const char *pSecret, const janus_id_t serverId, const janus_id_t senderId, const janus_id_t roomId, const char *pDescription, switch_bool_t record, const char *pRecordingFile, const char *pPin); -switch_status_t apiJoin(const char *pUrl, const char *pSecret, const janus_id_t serverId, const janus_id_t senderId, const janus_id_t roomId, const char *pDisplay, const char *pPin, const char *pToken, const char *callId); +janus_id_t apiCreateRoom(const char *pUrl, const char *pSecret, const janus_id_t serverId, const janus_id_t senderId, const janus_id_t roomId, const char *pRoomIdStr, const char *pDescription, switch_bool_t record, const char *pRecordingFile, const char *pPin); +switch_status_t apiJoin(const char *pUrl, const char *pSecret, const janus_id_t serverId, const janus_id_t senderId, const janus_id_t roomId, const char *pRoomIdStr, const char *pDisplay, const char *pPin, const char *pToken, const char *callId); +switch_status_t apiJoinAndConfigure(const char *pUrl, const char *pSecret, + const janus_id_t serverId, const janus_id_t senderId, const janus_id_t roomId, const char *pRoomIdStr, + const char *pDisplay, const char *pPin, const char *pToken, const char *callId, + const switch_bool_t muted, switch_bool_t record, const char *pRecordingFile, + const char *pType, const char *pSdp); switch_status_t apiConfigure(const char *pUrl, const char *pSecret, const janus_id_t serverId, const janus_id_t senderId, const switch_bool_t muted, switch_bool_t record, const char *pRecordingFile, diff --git a/mod_janus.c b/mod_janus.c index a17e1a5..4a6673c 100644 --- a/mod_janus.c +++ b/mod_janus.c @@ -59,7 +59,8 @@ typedef enum { TFLAG_HANGUP = (1 << 5), TFLAG_LINEAR = (1 << 6), TFLAG_CODEC = (1 << 7), - TFLAG_BREAK = (1 << 8) + TFLAG_BREAK = (1 << 8), + TFLAG_JOIN_AND_CONFIGURE = (1 << 9) } TFLAGS; struct private_object { @@ -135,75 +136,78 @@ switch_status_t joined(janus_id_t serverId, janus_id_t senderId, janus_id_t room switch_channel_set_variable(channel, "media_webrtc", "true"); switch_channel_set_flag(channel, CF_AUDIO); - if (switch_core_session_get_partner(session, &partner_session) == SWITCH_STATUS_SUCCESS) { - switch_channel_t *partner_channel = switch_core_session_get_channel(partner_session); - // forces the B-leg to use the same codec of A-leg and set rfc2833 flags for DTMF to work - // passed by originate flag use-bridged-channel-codec - if (switch_channel_var_true(channel, "janus-use-bridged-channel-codec")) { - // required to force Freeswitch to add telephone-event in sdp to Janus + /* When join-and-configure was used, SDP offer was sent with join; skip configure path */ + if (!switch_test_flag(tech_pvt, TFLAG_JOIN_AND_CONFIGURE)) { + if (switch_core_session_get_partner(session, &partner_session) == SWITCH_STATUS_SUCCESS) { + switch_channel_t *partner_channel = switch_core_session_get_channel(partner_session); + // forces the B-leg to use the same codec of A-leg and set rfc2833 flags for DTMF to work + // passed by originate flag use-bridged-channel-codec + if (switch_channel_var_true(channel, "janus-use-bridged-channel-codec")) { + // required to force Freeswitch to add telephone-event in sdp to Janus + tech_pvt->mparams.te = 101; + tech_pvt->mparams.recv_te = 101; + // make sure DTMF input is pass trough and with no delays + switch_channel_set_flag(channel, CF_PASS_RFC2833); + + const char *partner_codec = switch_channel_get_variable(partner_channel, "ep_codec_string"); + const char *partner_dtmf_type = switch_channel_get_variable(partner_channel, "dtmf_type"); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Using codec from A-Leg: %s\n", partner_codec); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Using DTMF Type from A-Leg: %s\n", partner_dtmf_type); + + switch_channel_set_variable(channel, "dtmf_type", partner_dtmf_type); + switch_channel_set_variable(channel, "absolute_codec_string", switch_channel_get_variable(partner_channel, partner_codec)); + } else { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Using codec from janus.conf.xml: %s\n", pServer->codec_string); + switch_channel_set_variable(channel, "absolute_codec_string", pServer->codec_string); + } + switch_core_session_rwunlock(partner_session); + } else if (switch_channel_var_true(channel, "janus-use-bridged-channel-codec")) { + // Make sure that telephone-event is added to the SDP towards Janus tech_pvt->mparams.te = 101; tech_pvt->mparams.recv_te = 101; // make sure DTMF input is pass trough and with no delays switch_channel_set_flag(channel, CF_PASS_RFC2833); - const char *partner_codec = switch_channel_get_variable(partner_channel, "ep_codec_string"); - const char *partner_dtmf_type = switch_channel_get_variable(partner_channel, "dtmf_type"); - - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Using codec from A-Leg: %s\n", partner_codec); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Using DTMF Type from A-Leg: %s\n", partner_dtmf_type); - - switch_channel_set_variable(channel, "dtmf_type", partner_dtmf_type); - switch_channel_set_variable(channel, "absolute_codec_string", switch_channel_get_variable(partner_channel, partner_codec)); - } else { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Using codec from janus.conf.xml: %s\n", pServer->codec_string); - switch_channel_set_variable(channel, "absolute_codec_string", pServer->codec_string); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Using default DTMF Type: rfc2833 \n"); + switch_channel_set_variable(channel, "dtmf_type", "rfc2833"); } - switch_core_session_rwunlock(partner_session); - } else if (switch_channel_var_true(channel, "janus-use-bridged-channel-codec")) { - // Make sure that telephone-event is added to the SDP towards Janus - tech_pvt->mparams.te = 101; - tech_pvt->mparams.recv_te = 101; - // make sure DTMF input is pass trough and with no delays - switch_channel_set_flag(channel, CF_PASS_RFC2833); - - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Using default DTMF Type: rfc2833 \n"); - switch_channel_set_variable(channel, "dtmf_type", "rfc2833"); - } - switch_core_media_prepare_codecs(session, SWITCH_TRUE); + switch_core_media_prepare_codecs(session, SWITCH_TRUE); - switch_core_session_set_ice(session); + switch_core_session_set_ice(session); - for (unsigned int i = 0; i < pServer->cand_acl_count; i ++) { - switch_core_media_add_ice_acl(session, SWITCH_MEDIA_TYPE_AUDIO, pServer->cand_acl[i]); - } - - if (switch_core_media_choose_ports(session, SWITCH_TRUE, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot choose ports\n"); - switch_channel_hangup(channel, SWITCH_CAUSE_NETWORK_OUT_OF_ORDER); - return SWITCH_STATUS_FALSE; - } - - switch_core_media_gen_local_sdp(session, SDP_TYPE_REQUEST, NULL, 0, NULL, 0); - - //switch_channel_set_flag(channel, CF_REQ_MEDIA); - //switch_channel_set_flag(channel, CF_MEDIA_ACK); - //switch_channel_set_flag(channel, CF_MEDIA_SET); + for (unsigned int i = 0; i < pServer->cand_acl_count; i ++) { + switch_core_media_add_ice_acl(session, SWITCH_MEDIA_TYPE_AUDIO, pServer->cand_acl[i]); + } - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Generated SDP=%s\n", tech_pvt->mparams.local_sdp_str); + if (switch_core_media_choose_ports(session, SWITCH_TRUE, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot choose ports\n"); + switch_channel_hangup(channel, SWITCH_CAUSE_NETWORK_OUT_OF_ORDER); + return SWITCH_STATUS_FALSE; + } - if (apiConfigure(pServer->pUrl, - pServer->pSecret, - tech_pvt->serverId, - tech_pvt->senderId, - switch_channel_var_true(channel, "janus-start-muted"), - switch_channel_var_true(channel, "janus-user-record"), - switch_channel_get_variable(channel, "janus-user-record-file"), - "offer", - tech_pvt->mparams.local_sdp_str, - tech_pvt->callId) != SWITCH_STATUS_SUCCESS) { - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failed to configure\n"); - switch_channel_hangup(channel, SWITCH_CAUSE_NETWORK_OUT_OF_ORDER); + switch_core_media_gen_local_sdp(session, SDP_TYPE_REQUEST, NULL, 0, NULL, 0); + + //switch_channel_set_flag(channel, CF_REQ_MEDIA); + //switch_channel_set_flag(channel, CF_MEDIA_ACK); + //switch_channel_set_flag(channel, CF_MEDIA_SET); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Generated SDP=%s\n", tech_pvt->mparams.local_sdp_str); + + if (apiConfigure(pServer->pUrl, + pServer->pSecret, + tech_pvt->serverId, + tech_pvt->senderId, + switch_channel_var_true(channel, "janus-start-muted"), + switch_channel_var_true(channel, "janus-user-record"), + switch_channel_get_variable(channel, "janus-user-record-file"), + "offer", + tech_pvt->mparams.local_sdp_str, + tech_pvt->callId) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failed to configure\n"); + switch_channel_hangup(channel, SWITCH_CAUSE_NETWORK_OUT_OF_ORDER); + } } switch_channel_mark_ring_ready(channel); @@ -660,6 +664,51 @@ static switch_status_t channel_on_routing(switch_core_session_t *session) return SWITCH_STATUS_NOTFOUND; } + if (switch_channel_var_true(channel, "janus-join-and-configure")) { + /* Combine join and configure: generate SDP before join, send one request with room + offer */ + if (pServer->codec_string) { + switch_channel_set_variable(channel, "absolute_codec_string", pServer->codec_string); + } + switch_channel_set_variable(channel, "dtmf_type", "rfc2833"); + tech_pvt->mparams.te = 101; + tech_pvt->mparams.recv_te = 101; + + switch_core_media_prepare_codecs(session, SWITCH_TRUE); + switch_core_session_set_ice(session); + for (unsigned int i = 0; i < pServer->cand_acl_count; i++) { + switch_core_media_add_ice_acl(session, SWITCH_MEDIA_TYPE_AUDIO, pServer->cand_acl[i]); + } + if (switch_core_media_choose_ports(session, SWITCH_TRUE, SWITCH_FALSE) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Cannot choose ports (join-and-configure)\n"); + switch_channel_hangup(channel, SWITCH_CAUSE_NETWORK_OUT_OF_ORDER); + return SWITCH_STATUS_FALSE; + } + switch_core_media_gen_local_sdp(session, SDP_TYPE_REQUEST, NULL, 0, NULL, 0); + + if (apiJoinAndConfigure( + pServer->pUrl, + pServer->pSecret, + tech_pvt->serverId, + tech_pvt->senderId, + tech_pvt->roomId, + tech_pvt->pRoomIdStr, + tech_pvt->pDisplay, + switch_channel_get_variable(channel, "janus-room-pin"), + switch_channel_get_variable(channel, "janus-user-token"), + tech_pvt->callId, + switch_channel_var_true(channel, "janus-start-muted"), + switch_channel_var_true(channel, "janus-user-record"), + switch_channel_get_variable(channel, "janus-user-record-file"), + "offer", + tech_pvt->mparams.local_sdp_str) != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Failed to join-and-configure\n"); + switch_channel_hangup(channel, SWITCH_CAUSE_INCOMPATIBLE_DESTINATION); + return SWITCH_STATUS_FALSE; + } + switch_set_flag_locked(tech_pvt, TFLAG_JOIN_AND_CONFIGURE); + return SWITCH_STATUS_SUCCESS; + } + if (apiJoin( pServer->pUrl, pServer->pSecret,