From 055b7c8de6a511ff83d843826517a7bdbbd354a4 Mon Sep 17 00:00:00 2001 From: keremcadirci Date: Sun, 19 May 2024 19:29:05 +0300 Subject: [PATCH 1/4] AudioBridge - Configure rtp --- src/plugins/janus_audiobridge.c | 136 +++++++++++++++++++++++++++++++- 1 file changed, 135 insertions(+), 1 deletion(-) diff --git a/src/plugins/janus_audiobridge.c b/src/plugins/janus_audiobridge.c index 60d8cacc53..7ffba7e457 100644 --- a/src/plugins/janus_audiobridge.c +++ b/src/plugins/janus_audiobridge.c @@ -933,6 +933,13 @@ room-: { "record": ", "group" : "" + "rtp" : { + "ip" : "", + "port" : , + "payload_type" : , + "audiolevel_ext" : , + "fec" : + } } \endverbatim * @@ -942,7 +949,8 @@ room-: { * to a Janus .mjr file, and \c filename to provide a basename for the path to * save the file to (notice that this is different from the recording of a whole * room: this feature only records the packets this user is sending, and is not - * related to the mixer stuff). A successful request will result in a \c ok event: + * related to the mixer stuff). \c rtp is used to reconfigure remote rtp information while keeping local rtp which is provided with joined event. + * A successful request will result in a \c ok event: * \verbatim { @@ -1670,7 +1678,9 @@ typedef struct janus_audiobridge_plainrtp_media { GThread *thread; } janus_audiobridge_plainrtp_media; static void janus_audiobridge_plainrtp_media_cleanup(janus_audiobridge_plainrtp_media *media); +static void janus_audiobridge_plainrtp_media_replace_remote(janus_audiobridge_plainrtp_media *media, char *remote_audio_ip, int remote_audio_rtp_port); static int janus_audiobridge_plainrtp_allocate_port(janus_audiobridge_plainrtp_media *media); +static int janus_audiobridge_plainrtp_allocate_exising_port(janus_audiobridge_plainrtp_media *media); static void *janus_audiobridge_plainrtp_relay_thread(void *data); /* AudioBridge participant */ @@ -6986,6 +6996,21 @@ static void *janus_audiobridge_handler(void *data) { json_t *group = json_object_get(root, "group"); json_t *gen_offer = json_object_get(root, "generate_offer"); json_t *update = json_object_get(root, "update"); + json_t *rtp = json_object_get(root, "rtp"); + if(rtp != NULL) { + JANUS_VALIDATE_JSON_OBJECT(root, rtp_parameters, + error_code, error_cause, TRUE, + JANUS_AUDIOBRIDGE_ERROR_MISSING_ELEMENT, JANUS_AUDIOBRIDGE_ERROR_INVALID_ELEMENT); + if(error_code != 0) { + janus_mutex_unlock(&sessions_mutex); + goto error; + } + if(msg_sdp != NULL) { + JANUS_LOG(LOG_WARN, "Added plain RTP details but negotiating a WebRTC PeerConnection: plain RTP will be ignored\n"); + rtp = NULL; + json_object_del(root, "rtp"); + } + } if(gain) participant->volume_gain = json_integer_value(gain); if(bitrate) { @@ -7109,6 +7134,80 @@ static void *janus_audiobridge_handler(void *data) { } janus_mutex_unlock(&rooms_mutex); } + janus_audiobridge_room *audiobridge = participant->room; + if(rtp != NULL && !audiobridge->allow_plainrtp) { + /* Plain RTP participants are not allowed in this room */ + error_code = JANUS_AUDIOBRIDGE_ERROR_UNAUTHORIZED; + JANUS_LOG(LOG_ERR, "Plain RTP participants not allowed in this room\n"); + g_snprintf(error_cause, 512, "Plain RTP participants not allowed in this room"); + goto error; + } + if(rtp != NULL && participant->plainrtp_media.audio_rtp_fd <= 0) { + /* Plain RTP participants is not set during join */ + error_code = JANUS_AUDIOBRIDGE_ERROR_INVALID_REQUEST; + JANUS_LOG(LOG_ERR, "Plain RTP participants is not set while joining, you can not reconfigure plain rtp\n"); + g_snprintf(error_cause, 512, "Plain RTP participants is not set while joining, you can not reconfigure plain rtp"); + goto error; + } + /* Reconfigure remote RTP*/ + if(rtp != NULL) + { + gen_offer = NULL; + const char *ip = json_string_value(json_object_get(rtp, "ip")); + uint16_t port = json_integer_value(json_object_get(rtp, "port")); + if(participant->codec == JANUS_AUDIOCODEC_OPUS) { + int pt = json_integer_value(json_object_get(rtp, "payload_type")); + if(pt == 0) + pt = 100; + participant->opus_pt = pt; + } + int audiolevel_ext_id = json_integer_value(json_object_get(rtp, "audiolevel_ext")); + if(audiolevel_ext_id > 0) + participant->extmap_id = audiolevel_ext_id; + gboolean fec = json_is_true(json_object_get(rtp, "fec")); + if(participant->codec == JANUS_AUDIOCODEC_OPUS && fec) { + participant->fec = TRUE; + opus_encoder_ctl(participant->encoder, OPUS_SET_INBAND_FEC(participant->fec)); + opus_encoder_ctl(participant->encoder, OPUS_SET_PACKET_LOSS_PERC(participant->expected_loss)); + } + + /* Create the socket */ + janus_mutex_lock(&participant->pmutex); + janus_audiobridge_plainrtp_media_replace_remote(&participant->plainrtp_media, g_strdup(ip), port); + + if(ip != NULL && port > 0) { + /* Resolve the address */ + gboolean have_audio_server_ip = FALSE; + struct sockaddr_storage audio_server_addr = { 0 }; + if(janus_network_resolve_address(participant->plainrtp_media.remote_audio_ip, &audio_server_addr) < 0) { + JANUS_LOG(LOG_ERR, "[AudioBridge-%p] Couldn't get host '%s'\n", session, + participant->plainrtp_media.remote_audio_ip); + } else { + /* Address resolved */ + have_audio_server_ip = TRUE; + if(audio_server_addr.ss_family == AF_INET6) { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&audio_server_addr; + addr6->sin6_port = htons(port); + } else if(audio_server_addr.ss_family == AF_INET) { + struct sockaddr_in *addr = (struct sockaddr_in *)&audio_server_addr; + addr->sin_port = htons(port); + } + } + + janus_audiobridge_plainrtp_allocate_exising_port(&participant->plainrtp_media); + if(have_audio_server_ip) { + if(connect(participant->plainrtp_media.audio_rtp_fd, (struct sockaddr *)&audio_server_addr, sizeof(audio_server_addr)) == -1) { + JANUS_LOG(LOG_ERR, "[AudioBridge-%p] Couldn't connect audio RTP? (%s:%d)\n", session, + participant->plainrtp_media.remote_audio_ip, participant->plainrtp_media.remote_audio_rtp_port); + JANUS_LOG(LOG_ERR, "[AudioBridge-%p] -- %d (%s)\n", session, errno, g_strerror(errno)); + } else { + participant->plainrtp_media.audio_send = TRUE; + } + } + } + janus_mutex_unlock(&participant->pmutex); + } + janus_mutex_lock(&participant->rec_mutex); const char *recording_base = json_string_value(recfile); if(recording_base) { @@ -9048,6 +9147,20 @@ static void janus_audiobridge_plainrtp_media_cleanup(janus_audiobridge_plainrtp_ close(media->pipefd[1]); media->pipefd[1] = -1; } + +static void janus_audiobridge_plainrtp_media_replace_remote(janus_audiobridge_plainrtp_media *media, char *remote_audio_ip, int remote_audio_rtp_port) { + if(media == NULL) + return; + media->ready = FALSE; + media->audio_pt = -1; + media->audio_send = FALSE; + media->remote_audio_rtp_port = remote_audio_rtp_port; + g_free(media->remote_audio_ip); + media->remote_audio_ip = remote_audio_ip; + media->audio_ssrc = 0; + media->audio_ssrc_peer = 0; +} + static int janus_audiobridge_plainrtp_allocate_port(janus_audiobridge_plainrtp_media *media) { /* Read global slider */ uint16_t rtp_port_next = rtp_range_slider; @@ -9106,6 +9219,27 @@ static int janus_audiobridge_plainrtp_allocate_port(janus_audiobridge_plainrtp_m } return -1; } + +static int janus_audiobridge_plainrtp_allocate_exising_port(janus_audiobridge_plainrtp_media *media) { + int rtp_port = media->local_audio_rtp_port; + struct sockaddr_storage rtp_address = { 0 }; + if(!ipv6_disabled) { + struct sockaddr_in6 *addr = (struct sockaddr_in6 *)&rtp_address; + addr->sin6_family = AF_INET6; + addr->sin6_port = htons(rtp_port); + addr->sin6_addr = in6addr_any; + } else { + struct sockaddr_in *addr = (struct sockaddr_in *)&rtp_address; + addr->sin_family = AF_INET; + addr->sin_port = htons(rtp_port); + addr->sin_addr.s_addr = INADDR_ANY; + } + if(bind(media->audio_rtp_fd, (struct sockaddr *)(&rtp_address), sizeof(rtp_address)) == 0) + return 0; + + return -1; +} + /* Thread to relay RTP/RTCP frames coming from the peer */ static void *janus_audiobridge_plainrtp_relay_thread(void *data) { janus_audiobridge_participant *participant = (janus_audiobridge_participant *)data; From 3145ae9a15eebf115602c0faaf922b1b78ab108c Mon Sep 17 00:00:00 2001 From: keremcadirci Date: Fri, 7 Jun 2024 16:44:58 +0300 Subject: [PATCH 2/4] AudioBridge - join rtp.generate-offer --- src/plugins/janus_audiobridge.c | 112 ++++++++++++++++++-------------- 1 file changed, 63 insertions(+), 49 deletions(-) diff --git a/src/plugins/janus_audiobridge.c b/src/plugins/janus_audiobridge.c index 7ffba7e457..b78b3093e5 100644 --- a/src/plugins/janus_audiobridge.c +++ b/src/plugins/janus_audiobridge.c @@ -884,6 +884,7 @@ room-: { "request" : "join", [..] "rtp" : { + "generate_offer" : "true|false, if true other rtp parameters below are ignored. ", "ip" : "", "port" : , "payload_type" : , @@ -6742,61 +6743,74 @@ static void *janus_audiobridge_handler(void *data) { /* If this is a plain RTP participant, create the socket */ if(rtp != NULL) { gen_offer = NULL; - const char *ip = json_string_value(json_object_get(rtp, "ip")); - uint16_t port = json_integer_value(json_object_get(rtp, "port")); - if(participant->codec == JANUS_AUDIOCODEC_OPUS) { - int pt = json_integer_value(json_object_get(rtp, "payload_type")); - if(pt == 0) - pt = 100; - participant->opus_pt = pt; - } - int audiolevel_ext_id = json_integer_value(json_object_get(rtp, "audiolevel_ext")); - if(audiolevel_ext_id > 0) - participant->extmap_id = audiolevel_ext_id; - gboolean fec = json_is_true(json_object_get(rtp, "fec")); - if(participant->codec == JANUS_AUDIOCODEC_OPUS && fec) { - participant->fec = TRUE; - opus_encoder_ctl(participant->encoder, OPUS_SET_INBAND_FEC(participant->fec)); - opus_encoder_ctl(participant->encoder, OPUS_SET_PACKET_LOSS_PERC(participant->expected_loss)); + json_t *gen_rtp_offer = json_object_get(rtp, "generate_offer"); + + if(gen_rtp_offer != NULL && json_is_true(gen_rtp_offer)) + { + janus_mutex_lock(&participant->pmutex); + janus_audiobridge_plainrtp_media_cleanup(&participant->plainrtp_media); + if(janus_audiobridge_plainrtp_allocate_port(&participant->plainrtp_media) < 0) { + JANUS_LOG(LOG_ERR, "[AudioBridge-%p] Couldn't bind to local port\n", session); + } + janus_mutex_unlock(&participant->pmutex); } - /* Create the socket */ - janus_mutex_lock(&participant->pmutex); - janus_audiobridge_plainrtp_media_cleanup(&participant->plainrtp_media); - if(janus_audiobridge_plainrtp_allocate_port(&participant->plainrtp_media) < 0) { - JANUS_LOG(LOG_ERR, "[AudioBridge-%p] Couldn't bind to local port\n", session); - } else if(ip != NULL && port > 0) { - /* Connect the socket, if there's a remote address */ - g_free(participant->plainrtp_media.remote_audio_ip); - participant->plainrtp_media.remote_audio_ip = g_strdup(ip); - participant->plainrtp_media.remote_audio_rtp_port = port; - /* Resolve the address */ - gboolean have_audio_server_ip = FALSE; - struct sockaddr_storage audio_server_addr = { 0 }; - if(janus_network_resolve_address(participant->plainrtp_media.remote_audio_ip, &audio_server_addr) < 0) { - JANUS_LOG(LOG_ERR, "[AudioBridge-%p] Couldn't get host '%s'\n", session, - participant->plainrtp_media.remote_audio_ip); - } else { - /* Address resolved */ - have_audio_server_ip = TRUE; - if(audio_server_addr.ss_family == AF_INET6) { - struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&audio_server_addr; - addr6->sin6_port = htons(port); - } else if(audio_server_addr.ss_family == AF_INET) { - struct sockaddr_in *addr = (struct sockaddr_in *)&audio_server_addr; - addr->sin_port = htons(port); - } + else{ + const char *ip = json_string_value(json_object_get(rtp, "ip")); + uint16_t port = json_integer_value(json_object_get(rtp, "port")); + if(participant->codec == JANUS_AUDIOCODEC_OPUS) { + int pt = json_integer_value(json_object_get(rtp, "payload_type")); + if(pt == 0) + pt = 100; + participant->opus_pt = pt; } - if(have_audio_server_ip) { - if(connect(participant->plainrtp_media.audio_rtp_fd, (struct sockaddr *)&audio_server_addr, sizeof(audio_server_addr)) == -1) { - JANUS_LOG(LOG_ERR, "[AudioBridge-%p] Couldn't connect audio RTP? (%s:%d)\n", session, - participant->plainrtp_media.remote_audio_ip, participant->plainrtp_media.remote_audio_rtp_port); - JANUS_LOG(LOG_ERR, "[AudioBridge-%p] -- %d (%s)\n", session, errno, g_strerror(errno)); + int audiolevel_ext_id = json_integer_value(json_object_get(rtp, "audiolevel_ext")); + if(audiolevel_ext_id > 0) + participant->extmap_id = audiolevel_ext_id; + gboolean fec = json_is_true(json_object_get(rtp, "fec")); + if(participant->codec == JANUS_AUDIOCODEC_OPUS && fec) { + participant->fec = TRUE; + opus_encoder_ctl(participant->encoder, OPUS_SET_INBAND_FEC(participant->fec)); + opus_encoder_ctl(participant->encoder, OPUS_SET_PACKET_LOSS_PERC(participant->expected_loss)); + } + /* Create the socket */ + janus_mutex_lock(&participant->pmutex); + janus_audiobridge_plainrtp_media_cleanup(&participant->plainrtp_media); + if(janus_audiobridge_plainrtp_allocate_port(&participant->plainrtp_media) < 0) { + JANUS_LOG(LOG_ERR, "[AudioBridge-%p] Couldn't bind to local port\n", session); + } else if(ip != NULL && port > 0) { + /* Connect the socket, if there's a remote address */ + g_free(participant->plainrtp_media.remote_audio_ip); + participant->plainrtp_media.remote_audio_ip = g_strdup(ip); + participant->plainrtp_media.remote_audio_rtp_port = port; + /* Resolve the address */ + gboolean have_audio_server_ip = FALSE; + struct sockaddr_storage audio_server_addr = { 0 }; + if(janus_network_resolve_address(participant->plainrtp_media.remote_audio_ip, &audio_server_addr) < 0) { + JANUS_LOG(LOG_ERR, "[AudioBridge-%p] Couldn't get host '%s'\n", session, + participant->plainrtp_media.remote_audio_ip); } else { - participant->plainrtp_media.audio_send = TRUE; + /* Address resolved */ + have_audio_server_ip = TRUE; + if(audio_server_addr.ss_family == AF_INET6) { + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&audio_server_addr; + addr6->sin6_port = htons(port); + } else if(audio_server_addr.ss_family == AF_INET) { + struct sockaddr_in *addr = (struct sockaddr_in *)&audio_server_addr; + addr->sin_port = htons(port); + } + } + if(have_audio_server_ip) { + if(connect(participant->plainrtp_media.audio_rtp_fd, (struct sockaddr *)&audio_server_addr, sizeof(audio_server_addr)) == -1) { + JANUS_LOG(LOG_ERR, "[AudioBridge-%p] Couldn't connect audio RTP? (%s:%d)\n", session, + participant->plainrtp_media.remote_audio_ip, participant->plainrtp_media.remote_audio_rtp_port); + JANUS_LOG(LOG_ERR, "[AudioBridge-%p] -- %d (%s)\n", session, errno, g_strerror(errno)); + } else { + participant->plainrtp_media.audio_send = TRUE; + } } } + janus_mutex_unlock(&participant->pmutex); } - janus_mutex_unlock(&participant->pmutex); } /* Check if we need to record this participant right away */ janus_mutex_lock(&participant->rec_mutex); From 008801ee9db118c279a36da983db01aa044d3ab2 Mon Sep 17 00:00:00 2001 From: keremcadirci Date: Fri, 7 Jun 2024 17:46:39 +0300 Subject: [PATCH 3/4] AudioBridge - join rtp.generate-offer - doc updated --- src/plugins/janus_audiobridge.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/janus_audiobridge.c b/src/plugins/janus_audiobridge.c index b78b3093e5..2a09fd99c4 100644 --- a/src/plugins/janus_audiobridge.c +++ b/src/plugins/janus_audiobridge.c @@ -884,7 +884,7 @@ room-: { "request" : "join", [..] "rtp" : { - "generate_offer" : "true|false, if true other rtp parameters below are ignored. ", + "generate_offer: , "ip" : "", "port" : , "payload_type" : , From 99be1482f5b9459b3910996cf164038ebefaf9ac Mon Sep 17 00:00:00 2001 From: keremcadirci Date: Mon, 10 Jun 2024 16:14:40 +0300 Subject: [PATCH 4/4] AudioBridge - join rtp.generate-offer - code style --- src/plugins/janus_audiobridge.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/plugins/janus_audiobridge.c b/src/plugins/janus_audiobridge.c index 2a09fd99c4..21f2b3c5c0 100644 --- a/src/plugins/janus_audiobridge.c +++ b/src/plugins/janus_audiobridge.c @@ -884,7 +884,7 @@ room-: { "request" : "join", [..] "rtp" : { - "generate_offer: , + "generate_offer: , "ip" : "", "port" : , "payload_type" : , @@ -6744,9 +6744,7 @@ static void *janus_audiobridge_handler(void *data) { if(rtp != NULL) { gen_offer = NULL; json_t *gen_rtp_offer = json_object_get(rtp, "generate_offer"); - - if(gen_rtp_offer != NULL && json_is_true(gen_rtp_offer)) - { + if(gen_rtp_offer != NULL && json_is_true(gen_rtp_offer)) { janus_mutex_lock(&participant->pmutex); janus_audiobridge_plainrtp_media_cleanup(&participant->plainrtp_media); if(janus_audiobridge_plainrtp_allocate_port(&participant->plainrtp_media) < 0) { @@ -6754,7 +6752,7 @@ static void *janus_audiobridge_handler(void *data) { } janus_mutex_unlock(&participant->pmutex); } - else{ + else { const char *ip = json_string_value(json_object_get(rtp, "ip")); uint16_t port = json_integer_value(json_object_get(rtp, "port")); if(participant->codec == JANUS_AUDIOCODEC_OPUS) { @@ -7163,9 +7161,8 @@ static void *janus_audiobridge_handler(void *data) { g_snprintf(error_cause, 512, "Plain RTP participants is not set while joining, you can not reconfigure plain rtp"); goto error; } - /* Reconfigure remote RTP*/ - if(rtp != NULL) - { + /* Reconfigure remote RTP */ + if(rtp != NULL) { gen_offer = NULL; const char *ip = json_string_value(json_object_get(rtp, "ip")); uint16_t port = json_integer_value(json_object_get(rtp, "port"));