From beffab1d680104f2c469d0947a2a8b0e09d9c343 Mon Sep 17 00:00:00 2001 From: Dragos Oancea Date: Sun, 20 Mar 2022 17:25:37 +0300 Subject: [PATCH] [core] RTP: a media timeout fix + add pcap based-unit tests. --- configure.ac | 16 + src/switch_core_media.c | 12 +- src/switch_rtp.c | 2 +- tests/unit/Makefile.am | 6 + tests/unit/conf_rtp/freeswitch.xml | 73 +++ tests/unit/pcap/milliwatt.long.pcmu.rtp.pcap | Bin 0 -> 164014 bytes tests/unit/pcap/milliwatt.pcmu.rtp.pcap | Bin 0 -> 53384 bytes tests/unit/switch_rtp_pcap.c | 580 +++++++++++++++++++ 8 files changed, 687 insertions(+), 2 deletions(-) create mode 100644 tests/unit/conf_rtp/freeswitch.xml create mode 100644 tests/unit/pcap/milliwatt.long.pcmu.rtp.pcap create mode 100644 tests/unit/pcap/milliwatt.pcmu.rtp.pcap create mode 100644 tests/unit/switch_rtp_pcap.c diff --git a/configure.ac b/configure.ac index 4131e629a6..3ee7c58f9d 100644 --- a/configure.ac +++ b/configure.ac @@ -1970,6 +1970,22 @@ else AC_MSG_WARN([python3 support disabled, building mod_python3 will fail!]) fi +# pcap lib for unit-testing +AC_MSG_CHECKING(libpcap) +AC_CHECK_PROG(HAVE_PCAP_CONFIG,pcap-config,[true],[false]) +if test x"$HAVE_PCAP_CONFIG" = x"true"; then + AC_MSG_RESULT(yes) + PCAP_CONFIG=pcap-config + PCAP_LIBS="`$PCAP_CONFIG --libs`" + PCAP_CFLAGS="`$PCAP_CONFIG --cflags`" + AM_CONDITIONAL([HAVE_PCAP], [true]) +else + AC_MSG_RESULT(no) + AM_CONDITIONAL([HAVE_PCAP], [false]) +fi +AC_SUBST([PCAP_CFLAGS]) +AC_SUBST([PCAP_LIBS]) + # # SNMP checks for mod_snmp # diff --git a/src/switch_core_media.c b/src/switch_core_media.c index e27b81e166..347735cf1b 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -2882,8 +2882,18 @@ static void check_media_timeout_params(switch_core_session_t *session, switch_rt if (switch_rtp_ready(engine->rtp_session) && engine->media_timeout) { switch_rtp_set_media_timeout(engine->rtp_session, engine->media_timeout); - } + if (engine->type == SWITCH_MEDIA_TYPE_AUDIO) { + /* the values are in milliseconds, not in seconds as the deprecated rtp_timeout_sec */ + engine->max_missed_packets = (engine->read_impl.samples_per_second * engine->media_timeout / 1000) / engine->read_impl.samples_per_packet; + + switch_rtp_set_max_missed_packets(engine->rtp_session, engine->max_missed_packets); + if (!engine->media_hold_timeout) { + engine->media_hold_timeout = engine->media_timeout * 10; + } + engine->max_missed_hold_packets = (engine->read_impl.samples_per_second * engine->media_hold_timeout / 1000) / engine->read_impl.samples_per_packet; + } + } } SWITCH_DECLARE(switch_status_t) switch_core_media_read_frame(switch_core_session_t *session, switch_frame_t **frame, diff --git a/src/switch_rtp.c b/src/switch_rtp.c index a7bd2f8d83..5f4ce0f738 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -7350,7 +7350,7 @@ static void check_timeout(switch_rtp_t *rtp_session) } switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG10, - "%s MEDIA TIMEOUT %s %d/%d", switch_core_session_get_name(rtp_session->session), rtp_type(rtp_session), + "%s MEDIA TIMEOUT %s %d/%d\n", switch_core_session_get_name(rtp_session->session), rtp_type(rtp_session), elapsed, rtp_session->media_timeout); if (elapsed > rtp_session->media_timeout) { diff --git a/tests/unit/Makefile.am b/tests/unit/Makefile.am index d24e30f62b..e2cec69089 100644 --- a/tests/unit/Makefile.am +++ b/tests/unit/Makefile.am @@ -5,6 +5,12 @@ noinst_PROGRAMS = switch_event switch_hash switch_ivr_originate switch_utils swi noinst_PROGRAMS += switch_core_video switch_core_db switch_vad switch_packetizer switch_core_session test_sofia switch_ivr_async switch_core_asr switch_log noinst_PROGRAMS+= switch_hold switch_sip + +if HAVE_PCAP +noinst_PROGRAMS += switch_rtp_pcap +AM_LDFLAGS += $(PCAP_LIBS) +endif + AM_LDFLAGS += -avoid-version -no-undefined $(SWITCH_AM_LDFLAGS) $(openssl_LIBS) AM_LDFLAGS += $(FREESWITCH_LIBS) $(switch_builddir)/libfreeswitch.la $(CORE_LIBS) $(APR_LIBS) diff --git a/tests/unit/conf_rtp/freeswitch.xml b/tests/unit/conf_rtp/freeswitch.xml new file mode 100644 index 0000000000..69130f8f82 --- /dev/null +++ b/tests/unit/conf_rtp/freeswitch.xml @@ -0,0 +1,73 @@ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + + + + +
+
diff --git a/tests/unit/pcap/milliwatt.long.pcmu.rtp.pcap b/tests/unit/pcap/milliwatt.long.pcmu.rtp.pcap new file mode 100644 index 0000000000000000000000000000000000000000..36ed6a1779602d0846fa8cc3f5e5b73d2bdabd00 GIT binary patch literal 164014 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 literal 0 Hc-jL100001 diff --git a/tests/unit/pcap/milliwatt.pcmu.rtp.pcap b/tests/unit/pcap/milliwatt.pcmu.rtp.pcap new file mode 100644 index 0000000000000000000000000000000000000000..8f68eaca98346a4eba5c4d4c77d71034ec9b4184 GIT binary patch literal 53384 zc-qa~ca&6B(g5%x3X&ukknWjbNCU%=K?D&H6iI?0(2bygAkd9sB)GT^A_%B}32=J? z149lYNl>Ch!Gr-+RuPpop@`_p8j!83*H`=A&#vv$Ux&NrczVxS_mAIucdDvi*QF(>i{h) z-&|_vGANG!6uW(XjU}+*wd=F9_h;k(VZqQ3Pv3WPVPMhtTCUp1@N;;&tIm;vK;c0A zd~-oyai5y5y07AA&oo!P{rQ2VUDI3{yYchdR9F2S`GI8}Q(Xf4IkDpufa#plU zab>=gm$R~MimTz~yqr~6rns_R$jw=GMT)!8hTNRhEmPc$*X8D{Y3_D6c|JF{sHxlC zv?wR{`6h06^EElS>l(XLTCUE?-O$LLa>c5g+!wN3DOZwm?N_4mEmnfAtkXLi_D{wu z)q#|&a`HAebfvUik(2jQrYoi0@|?V_4P2@1@pD^!S8B&)f&3jAuGDLn2J&}jxYD{V z3FIHB=Su6fII!ULx~`i2@$>CEuJl_90|iI%bIhW^!V|S!wa((_S-f~B@ZycB<*IWO zFWxP9@!l>7EbfmN?{&O*y=uDZ9mo$X>5dm~FJ8PZsjiHd@#1yDi?}`Cxp>=1`9zEI zlJW>C2mY!nziWrehf#T)lz1zyPIYB&!CP?^-hvnLRlAm^M!W^B@K&tH zThPMoZoD=(r>Gg-0=yM?3!33ASlig`Zh^O8J>G&=cndb-EohAwZxdd;HYAlHQba(7tci(Z&mQ( ztqxwiHF)uw6glU(SyAxft;r$f(JmDv<>#zCsViNR@bB^wfD$`N>D+?VbPL>c3vxGQ zIk(_NycJjBE!dLjO1(OG3-DItZ^v8E32(*AcniAVE!d0axVv+XYlfWTf{=4uE94w6 z`r{m@hn(XDA?G;FImf{uK+4H?ipnoo`L(Wmp%dQAsQ@L1kdkgeM(`HY54i=)gSTKs zWT7O-2_?C5C{aNmvU8k9=a`gpw}{HGSox){d}9wPrva1#Qu1HJ;*h_FI`r2NJjaVd z&hek2q;^OsDGUiEb;E~}fD=l>1%bTqL4cIgFRUmjPqOlJU3q2(Dj!89XA%`k^uLCX za~upM>KtbV&vAp0bBwou&+*cbbL@nY{E$#m^N)*%pCLh@mKX#`d8tQKe#^=ey0Yd6 zsGJT^nlp(RO8&no(f!vcv2pMmHwvEPtdMiO(mBV$AdnIg1oF1mC*{n=qVg0gKhu@H zN}zHEDmjzHD3nB#5`!0y&vDA1=NK>E%0Dk2DZltZRQ|xqV@B;W1(h=aF5pb!&I*Q- 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 D{trCK literal 0 Hc-jL100001 diff --git a/tests/unit/switch_rtp_pcap.c b/tests/unit/switch_rtp_pcap.c new file mode 100644 index 0000000000..74b79f9a24 --- /dev/null +++ b/tests/unit/switch_rtp_pcap.c @@ -0,0 +1,580 @@ +/* +* FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application +* Copyright (C) 2005-2021, Anthony Minessale II +* +* Version: MPL 1.1 +* +* The contents of this file are subject to the Mozilla Public License Version +* 1.1 (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* http://www.mozilla.org/MPL/ +* +* Software distributed under the License is distributed on an "AS IS" basis, +* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +* for the specific language governing rights and limitations under the +* License. +* +* The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application +* +* The Initial Developer of the Original Code is +* Anthony Minessale II +* Portions created by the Initial Developer are Copyright (C) +* the Initial Developer. All Rights Reserved. +* +* Contributor(s): +* Dragos Oancea +* +* switch_rtp_pcap.c -- tests RTP stack using PCAP. +*/ + + +#include +#include + +/* before adding a pcap file: tcprewrite --dstipmap=X.X.X.X/32:192.168.0.1/32 --srcipmap=X.X.X.X/32:192.168.0.2/32 -i in.pcap -o out.pcap */ + +#include + +#ifndef MSG_CONFIRM +#define MSG_CONFIRM 0 +#endif + +static const char *rx_host = "127.0.0.1"; +static const char *tx_host = "127.0.0.1"; +static switch_rtp_t *rtp_session = NULL; +const char *err = NULL; +switch_rtp_packet_t rtp_packet; +switch_frame_flag_t *frame_flags; +switch_io_flag_t io_flags; +switch_payload_t read_pt; +static switch_port_t audio_rx_port = 1234; + +static int got_media_timeout = 0; + +//#define USE_RTCP_PCAP + +#define NTP_TIME_OFFSET 2208988800UL + +/* https://www.tcpdump.org/pcap.html */ +/* IP header */ +struct sniff_ip { + u_char ip_vhl; /* version << 4 | header length >> 2 */ + u_char ip_tos; /* type of service */ + u_short ip_len; /* total length */ + u_short ip_id; /* identification */ + u_short ip_off; /* fragment offset field */ +#define IP_RF 0x8000 /* reserved fragment flag */ +#define IP_DF 0x4000 /* dont fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + u_char ip_ttl; /* time to live */ + u_char ip_p; /* protocol */ + u_short ip_sum; /* checksum */ + struct in_addr ip_src,ip_dst; /* source and dest address */ +}; + +#define IP_HL(ip) (((ip)->ip_vhl) & 0x0f) + +/* switch_rtp.c - calc_local_lsr_now() */ +static inline uint32_t test_calc_local_lsr_now(switch_time_t now, uint32_t past /*milliseconds*/) +{ +// switch_time_t now; + uint32_t ntp_sec, ntp_usec, lsr_now, sec; +// now = switch_micro_time_now() - (past * 1000); + now = now - (past * 1000); + sec = (uint32_t)(now/1000000); /* convert to seconds */ + ntp_sec = sec+NTP_TIME_OFFSET; /* convert to NTP seconds */ + ntp_usec = (uint32_t)(now - ((switch_time_t) sec*1000000)); /* remove seconds to keep only the microseconds */ + + lsr_now = (uint32_t)(ntp_usec*0.065536) | (ntp_sec&0x0000ffff)<<16; /* 0.065536 is used for convertion from useconds to fraction of 65536 (x65536/1000000) */ + return lsr_now; +} + +#if 0 +static void test_prepare_rtcp(void *rtcp_packet, float est_last, uint32_t rtt, uint8_t loss) +{ + /* taken from switch_rtp.c, rtcp_generate_sender_info() */ + /* === */ + char *rtcp_sr_trigger = rtcp_packet; + switch_time_t now; + uint32_t sec, ntp_sec, ntp_usec; + uint32_t ntp_msw; + uint32_t ntp_lsw; + uint32_t *ptr_msw; + uint32_t *ptr_lsw; + uint32_t lsr; + uint32_t *ptr_lsr; + uint32_t dlsr = 0; + uint32_t *ptr_dlsr; + uint8_t *ptr_loss; + + now = switch_micro_time_now(); + sec = (uint32_t)(now/1000000); /* convert to seconds */ + ntp_sec = sec+NTP_TIME_OFFSET; /* convert to NTP seconds */ + ntp_msw = htonl(ntp_sec); /* store result in "most significant word" */ + ntp_usec = (uint32_t)(now - (sec*1000000)); /* remove seconds to keep only the microseconds */ + ntp_lsw = htonl((u_long)(ntp_usec*(double)(((uint64_t)1)<<32)*1.0e-6)); + + /* === */ + + /*patch the RTCP payload to set the RTT we want */ + + ptr_msw = (uint32_t *)rtcp_sr_trigger + 2; + *ptr_msw = ntp_msw; + + ptr_lsw = (uint32_t *)rtcp_sr_trigger + 3; + *ptr_lsw = ntp_lsw; + + lsr = test_calc_local_lsr_now(now, est_last * 1000 + rtt /*ms*/); + + ptr_lsr = (uint32_t *)rtcp_sr_trigger + 11; + *ptr_lsr = htonl(lsr); + + ptr_dlsr = (uint32_t *)rtcp_sr_trigger + 12; + *ptr_dlsr = htonl(dlsr); + + ptr_loss = (uint8_t *)rtcp_sr_trigger + 32; + *ptr_loss = loss; +} +#endif + +static switch_status_t rtp_test_start_call(switch_core_session_t **psession) +{ + char *r_sdp; + uint8_t match = 0, p = 0; + switch_core_session_t *session; + switch_channel_t *channel = NULL; + switch_status_t status; + switch_media_handle_t *media_handle; + switch_core_media_params_t *mparams; + switch_stream_handle_t stream = { 0 }; + switch_call_cause_t cause; + + /*tone stream extension*/ + status = switch_ivr_originate(NULL, psession, &cause, "null/+1234", 2, NULL, NULL, NULL, NULL, NULL, SOF_NONE, NULL, NULL); + session = *psession; + + if (!(session)) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "no session\n"); + return SWITCH_STATUS_FALSE; + } + + if (status != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "switch_ivr_originate() failed\n"); + return SWITCH_STATUS_FALSE; + } + + channel = switch_core_session_get_channel(session); + if (!channel) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "switch_core_session_get_channel() failed\n"); + return SWITCH_STATUS_FALSE; + } + mparams = switch_core_session_alloc(session, sizeof(switch_core_media_params_t)); + 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); + if (status != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "switch_media_handle_create() failed\n"); + return SWITCH_STATUS_FALSE; + } + + switch_channel_set_variable(channel, "absolute_codec_string", "PCMU"); + switch_channel_set_variable(channel, "send_silence_when_idle", "-1"); + switch_channel_set_variable(channel, "rtp_timer_name", "soft"); + switch_channel_set_variable(channel, "media_timeout", "1000"); + + 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-mux\n", + tx_host, tx_host); + + status = switch_core_media_prepare_codecs(session, SWITCH_FALSE); + if (status != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "switch_core_media_prepare_codecs() failed\n"); + return SWITCH_STATUS_FALSE; + } + + match = switch_core_media_negotiate_sdp(session, r_sdp, &p, SDP_TYPE_REQUEST); + if (match != 1) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "switch_core_media_negotiate_sdp() failed\n"); + return SWITCH_STATUS_FALSE; + } + + status = switch_core_media_choose_ports(session, SWITCH_TRUE, SWITCH_FALSE); + if (status != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "switch_core_media_choose_ports() failed\n"); + return SWITCH_STATUS_FALSE; + } + + status = switch_core_media_activate_rtp(session); + if (status != SWITCH_STATUS_SUCCESS) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "switch_core_media_activate_rtp() failed\n"); + return SWITCH_STATUS_FALSE; + } + + 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_STANDARD_STREAM(stream); + switch_api_execute("fsctl", "debug_level 10", session, &stream); + switch_safe_free(stream.data); + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t rtp_test_end_call(switch_core_session_t **psession) +{ + switch_channel_t *channel = NULL; + switch_core_session_t *session = *psession; + + channel = switch_core_session_get_channel(session); + if (!channel) { + return SWITCH_STATUS_FALSE; + } + switch_channel_hangup(channel, SWITCH_CAUSE_NORMAL_CLEARING); + switch_media_handle_destroy(session); + switch_core_session_rwunlock(session); + + return SWITCH_STATUS_SUCCESS; +} + +static void rtp_test_init_frame(switch_frame_t **pwrite_frame, switch_core_session_t **psession) +{ + const unsigned char hdr_packet[]="\x80\x00\xcd\x15\xfd\x86\x00\x00\x61\x5a\xe1\x37"; + + switch_frame_alloc(pwrite_frame, SWITCH_RECOMMENDED_BUFFER_SIZE); + (*pwrite_frame)->codec = switch_core_session_get_write_codec(*psession); + + (*pwrite_frame)->datalen = SWITCH_RTP_HEADER_LEN; /*init with dummy RTP header*/ + memcpy((*pwrite_frame)->data, &hdr_packet, SWITCH_RTP_HEADER_LEN); +} + +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 event_handler(switch_event_t *event) +{ + const char *new_ev = switch_event_get_header(event, "Event-Name"); + + if (new_ev && !strcmp(new_ev, "CHANNEL_HANGUP")) { + if (!strcmp(switch_event_get_header(event, "Hangup-Cause"), "MEDIA_TIMEOUT")) { + got_media_timeout = 1; + } + } + + show_event(event); +} + +FST_CORE_DB_BEGIN("./conf_rtp") +{ +FST_SUITE_BEGIN(switch_rtp_pcap) +{ + +FST_SETUP_BEGIN() +{ + fst_requires_module("mod_loopback"); +} +FST_SETUP_END() + +FST_TEARDOWN_BEGIN() +{ +} +FST_TEARDOWN_END() +#if 0 + FST_TEST_BEGIN(test_rtp_stall_with_rtcp_muxed_with_timer) + { + switch_core_session_t *session = NULL; + switch_status_t status; + uint32_t plen = SWITCH_RTP_HEADER_LEN; + char rpacket[SWITCH_RECOMMENDED_BUFFER_SIZE]; + switch_payload_t pt = { 0 }; + switch_frame_flag_t frameflags = { 0 }; + int x = 0; + switch_frame_t *write_frame; + pcap_t *pcap; + const unsigned char *packet; + char errbuf[PCAP_ERRBUF_SIZE]; + struct pcap_pkthdr pcap_header; + char rtcp_sr_trigger[] = "\x81\xc8\x00\x0c\x78\x9d\xac\x45\xe2\x67\xa5\x74\x30\x60\x56\x81\x00\x19" + "\xaa\x00\x00\x00\x06\xd7\x00\x01\x2c\x03\x5e\xbd\x2f\x0b\x00" + "\x00\x00\x00\x00\x00\x57\xc4\x00\x00\x00\x39\xa5\x73\xfe\x90\x00\x00\x2c\x87" + "\x81\xca\x00\x0c\x78\x9d\xac\x45\x01\x18\x73\x69\x70\x3a\x64\x72\x40\x31\x39\x32\x2e" + "\x31\x36\x38\x2e\x30\x2e\x31\x33\x3a\x37\x30\x36\x30\x06\x0e\x4c\x69\x6e\x70\x68\x6f" + "\x6e\x65\x2d\x33\x2e\x36\x2e\x31\x00\x00"; + const struct sniff_ip *ip; /* The IP header */ + int size_ip, jump_over; + struct timeval prev_ts = { 0 }; + switch_time_t time_nowpacket = 0, time_prevpacket = 0; + switch_socket_t *sock_rtp = NULL; + switch_sockaddr_t *sock_addr = NULL; + const char *str_err; + switch_size_t rough_add = 0; + + status = rtp_test_start_call(&session); + fst_requires(status == SWITCH_STATUS_SUCCESS); + fst_requires(session); + + pcap = pcap_open_offline_with_tstamp_precision("pcap/milliwatt.long.pcmu.rtp.pcap", PCAP_TSTAMP_PRECISION_MICRO, errbuf); + fst_requires(pcap); + + switch_core_media_set_rtp_flag(session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_RTP_FLAG_ENABLE_RTCP); + + rtp_session = switch_core_media_get_rtp_session(session, SWITCH_MEDIA_TYPE_AUDIO); + + rtp_test_init_frame(&write_frame, &session); + + switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_PAUSE); + + if (switch_socket_create(&sock_rtp, AF_INET, SOCK_DGRAM, 0, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + fst_requires(0); /*exit*/ + } + + switch_sockaddr_new(&sock_addr, rx_host, audio_rx_port, switch_core_session_get_pool(session)); + fst_requires(sock_addr); + + switch_rtp_set_remote_address(rtp_session, tx_host, switch_sockaddr_get_port(sock_addr), 0, SWITCH_FALSE, &str_err); + switch_rtp_reset(rtp_session); + + while ((packet = pcap_next(pcap, &pcap_header))) { + /*assume only UDP/RTP packets in the pcap*/ + uint32_t rcvd_datalen = pcap_header.caplen; + size_t len; + switch_size_t tmp_len; + + int diff_us = (pcap_header.ts.tv_sec-prev_ts.tv_sec)*1000000+(pcap_header.ts.tv_usec-prev_ts.tv_usec); + + if (diff_us > 0) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "SENT pkt diff: %d us\n", diff_us); + usleep(diff_us); + } + + prev_ts = pcap_header.ts; + + len = pcap_header.caplen; + + if (len <= 42) { + continue; + } + + ip = (struct sniff_ip*)(packet + 14); + size_ip = IP_HL(ip) * 4; + + jump_over = 14 /*SIZE_ETHERNET*/ + size_ip /*IP HDR size*/ + 8 /* UDP HDR SIZE */; /* jump 42 bytes over network layers/headers */ + packet += jump_over; + x++; + + if (!(x%10)) { /* send a RTCP SR packet every 10th RTP packet */ + int add_rtt = 200; + test_prepare_rtcp(&rtcp_sr_trigger, 2, add_rtt, 0xa0); + tmp_len = sizeof(rtcp_sr_trigger); + /*RTCP muxed*/ + if (switch_socket_sendto(sock_rtp, sock_addr, MSG_CONFIRM, (const char*)rtcp_sr_trigger, &tmp_len) != SWITCH_STATUS_SUCCESS) { + fst_requires(0); + } + + plen = sizeof(rtcp_sr_trigger); + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_INFO, "Sent RTCP. Packet size = [%u]\n", plen); + status = switch_rtp_read(rtp_session, (void *)rpacket, &rcvd_datalen, &pt, &frameflags, io_flags); + if (pt == SWITCH_RTP_CNG_PAYLOAD /*timeout*/) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "read CNG/RTCP, skip\n"); + while (1) { + status = switch_rtp_read(rtp_session, (void *)&rpacket, &rcvd_datalen, &pt, &frameflags, io_flags); + if (frameflags || SFF_RTCP) break; + } + } + fst_requires(status == SWITCH_STATUS_SUCCESS); + } + + if (packet[0] == 0x80 && packet[1] == 0 /*PCMU*/) { + int16_t *seq = (int16_t *)packet + 1; + plen = len - jump_over; + tmp_len = plen; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Sent RTP. Packet size = [%u] seq = [%d]\n", plen, htons(*seq)); + if (switch_socket_sendto(sock_rtp, sock_addr, MSG_CONFIRM, (const char*)packet, &tmp_len) != SWITCH_STATUS_SUCCESS) { + fst_requires(0); + } + } + + status = switch_rtp_read(rtp_session, (void *)&rpacket, &rcvd_datalen, &pt, &frameflags, io_flags); + if (pt == SWITCH_RTP_CNG_PAYLOAD /*timeout*/) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "read CNG, skip\n"); + continue; + } + time_prevpacket = time_nowpacket; + time_nowpacket = switch_time_now(); + if (time_prevpacket) { // skip init. + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "RECV pkt diff: %ld us\n", time_nowpacket - time_prevpacket); + + fst_requires((time_nowpacket - time_prevpacket) < 80000); + rough_add += time_nowpacket - time_prevpacket; /* just add to var for visual comparison */ + } + fst_requires(status == SWITCH_STATUS_SUCCESS); + if (pt == SWITCH_RTP_CNG_PAYLOAD /*timeout*/) continue; + fst_requires(rcvd_datalen == plen - SWITCH_RTP_HEADER_LEN); + } + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "RECV total delay: %lu\n", rough_add); /*around 17092408 us*/ + switch_yield(1000 * 1000); + + if (write_frame) switch_frame_free(&write_frame); + + switch_rtp_destroy(&rtp_session); + + rtp_test_end_call(&session); + + switch_socket_close(sock_rtp); + + pcap_close(pcap); + + switch_yield(1000 * 1000); + } + FST_TEST_END() +#endif + + FST_TEST_BEGIN(test_rtp_media_timeout) + { + switch_core_session_t *session = NULL; + switch_status_t status; + uint32_t plen = SWITCH_RTP_HEADER_LEN; + char rpacket[SWITCH_RECOMMENDED_BUFFER_SIZE]; + switch_payload_t pt = { 0 }; + switch_frame_flag_t frameflags = { 0 }; + int x = 0; + switch_frame_t *write_frame; + pcap_t *pcap; + const unsigned char *packet; + char errbuf[PCAP_ERRBUF_SIZE]; + struct pcap_pkthdr pcap_header; + const struct sniff_ip *ip; /* The IP header */ + int size_ip, jump_over; + struct timeval prev_ts = { 0 }; + switch_socket_t *sock_rtp = NULL; + switch_sockaddr_t *sock_addr = NULL; + const char *str_err; + + status = rtp_test_start_call(&session); + fst_requires(status == SWITCH_STATUS_SUCCESS); + fst_requires(session); + + switch_event_bind("", SWITCH_EVENT_ALL, SWITCH_EVENT_SUBCLASS_ANY, event_handler, NULL); + + pcap = pcap_open_offline_with_tstamp_precision("pcap/milliwatt.pcmu.rtp.pcap", PCAP_TSTAMP_PRECISION_MICRO, errbuf); + fst_requires(pcap); + + switch_core_media_set_rtp_flag(session, SWITCH_MEDIA_TYPE_AUDIO, SWITCH_RTP_FLAG_ENABLE_RTCP); + + rtp_session = switch_core_media_get_rtp_session(session, SWITCH_MEDIA_TYPE_AUDIO); + fst_requires(rtp_session); + + rtp_test_init_frame(&write_frame, &session); + + switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_PAUSE); + + if (switch_socket_create(&sock_rtp, AF_INET, SOCK_DGRAM, 0, switch_core_session_get_pool(session)) != SWITCH_STATUS_SUCCESS) { + fst_requires(0); /*exit*/ + } + + switch_sockaddr_new(&sock_addr, rx_host, audio_rx_port, switch_core_session_get_pool(session)); + fst_requires(sock_addr); + + switch_rtp_set_remote_address(rtp_session, tx_host, switch_sockaddr_get_port(sock_addr), 0, SWITCH_FALSE, &str_err); + switch_rtp_reset(rtp_session); + + /* send 3 packets then wait and expect RTP timeout */ + while ((packet = pcap_next(pcap, &pcap_header)) && x < 3) { + /*assume only UDP/RTP packets in the pcap*/ + uint32_t rcvd_datalen = pcap_header.caplen; + size_t len; + switch_size_t tmp_len; + + int diff_us = (pcap_header.ts.tv_sec-prev_ts.tv_sec)*1000000+(pcap_header.ts.tv_usec-prev_ts.tv_usec); + if (diff_us > 0) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "SENT pkt diff: %d us\n", diff_us); + usleep(diff_us); + } + + x++; + + prev_ts = pcap_header.ts; + + len = pcap_header.caplen; + + if (len <= 42) { + continue; + } + + ip = (struct sniff_ip*)(packet + 14); + size_ip = IP_HL(ip) * 4; + + jump_over = 14 /*SIZE_ETHERNET*/ + size_ip /*IP HDR size*/ + 8 /* UDP HDR SIZE */; /* jump 42 bytes over network layers/headers */ + packet += jump_over; + + if (packet[0] == 0x80 && packet[1] == 0 /*PCMU*/) { + int16_t *seq = (int16_t *)packet + 1; + plen = len - jump_over; + tmp_len = plen; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "Sent RTP. Packet size = [%u] seq = [%d]\n", plen, htons(*seq)); + if (switch_socket_sendto(sock_rtp, sock_addr, MSG_CONFIRM, (const char*)packet, &tmp_len) != SWITCH_STATUS_SUCCESS) { + fst_requires(0); + } + } + + status = switch_rtp_read(rtp_session, (void *)&rpacket, &rcvd_datalen, &pt, &frameflags, io_flags); + if (pt == SWITCH_RTP_CNG_PAYLOAD /*timeout*/) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "read CNG, skip\n"); + continue; + } + + fst_requires(status == SWITCH_STATUS_SUCCESS); + fst_requires(rcvd_datalen == plen - SWITCH_RTP_HEADER_LEN); + } + + x = 150; /* 3 seconds max */ + while (x || !got_media_timeout) { + uint32_t rcvd_datalen; + status = switch_rtp_read(rtp_session, (void *)&rpacket, &rcvd_datalen, &pt, &frameflags, io_flags); + if (pt == SWITCH_RTP_CNG_PAYLOAD /*timeout*/) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_DEBUG, "read CNG, skip\n"); + } + switch_yield(20 * 1000); + fst_requires(status == SWITCH_STATUS_SUCCESS); + x--; + } + + if (write_frame) switch_frame_free(&write_frame); + + switch_rtp_destroy(&rtp_session); + + rtp_test_end_call(&session); + + switch_socket_close(sock_rtp); + + pcap_close(pcap); + + fst_check(got_media_timeout); + } + FST_TEST_END() +} +FST_SUITE_END() +} +FST_CORE_END() + -- 2.47.2