diff --git a/libs/esl/src/esl_event.c b/libs/esl/src/esl_event.c index 68e0688114..8a88395ec2 100644 --- a/libs/esl/src/esl_event.c +++ b/libs/esl/src/esl_event.c @@ -132,6 +132,7 @@ static const char *EVENT_NAMES[] = { "SEND_INFO", "RECV_INFO", "RECV_RTCP_MESSAGE", + "SEND_RTCP_MESSAGE", "CALL_SECURE", "NAT", "RECORD_START", diff --git a/libs/esl/src/include/esl_event.h b/libs/esl/src/include/esl_event.h index de070ad723..01e7ada07e 100644 --- a/libs/esl/src/include/esl_event.h +++ b/libs/esl/src/include/esl_event.h @@ -120,6 +120,7 @@ typedef enum { ESL_EVENT_SEND_INFO, ESL_EVENT_RECV_INFO, ESL_EVENT_RECV_RTCP_MESSAGE, + ESL_EVENT_SEND_RTCP_MESSAGE, ESL_EVENT_CALL_SECURE, ESL_EVENT_NAT, ESL_EVENT_RECORD_START, diff --git a/src/include/switch_types.h b/src/include/switch_types.h index fafc508823..059023e450 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -843,6 +843,8 @@ typedef enum { SWITCH_RTP_FLAG_PASSTHRU, SWITCH_RTP_FLAG_SECURE_SEND_MKI, SWITCH_RTP_FLAG_SECURE_RECV_MKI, + SWITCH_RTP_FLAG_AUDIO_FIRE_SEND_RTCP_EVENT, + SWITCH_RTP_FLAG_VIDEO_FIRE_SEND_RTCP_EVENT, SWITCH_RTP_FLAG_INVALID } switch_rtp_flag_t; @@ -2037,6 +2039,7 @@ typedef uint32_t switch_io_flag_t; SWITCH_EVENT_SEND_INFO SWITCH_EVENT_RECV_INFO SWITCH_EVENT_RECV_RTCP_MESSAGE + SWITCH_EVENT_SEND_RTCP_MESSAGE SWITCH_EVENT_CALL_SECURE SWITCH_EVENT_NAT - NAT Management (new/del/status) SWITCH_EVENT_RECORD_START @@ -2132,6 +2135,7 @@ typedef enum { SWITCH_EVENT_SEND_INFO, SWITCH_EVENT_RECV_INFO, SWITCH_EVENT_RECV_RTCP_MESSAGE, + SWITCH_EVENT_SEND_RTCP_MESSAGE, SWITCH_EVENT_CALL_SECURE, SWITCH_EVENT_NAT, SWITCH_EVENT_RECORD_START, diff --git a/src/switch_core_media.c b/src/switch_core_media.c index bfacc704df..0e94724249 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -8596,6 +8596,14 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi goto end; } + if (switch_channel_var_true(session->channel, "fire_rtcp_events")) { + flags[SWITCH_RTP_FLAG_AUDIO_FIRE_SEND_RTCP_EVENT] = 1; + if (switch_channel_test_flag(session->channel, CF_VIDEO_POSSIBLE) && + switch_channel_var_true(session->channel, "rtp_video_send_rtcp_message_event")) { + flags[SWITCH_RTP_FLAG_VIDEO_FIRE_SEND_RTCP_EVENT] = 1; + } + } + if (!is_reinvite) { if (switch_rtp_ready(a_engine->rtp_session)) { if (switch_channel_test_flag(session->channel, CF_TEXT_POSSIBLE) && !switch_rtp_ready(t_engine->rtp_session)) { @@ -8774,8 +8782,6 @@ SWITCH_DECLARE(switch_status_t) switch_core_media_activate_rtp(switch_core_sessi if (switch_channel_up(session->channel)) { switch_channel_set_variable(session->channel, "rtp_use_timer_name", timer_name); - - a_engine->rtp_session = switch_rtp_new(a_engine->local_sdp_ip, a_engine->local_sdp_port, a_engine->cur_payload_map->remote_sdp_ip, diff --git a/src/switch_event.c b/src/switch_event.c index 709ea95e7e..0a0e4b3c6c 100644 --- a/src/switch_event.c +++ b/src/switch_event.c @@ -207,6 +207,7 @@ static char *EVENT_NAMES[] = { "SEND_INFO", "RECV_INFO", "RECV_RTCP_MESSAGE", + "SEND_RTCP_MESSAGE", "CALL_SECURE", "NAT", "RECORD_START", diff --git a/src/switch_rtp.c b/src/switch_rtp.c index d3f43faef1..e2fcf45419 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -26,6 +26,7 @@ * Anthony Minessale II * Marcel Barbulescu * Seven Du + * Noah Mehl - Open Telecom Foundation * * switch_rtp.c -- RTP * @@ -1816,10 +1817,10 @@ static void rtcp_generate_sender_info(switch_rtp_t *rtp_session, struct switch_r sr->oc = htonl(rtp_session->stats.outbound.raw_bytes - rtp_session->stats.outbound.packet_count * sizeof(srtp_hdr_t)); switch_time_exp_gmt(&now_hr,now); - switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG10,"Sending an RTCP packet[%04d-%02d-%02d %02d:%02d:%02d.%d] lsr[%u] msw[%u] lsw[%u] stats_ssrc[%u]\n", + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG10,"Sending an RTCP packet[%04d-%02d-%02d %02d:%02d:%02d.%d] lsr[%u] msw[%u] lsw[%u] stats_ssrc[%u] packet_count[%u] OC[%u]\n", 1900 + now_hr.tm_year, now_hr.tm_mday, now_hr.tm_mon, now_hr.tm_hour, now_hr.tm_min, now_hr.tm_sec, now_hr.tm_usec, (ntohl(sr->ntp_lsw)&0xffff0000)>>16 | (ntohl(sr->ntp_msw)&0x0000ffff)<<16, - ntohl(sr->ntp_msw),ntohl(sr->ntp_lsw), rtp_session->stats.rtcp.ssrc + ntohl(sr->ntp_msw),ntohl(sr->ntp_lsw), rtp_session->stats.rtcp.ssrc, ntohl(sr->pc), ntohl(sr->oc) ); } @@ -1895,7 +1896,13 @@ static void rtcp_generate_report_block(switch_rtp_t *rtp_session, struct switch_ } rtcp_report_block->lsr = stats->last_recv_lsr_peer; rtcp_report_block->dlsr = htonl(dlsr); - rtcp_report_block->ssrc = htonl(rtp_session->stats.rtcp.peer_ssrc); + if (rtp_session->stats.rtcp.peer_ssrc) { + rtcp_report_block->ssrc = htonl(rtp_session->stats.rtcp.peer_ssrc); + } else { + /* if remote is not sending rtcp reports, take ssrc as assigned from rtp */ + rtcp_report_block->ssrc = htonl(rtp_session->remote_ssrc); + } + stats->rtcp_rtp_count++; } @@ -2057,6 +2064,79 @@ static int using_ice(switch_rtp_t *rtp_session) return 0; } +static void switch_send_rtcp_event(switch_rtp_t *rtp_session ,struct switch_rtcp_sender_report *sr,struct switch_rtcp_report_block *rtcp_report_block) +{ + if (sr && rtcp_report_block) { + switch_event_t *event; + + if (switch_event_create(&event, SWITCH_EVENT_SEND_RTCP_MESSAGE) == SWITCH_STATUS_SUCCESS) { + char value[30]; + char header[50]; + uint32_t tmpLost; + char *uuid = switch_core_session_get_uuid(rtp_session->session); + if (uuid) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Unique-ID", switch_core_session_get_uuid(rtp_session->session)); + } + + snprintf(value, sizeof(value), "%.8x", rtp_session->stats.rtcp.ssrc); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "SSRC", value); + + snprintf(value, sizeof(value), "%u", ntohl(sr->sender_info.ntp_msw)); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "NTP-Most-Significant-Word", value); + + snprintf(value, sizeof(value), "%u", ntohl(sr->sender_info.ntp_lsw)); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "NTP-Least-Significant-Word", value); + + snprintf(value, sizeof(value), "%u", ntohl(sr->sender_info.ts)); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "RTP-Timestamp", value); + + snprintf(value, sizeof(value), "%u", ntohl(sr->sender_info.pc)); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Sender-Packet-Count", value); + + snprintf(value, sizeof(value), "%u", ntohl(sr->sender_info.oc)); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Octect-Packet-Count", value); + + snprintf(value, sizeof(value), "%u", ntohl(sr->sender_info.ts)); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Last-RTP-Timestamp", value); + + snprintf(value, sizeof(value), "%" SWITCH_TIME_T_FMT, switch_time_now()); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Capture-Time", value); + + /* Add sources info */ + snprintf(header, sizeof(header), "Source-SSRC"); + snprintf(value, sizeof(value), "%.8x", rtp_session->stats.rtcp.peer_ssrc); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value); + snprintf(header, sizeof(header), "Source-Fraction"); + snprintf(value, sizeof(value), "%u", (uint8_t)rtcp_report_block->fraction); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value); + snprintf(header, sizeof(header), "Source-Lost"); +#if SWITCH_BYTE_ORDER == __BIG_ENDIAN + tmpLost = report->lost; /* signed 24bit will extended signess to int32_t automatically */ +#else + tmpLost = ntohl(rtcp_report_block->lost)>>8; + tmpLost = tmpLost | ((tmpLost & 0x00800000) ? 0xff000000 : 0x00000000); /* ...and signess compensation */ +#endif + snprintf(value, sizeof(value), "%u", tmpLost); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value); + snprintf(header, sizeof(header), "Source-Highest-Sequence-Number-Received"); + snprintf(value, sizeof(value), "%u", ntohl(rtcp_report_block->highest_sequence_number_received)); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value); + snprintf(header, sizeof(header), "Source-Jitter"); + snprintf(value, sizeof(value), "%u", ntohl(rtcp_report_block->jitter)); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value); + snprintf(header, sizeof(header), "Source-LSR"); + snprintf(value, sizeof(value), "%u", ntohl(rtcp_report_block->lsr)); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value); + snprintf(header, sizeof(header), "Source-DLSR"); + snprintf(value, sizeof(value), "%u", ntohl(rtcp_report_block->dlsr)); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, header, value); + + switch_event_fire(&event); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG10, "Dispatched RTCP SEND event\n"); + } + } +} + #define MAX_NACK 10 static int check_rtcp_and_ice(switch_rtp_t *rtp_session) { @@ -2206,6 +2286,10 @@ static int check_rtcp_and_ice(switch_rtp_t *rtp_session) rtcp_generate_report_block(rtp_session, rtcp_report_block, nack_dup); rtp_session->rtcp_send_msg.header.count = 1; /* reception report block count */ stats->sent_pkt_count = 0; + if ((!rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && rtp_session->flags[SWITCH_RTP_FLAG_AUDIO_FIRE_SEND_RTCP_EVENT]) || + (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && rtp_session->flags[SWITCH_RTP_FLAG_VIDEO_FIRE_SEND_RTCP_EVENT])) { + switch_send_rtcp_event(rtp_session, sr, rtcp_report_block); + } } switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG1, "Sending RTCP SR (ssrc=%u)\n", rtp_session->ssrc); } diff --git a/tests/unit/conf/freeswitch.xml b/tests/unit/conf/freeswitch.xml index 3dcb33198b..99609824b5 100644 --- a/tests/unit/conf/freeswitch.xml +++ b/tests/unit/conf/freeswitch.xml @@ -5,6 +5,7 @@ +
@@ -23,6 +24,23 @@ + + + + + + + + + + + + + + + + + diff --git a/tests/unit/switch_rtp.c b/tests/unit/switch_rtp.c index d411fe20fc..35de9d8630 100644 --- a/tests/unit/switch_rtp.c +++ b/tests/unit/switch_rtp.c @@ -3,7 +3,7 @@ #include static const char *rx_host = "127.0.0.1"; -static switch_port_t rx_port = 12346; +static switch_port_t rx_port = 1234; static const char *tx_host = "127.0.0.1"; static switch_port_t tx_port = 54320; static switch_memory_pool_t *pool = NULL; @@ -15,6 +15,28 @@ switch_rtp_packet_t rtp_packet; switch_frame_flag_t *frame_flags; switch_io_flag_t io_flags; switch_payload_t read_pt; +int send_rtcp_test_success = 0; + +static void show_event(switch_event_t *event) { + char *str; + /*print the event*/ + switch_event_serialize_json(event, &str); + if (str) { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "%s\n", str); + switch_safe_free(str); + } +} + +static void send_rtcp_event_handler(switch_event_t *event) +{ + const char *new_ev = switch_event_get_header(event, "Event-Name"); + + if (new_ev && !strcmp(new_ev, "SEND_RTCP_MESSAGE")) { + send_rtcp_test_success = 1; + } + + show_event(event); +} FST_CORE_BEGIN("./conf") { @@ -97,6 +119,160 @@ FST_TEARDOWN_END() switch_core_destroy_memory_pool(&pool); } FST_TEST_END() + FST_TEST_BEGIN(test_send_rtcp_event_audio) + { + switch_core_session_t *session = NULL; + switch_channel_t *channel = NULL; + switch_status_t status; + switch_call_cause_t cause; + switch_stream_handle_t stream = { 0 }; + const unsigned char packet[]="\x80\x00\xcd\x15\xfd\x86\x00\x00\x61\x5a\xe1\x37"; + uint32_t plen = 12; + char rpacket[SWITCH_RECOMMENDED_BUFFER_SIZE]; + switch_payload_t pt = { 0 }; + switch_frame_flag_t frameflags = { 0 }; + static switch_port_t audio_rx_port = 1234; + switch_media_handle_t *media_handle; + switch_core_media_params_t *mparams; + char *r_sdp; + uint8_t match = 0, p = 0; + struct sockaddr_in sin; + socklen_t len = sizeof(sin); + int x; + struct sockaddr_in servaddr_rtp; + int sockfd_rtp; + struct hostent *server; + int ret; + switch_frame_t *read_frame, *write_frame; + + switch_event_bind("", SWITCH_EVENT_ALL, SWITCH_EVENT_SUBCLASS_ANY, send_rtcp_event_handler, NULL); + + status = switch_ivr_originate(NULL, &session, &cause, "null/+15553334444", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + fst_requires(session); + fst_check(status == SWITCH_STATUS_SUCCESS); + + channel = switch_core_session_get_channel(session); + fst_requires(channel); + mparams = switch_core_session_alloc(session, sizeof(switch_core_media_params_t)); + mparams->num_codecs = 1; + mparams->inbound_codec_string = switch_core_session_strdup(session, "PCMU"); + mparams->outbound_codec_string = switch_core_session_strdup(session, "PCMU"); + mparams->rtpip = switch_core_session_strdup(session, (char *)rx_host); + + status = switch_media_handle_create(&media_handle, session, mparams); + fst_requires(status == SWITCH_STATUS_SUCCESS); + + switch_channel_set_variable(channel, "absolute_codec_string", "PCMU"); + switch_channel_set_variable(channel, "fire_rtcp_events", "true"); + switch_channel_set_variable(channel, "send_silence_when_idle", "-1"); + + switch_channel_set_variable(channel, SWITCH_LOCAL_MEDIA_IP_VARIABLE, rx_host); + switch_channel_set_variable_printf(channel, SWITCH_LOCAL_MEDIA_PORT_VARIABLE, "%d", audio_rx_port); + + r_sdp = switch_core_session_sprintf(session, + "v=0\n" + "o=FreeSWITCH 1632033305 1632033306 IN IP4 %s\n" + "s=-\n" + "c=IN IP4 %s\n" + "t=0 0\n" + "m=audio 11114 RTP/AVP 0 101\n" + "a=rtpmap:0 PCMU/8000\n" + "a=rtpmap:101 telephone-event/8000\n" + "a=rtcp:11115\n", + tx_host, tx_host); + + switch_core_media_prepare_codecs(session, SWITCH_FALSE); + + match = switch_core_media_negotiate_sdp(session, r_sdp, &p, SDP_TYPE_REQUEST); + fst_requires(match == 1); + + status = switch_core_media_choose_ports(session, SWITCH_TRUE, SWITCH_FALSE); + fst_requires(status == SWITCH_STATUS_SUCCESS); + + status = switch_core_media_activate_rtp(session); + fst_requires(status == SWITCH_STATUS_SUCCESS); + + switch_core_media_set_rtp_flag(session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_RTP_FLAG_DEBUG_RTP_READ); + switch_core_media_set_rtp_flag(session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_RTP_FLAG_DEBUG_RTP_WRITE); + switch_core_media_set_rtp_flag(session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_RTP_FLAG_AUDIO_FIRE_SEND_RTCP_EVENT); + switch_core_media_set_rtp_flag(session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_RTP_FLAG_ENABLE_RTCP); + + + switch_frame_alloc(&write_frame, SWITCH_RECOMMENDED_BUFFER_SIZE); + write_frame->codec = switch_core_session_get_write_codec(session); + + SWITCH_STANDARD_STREAM(stream); + switch_api_execute("fsctl", "debug_level 9", session, &stream); + switch_safe_free(stream.data); + + if ((sockfd_rtp = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket creation failed"); + fst_requires(0); /*exit*/ + } + + memset(&servaddr_rtp, 0, sizeof(servaddr_rtp)); + + servaddr_rtp.sin_family = AF_INET; + servaddr_rtp.sin_port = htons(audio_rx_port); + server = gethostbyname(rx_host); + bcopy((char *)server->h_addr, (char *)&servaddr_rtp.sin_addr.s_addr, server->h_length); + + /*get local UDP port (tx side) to trick FS into accepting our packets*/ + ret = sendto(sockfd_rtp, NULL, 0, MSG_CONFIRM, (const struct sockaddr *) &servaddr_rtp, sizeof(servaddr_rtp)); + if (ret < 0){ + perror("sendto"); + fst_requires(0); + } + + rtp_session = switch_core_media_get_rtp_session(session, SWITCH_MEDIA_TYPE_AUDIO); + len = sizeof(sin); + if (getsockname(sockfd_rtp, (struct sockaddr *)&sin, &len) == -1) { + perror("getsockname"); + fst_requires(0); + } else { + switch_rtp_set_remote_address(rtp_session, tx_host, ntohs(sin.sin_port), 0, SWITCH_FALSE, &err); + switch_rtp_reset(rtp_session); + } + + write_frame->datalen = plen; + memcpy(write_frame->data, &packet, plen); + + switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_PAUSE); + + for (x = 0; x < 3; x++) { + + switch_rtp_write_frame(rtp_session, write_frame); /* rtp_session->stats.rtcp.sent_pkt_count++; */ + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Sent RTP. Packet size = [%u]\n", plen); + ret = sendto(sockfd_rtp, (const char *) &packet, plen, MSG_CONFIRM, (const struct sockaddr *) &servaddr_rtp, sizeof(servaddr_rtp)); + if (ret < 0){ + perror("sendto"); + fst_requires(0); + } + + status = switch_rtp_read(rtp_session, (void *)&rpacket, &plen, &pt, &frameflags, io_flags); + fst_requires(status == SWITCH_STATUS_SUCCESS); + plen = 12; + if (pt == SWITCH_RTP_CNG_PAYLOAD /*timeout*/) continue; + + status = switch_core_session_read_frame(session, &read_frame, frameflags, 0); + fst_requires(status == SWITCH_STATUS_SUCCESS); + } + switch_sleep(3000 * 1000); + + fst_requires(send_rtcp_test_success); + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + + if (write_frame) switch_frame_free(&write_frame); + + switch_rtp_destroy(&rtp_session); + + switch_media_handle_destroy(session); + + switch_core_session_rwunlock(session); + } + FST_TEST_END() + } FST_SUITE_END() }