diff --git a/src/include/switch_types.h b/src/include/switch_types.h index bf91cd8296..331bcb523f 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -227,6 +227,15 @@ SWITCH_BEGIN_EXTERN_C #define SWITCH_MAX_TRANS 2000 #define SWITCH_CORE_SESSION_MAX_PRIVATES 2 +/* Jitter */ +#define JITTER_VARIANCE_THRESHOLD 400.0 +/* IPDV */ +#define IPDV_THRESHOLD 1.0 +/* Burst and Lost Rate */ +#define LOST_BURST_ANALYZE 500 +/* Burst */ +#define LOST_BURST_CAPTURE 1024 + typedef uint8_t switch_byte_t; typedef enum { @@ -591,6 +600,29 @@ typedef struct { switch_size_t cng_packet_count; switch_size_t flush_packet_count; switch_size_t largest_jb_size; + /* Jitter */ + int64_t last_proc_time; + int64_t jitter_n; + int64_t jitter_add; + int64_t jitter_addsq; + + double variance; + double min_variance; + double max_variance; + double std_deviation; + + /* Burst and Packet Loss */ + double lossrate; + double burstrate; + double mean_interval; + int loss[LOST_BURST_CAPTURE]; + int last_loss; + int recved; + int last_processed_seq; + switch_size_t flaws; + switch_size_t last_flaw; + double R; + double mos; } switch_rtp_numbers_t; diff --git a/src/mod/applications/mod_commands/mod_commands.c b/src/mod/applications/mod_commands/mod_commands.c index 4c872e5c23..6869e51436 100644 --- a/src/mod/applications/mod_commands/mod_commands.c +++ b/src/mod/applications/mod_commands/mod_commands.c @@ -2928,6 +2928,130 @@ SWITCH_STANDARD_API(uuid_deflect) return SWITCH_STATUS_SUCCESS; } + +#define UUID_MEDIA_STATS_SYNTAX "" +SWITCH_STANDARD_API(uuid_set_media_stats) +{ + switch_core_session_t *tsession = NULL; + const char *uuid = cmd; + + if (zstr(uuid)) { + stream->write_function(stream, "-USAGE: %s\n", UUID_MEDIA_STATS_SYNTAX); + } else { + if ((tsession = switch_core_session_locate(uuid))) { + switch_core_media_set_stats(tsession); + stream->write_function(stream, "+OK:\n"); + switch_core_session_rwunlock(tsession); + } else { + stream->write_function(stream, "-ERR No such channel %s!\n", uuid); + } + } + + return SWITCH_STATUS_SUCCESS; +} + +#define add_stat(_i, _s) cJSON_AddItemToObject(jstats, _s, cJSON_CreateNumber(_i)) + +static void jsonify_stats(cJSON *json, const char *name, switch_rtp_stats_t *stats) +{ + cJSON *jstats = cJSON_CreateObject(); + cJSON_AddItemToObject(json, name, jstats); + + stats->inbound.std_deviation = sqrt(stats->inbound.variance); + + add_stat(stats->inbound.raw_bytes, "in_raw_bytes"); + add_stat(stats->inbound.media_bytes, "in_media_bytes"); + add_stat(stats->inbound.packet_count, "in_packet_count"); + add_stat(stats->inbound.media_packet_count, "in_media_packet_count"); + add_stat(stats->inbound.skip_packet_count, "in_skip_packet_count"); + add_stat(stats->inbound.jb_packet_count, "in_jitter_packet_count"); + add_stat(stats->inbound.dtmf_packet_count, "in_dtmf_packet_count"); + add_stat(stats->inbound.cng_packet_count, "in_cng_packet_count"); + add_stat(stats->inbound.flush_packet_count, "in_flush_packet_count"); + add_stat(stats->inbound.largest_jb_size, "in_largest_jb_size"); + + add_stat (stats->inbound.min_variance, "in_jitter_min_variance"); + add_stat (stats->inbound.max_variance, "in_jitter_max_variance"); + add_stat (stats->inbound.lossrate, "in_jitter_loss_rate"); + add_stat (stats->inbound.burstrate, "in_jitter_burst_rate"); + add_stat (stats->inbound.mean_interval, "in_mean_interval"); + + add_stat(stats->inbound.flaws, "in_flaw_total"); + + add_stat (stats->inbound.R, "in_quality_percentage"); + add_stat (stats->inbound.mos, "in_mos"); + + + add_stat(stats->outbound.raw_bytes, "out_raw_bytes"); + add_stat(stats->outbound.media_bytes, "out_media_bytes"); + add_stat(stats->outbound.packet_count, "out_packet_count"); + add_stat(stats->outbound.media_packet_count, "out_media_packet_count"); + add_stat(stats->outbound.skip_packet_count, "out_skip_packet_count"); + add_stat(stats->outbound.dtmf_packet_count, "out_dtmf_packet_count"); + add_stat(stats->outbound.cng_packet_count, "out_cng_packet_count"); + + add_stat(stats->rtcp.packet_count, "rtcp_packet_count"); + add_stat(stats->rtcp.octet_count, "rtcp_octet_count"); + +} + +static switch_bool_t true_enough(cJSON *json) +{ + if (json && (json->type == cJSON_True || json->valueint || json->valuedouble || json->valuestring)) { + return SWITCH_TRUE; + } + + return SWITCH_FALSE; +} + +SWITCH_STANDARD_JSON_API(json_stats_function) +{ + cJSON *reply, *data = cJSON_GetObjectItem(json, "data"); + switch_status_t status = SWITCH_STATUS_FALSE; + const char *uuid = cJSON_GetObjectCstr(data, "uuid"); + cJSON *cdata = cJSON_GetObjectItem(data, "channelData"); + + switch_core_session_t *tsession; + + reply = cJSON_CreateObject(); + *json_reply = reply; + + if (zstr(uuid)) { + cJSON_AddItemToObject(reply, "response", cJSON_CreateString("INVALID INPUT")); + goto end; + } + + + if ((tsession = switch_core_session_locate(uuid))) { + cJSON *jevent; + switch_rtp_stats_t *audio_stats = NULL, *video_stats = NULL; + + switch_core_media_set_stats(tsession); + + audio_stats = switch_core_media_get_stats(tsession, SWITCH_MEDIA_TYPE_AUDIO, switch_core_session_get_pool(tsession)); + video_stats = switch_core_media_get_stats(tsession, SWITCH_MEDIA_TYPE_VIDEO, switch_core_session_get_pool(tsession)); + + jsonify_stats(reply, "audio", audio_stats); + jsonify_stats(reply, "video", video_stats); + + if (true_enough(cdata) && switch_ivr_generate_json_cdr(tsession, &jevent, SWITCH_FALSE) == SWITCH_STATUS_SUCCESS) { + cJSON_AddItemToObject(reply, "channelData", jevent); + } + + switch_core_session_rwunlock(tsession); + + status = SWITCH_STATUS_SUCCESS; + } else { + cJSON_AddItemToObject(reply, "response", cJSON_CreateString("Session does not exist")); + goto end; + } + + end: + + return status; +} + + #define UUID_RECOVERY_REFRESH_SYNTAX " " SWITCH_STANDARD_API(uuid_recovery_refresh) { @@ -6494,6 +6618,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) SWITCH_ADD_API(commands_api_interface, "uuid_kill", "Kill channel", kill_function, KILL_SYNTAX); SWITCH_ADD_API(commands_api_interface, "uuid_send_message", "Send MESSAGE to the endpoint", uuid_send_message_function, SEND_MESSAGE_SYNTAX); SWITCH_ADD_API(commands_api_interface, "uuid_send_info", "Send info to the endpoint", uuid_send_info_function, INFO_SYNTAX); + SWITCH_ADD_API(commands_api_interface, "uuid_set_media_stats", "Set media stats", uuid_set_media_stats, UUID_MEDIA_STATS_SYNTAX); SWITCH_ADD_API(commands_api_interface, "uuid_video_refresh", "Send video refresh.", uuid_video_refresh_function, VIDEO_REFRESH_SYNTAX); SWITCH_ADD_API(commands_api_interface, "uuid_outgoing_answer", "Answer outgoing channel", outgoing_answer_function, OUTGOING_ANSWER_SYNTAX); SWITCH_ADD_API(commands_api_interface, "uuid_limit", "Increase limit resource", uuid_limit_function, LIMIT_SYNTAX); @@ -6525,6 +6650,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_commands_load) SWITCH_ADD_API(commands_api_interface, "file_exists", "Check if a file exists on server", file_exists_function, ""); SWITCH_ADD_API(commands_api_interface, "json", "JSON API", json_function, "JSON"); + SWITCH_ADD_JSON_API(json_api_interface, "mediaStats", "JSON Media Stats", json_stats_function, ""); SWITCH_ADD_JSON_API(json_api_interface, "status", "JSON status API", json_status_function, ""); SWITCH_ADD_JSON_API(json_api_interface, "fsapi", "JSON FSAPI Gateway", json_api_function, ""); diff --git a/src/mod/applications/mod_conference/mod_conference.c b/src/mod/applications/mod_conference/mod_conference.c index fc9fad6af7..e7fc3760f2 100644 --- a/src/mod/applications/mod_conference/mod_conference.c +++ b/src/mod/applications/mod_conference/mod_conference.c @@ -3655,6 +3655,8 @@ static void *SWITCH_THREAD_FUNC conference_loop_input(switch_thread_t *thread, v switch_core_session_get_read_impl(session, &member->read_impl); + switch_channel_audio_sync(channel); + /* As long as we have a valid read, feed that data into an input buffer where the conference thread will take it and mux it with any audio from other channels. */ @@ -8119,8 +8121,6 @@ SWITCH_STANDARD_APP(conference_function) goto end; } - - if ((flags_str = strstr(mydata, flags_prefix))) { char *p; *((char *) flags_str) = '\0'; diff --git a/src/mod/applications/mod_dptools/mod_dptools.c b/src/mod/applications/mod_dptools/mod_dptools.c index 4167800a22..b6ccbb26a1 100755 --- a/src/mod/applications/mod_dptools/mod_dptools.c +++ b/src/mod/applications/mod_dptools/mod_dptools.c @@ -1175,6 +1175,13 @@ SWITCH_STANDARD_APP(eval_function) return; } +SWITCH_STANDARD_APP(set_media_stats_function) +{ + switch_core_media_set_stats(session); + + return; +} + SWITCH_STANDARD_APP(zombie_function) { switch_channel_t *channel = switch_core_session_get_channel(session); @@ -5686,6 +5693,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_dptools_load) SWITCH_ADD_APP(app_interface, "strftime", "strftime", "strftime", strftime_function, "[|]", SAF_SUPPORT_NOMEDIA); SWITCH_ADD_APP(app_interface, "phrase", "Say a Phrase", "Say a Phrase", phrase_function, ",", SAF_NONE); SWITCH_ADD_APP(app_interface, "eval", "Do Nothing", "Do Nothing", eval_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); + SWITCH_ADD_APP(app_interface, "set_media_stats", "Set Media Stats", "Set Media Stats", set_media_stats_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC | SAF_ZOMBIE_EXEC); SWITCH_ADD_APP(app_interface, "stop", "Do Nothing", "Do Nothing", eval_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); SWITCH_ADD_APP(app_interface, "set_zombie_exec", "Enable Zombie Execution", "Enable Zombie Execution", zombie_function, "", SAF_SUPPORT_NOMEDIA | SAF_ROUTING_EXEC); diff --git a/src/switch_core_media.c b/src/switch_core_media.c index d0ce10030e..f6121663f9 100644 --- a/src/switch_core_media.c +++ b/src/switch_core_media.c @@ -1116,6 +1116,11 @@ SWITCH_DECLARE(void) switch_core_session_check_outgoing_crypto(switch_core_sessi switch_snprintf(var_val, sizeof(var_val), "%" SWITCH_SIZE_T_FMT, _i); \ switch_channel_set_variable(channel, var_name, var_val) +#define add_stat_double(_i, _s) \ + switch_snprintf(var_name, sizeof(var_name), "rtp_%s_%s", switch_str_nil(prefix), _s) ; \ + switch_snprintf(var_val, sizeof(var_val), "%0.2f", _i); \ + switch_channel_set_variable(channel, var_name, var_val) + static void set_stats(switch_core_session_t *session, switch_media_type_t type, const char *prefix) { switch_rtp_stats_t *stats = switch_core_media_get_stats(session, type, NULL); @@ -1124,17 +1129,27 @@ static void set_stats(switch_core_session_t *session, switch_media_type_t type, char var_name[256] = "", var_val[35] = ""; if (stats) { + stats->inbound.std_deviation = sqrt(stats->inbound.variance); add_stat(stats->inbound.raw_bytes, "in_raw_bytes"); add_stat(stats->inbound.media_bytes, "in_media_bytes"); add_stat(stats->inbound.packet_count, "in_packet_count"); add_stat(stats->inbound.media_packet_count, "in_media_packet_count"); add_stat(stats->inbound.skip_packet_count, "in_skip_packet_count"); - add_stat(stats->inbound.jb_packet_count, "in_jb_packet_count"); + add_stat(stats->inbound.jb_packet_count, "in_jitter_packet_count"); add_stat(stats->inbound.dtmf_packet_count, "in_dtmf_packet_count"); add_stat(stats->inbound.cng_packet_count, "in_cng_packet_count"); add_stat(stats->inbound.flush_packet_count, "in_flush_packet_count"); add_stat(stats->inbound.largest_jb_size, "in_largest_jb_size"); + add_stat_double(stats->inbound.min_variance, "in_jitter_min_variance"); + add_stat_double(stats->inbound.max_variance, "in_jitter_max_variance"); + add_stat_double(stats->inbound.lossrate, "in_jitter_loss_rate"); + add_stat_double(stats->inbound.burstrate, "in_jitter_burst_rate"); + add_stat_double(stats->inbound.mean_interval, "in_mean_interval"); + add_stat(stats->inbound.flaws, "in_flaw_total"); + add_stat_double(stats->inbound.R, "in_quality_percentage"); + add_stat_double(stats->inbound.mos, "in_mos"); + add_stat(stats->outbound.raw_bytes, "out_raw_bytes"); add_stat(stats->outbound.media_bytes, "out_media_bytes"); diff --git a/src/switch_rtp.c b/src/switch_rtp.c index 48b723a7b5..fb821ef6e9 100644 --- a/src/switch_rtp.c +++ b/src/switch_rtp.c @@ -69,6 +69,8 @@ #define MAX_SRTP_ERRS 10 #define RTP_TS_RESET 1 +#define DTMF_SANITY (rtp_session->one_second * 30) + static switch_port_t START_PORT = RTP_START_PORT; static switch_port_t END_PORT = RTP_END_PORT; static switch_mutex_t *port_lock = NULL; @@ -353,6 +355,11 @@ struct switch_rtp { uint32_t conf_samples_per_interval; uint32_t rsamples_per_interval; uint32_t ms_per_packet; + uint32_t one_second; + uint32_t consecutive_flaws; + uint32_t jitter_lead; + double old_mean; + switch_time_t next_stat_check_time; switch_port_t local_port; switch_port_t remote_port; switch_port_t eff_remote_port; @@ -407,7 +414,6 @@ struct switch_rtp { int zinit; #endif - }; struct switch_rtcp_source { @@ -480,8 +486,10 @@ static handle_rfc2833_result_t handle_rfc2833(switch_rtp_t *rtp_session, switch_ if (rtp_session->flags[SWITCH_RTP_FLAG_DTMF_ON]) { rtp_session->flags[SWITCH_RTP_FLAG_DTMF_ON]++; - if (rtp_session->flags[SWITCH_RTP_FLAG_DTMF_ON] > 1000) { + if (rtp_session->flags[SWITCH_RTP_FLAG_DTMF_ON] > DTMF_SANITY) { rtp_session->flags[SWITCH_RTP_FLAG_DTMF_ON] = 0; + } else { + rtp_session->stats.inbound.last_processed_seq = 0; } } @@ -515,6 +523,8 @@ static handle_rfc2833_result_t handle_rfc2833(switch_rtp_t *rtp_session, switch_ uint16_t in_digit_seq; uint32_t ts; + rtp_session->stats.inbound.last_processed_seq = 0; + if (!(packet[0] || packet[1] || packet[2] || packet[3]) && len >= 8) { @@ -540,7 +550,7 @@ static handle_rfc2833_result_t handle_rfc2833(switch_rtp_t *rtp_session, switch_ if (rtp_session->flags[SWITCH_RTP_FLAG_PASS_RFC2833]) { if (end) { - rtp_session->flags[SWITCH_RTP_FLAG_DTMF_ON] = 998; + rtp_session->flags[SWITCH_RTP_FLAG_DTMF_ON] = DTMF_SANITY - 3; } else if (!rtp_session->flags[SWITCH_RTP_FLAG_DTMF_ON]) { rtp_session->flags[SWITCH_RTP_FLAG_DTMF_ON] = 1; } @@ -1563,6 +1573,188 @@ static void send_pli(switch_rtp_t *rtp_session) return; } +static void do_mos(switch_rtp_t *rtp_session, int force) { + + if (rtp_session->stats.inbound.recved < rtp_session->stats.inbound.flaws) { + rtp_session->stats.inbound.flaws = 0; + } + + if (rtp_session->stats.inbound.recved > 0 && + rtp_session->stats.inbound.flaws && (force || rtp_session->stats.inbound.last_flaw != rtp_session->stats.inbound.flaws)) { + int R; + + if (rtp_session->consecutive_flaws++) { + int diff, penalty; + + diff = (rtp_session->stats.inbound.flaws - rtp_session->stats.inbound.last_flaw); + + if (diff < 1) diff = 1; + + penalty = diff * 2; + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG1, "%s %s %d consecutive flaws, adding %d flaw penalty\n", + switch_core_session_get_name(rtp_session->session), rtp_type(rtp_session), + rtp_session->consecutive_flaws, penalty); + + rtp_session->stats.inbound.flaws += penalty; + } + + R = ((double)((double)(rtp_session->stats.inbound.recved - rtp_session->stats.inbound.flaws) / (double)rtp_session->stats.inbound.recved) * 100.0); + + if (R < 0 || R > 100) R = 100; + + rtp_session->stats.inbound.R = R; + rtp_session->stats.inbound.mos = 1 + (0.035) * R + (.000007) * R * (R-60) * (100-R); + rtp_session->stats.inbound.last_flaw = rtp_session->stats.inbound.flaws; + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG1, "%s %s stat %0.2f %ld/%d flaws: %ld mos: %0.2f v: %0.2f %0.2f/%0.2f\n", + switch_core_session_get_name(rtp_session->session), + rtp_type(rtp_session), + rtp_session->stats.inbound.R, + rtp_session->stats.inbound.recved - rtp_session->stats.inbound.flaws, rtp_session->stats.inbound.recved, + rtp_session->stats.inbound.flaws, + rtp_session->stats.inbound.mos, + rtp_session->stats.inbound.variance, + rtp_session->stats.inbound.min_variance, + rtp_session->stats.inbound.max_variance + ); + } else { + rtp_session->consecutive_flaws = 0; + } +} + +void burstr_calculate ( int loss[], int received, double *burstr, double *lossr ) +{ + int lost = 0; + int bursts = 0; + int i; + + for ( i = 0; i < LOST_BURST_ANALYZE; i++ ) { + lost += i * loss[i]; + bursts += loss[i]; + } + if (received > 0 && bursts > 0) { + *burstr = (double)((double)lost / (double)bursts) / (double)(1.0 / ( 1.0 - (double)lost / (double)received )); + if (*burstr < 0) { + *burstr = - *burstr; + } + } else { + *burstr = 0; + } + if (received > 0) { + *lossr = (double)((double)lost / (double)received); + } else { + *lossr = 0; + } +} + +static void reset_jitter_seq(switch_rtp_t *rtp_session) +{ + rtp_session->stats.inbound.last_proc_time = 0; + rtp_session->stats.inbound.last_processed_seq = 0; + rtp_session->jitter_lead = 0; +} + +static void check_jitter(switch_rtp_t *rtp_session) +{ + switch_time_t current_time; + int64_t diff_time = 0, cur_diff = 0; + int seq; + + current_time = switch_micro_time_now() / 1000; + + if (rtp_session->flags[SWITCH_RTP_FLAG_PAUSE] || rtp_session->flags[SWITCH_RTP_FLAG_DTMF_ON] || rtp_session->dtmf_data.in_digit_ts) { + reset_jitter_seq(rtp_session); + return; + } + + if (++rtp_session->jitter_lead < 10 || !rtp_session->stats.inbound.last_proc_time) { + rtp_session->stats.inbound.last_proc_time = current_time; + return; + } + + diff_time = (current_time - rtp_session->stats.inbound.last_proc_time); + seq = (int)(uint16_t) ntohs((uint16_t) rtp_session->recv_msg.header.seq); + + /* Burst and Packet Loss */ + rtp_session->stats.inbound.recved++; + + if (rtp_session->stats.inbound.last_processed_seq > 0 && seq > (int)(rtp_session->stats.inbound.last_processed_seq + 1)) { + int lost = (seq - rtp_session->stats.inbound.last_processed_seq - 1); + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG1, "%s Got: %s seq %d but expected: %d lost: %d\n", + switch_core_session_get_name(rtp_session->session), + rtp_type(rtp_session), + seq, + (rtp_session->stats.inbound.last_processed_seq + 1), lost); + rtp_session->stats.inbound.last_loss++; + + if (rtp_session->stats.inbound.last_loss > 0 && rtp_session->stats.inbound.last_loss < LOST_BURST_CAPTURE) { + rtp_session->stats.inbound.loss[rtp_session->stats.inbound.last_loss] += lost; + } + + rtp_session->stats.inbound.flaws += lost; + + } else { + rtp_session->stats.inbound.last_loss = 0; + } + + rtp_session->stats.inbound.last_processed_seq = seq; + + /* Burst and Packet Loss */ + + if (current_time > rtp_session->next_stat_check_time) { + rtp_session->next_stat_check_time = current_time + 5000; + burstr_calculate(rtp_session->stats.inbound.loss, rtp_session->stats.inbound.recved, + &(rtp_session->stats.inbound.burstrate), &(rtp_session->stats.inbound.lossrate)); + do_mos(rtp_session, SWITCH_TRUE); + } else { + do_mos(rtp_session, SWITCH_FALSE); + } + + + if ( diff_time < 0 ) { + diff_time = -diff_time; + } + + rtp_session->stats.inbound.jitter_n++; + rtp_session->stats.inbound.jitter_add += diff_time; + + cur_diff = diff_time - rtp_session->stats.inbound.mean_interval; + + rtp_session->stats.inbound.jitter_addsq += (cur_diff * cur_diff); + rtp_session->stats.inbound.last_proc_time = current_time; + + if (rtp_session->stats.inbound.jitter_n > 0) { + double ipdv; + + rtp_session->stats.inbound.mean_interval = (double)rtp_session->stats.inbound.jitter_add / (double)rtp_session->stats.inbound.jitter_n; + + if (!rtp_session->old_mean) { + rtp_session->old_mean = rtp_session->stats.inbound.mean_interval; + } + + rtp_session->stats.inbound.variance = (double)rtp_session->stats.inbound.jitter_addsq / (double)rtp_session->stats.inbound.jitter_n; + + //printf("CHECK %d +%ld +%ld %f %f\n", rtp_session->timer.samplecount, diff_time, (diff_time * diff_time), rtp_session->stats.inbound.mean_interval, rtp_session->stats.inbound.variance); + + ipdv = rtp_session->old_mean - rtp_session->stats.inbound.mean_interval; + + if ( ipdv > IPDV_THRESHOLD ) { /* It shows Increasing Delays */ + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG1, "Calculated Instantaneous Packet Delay Variation: %s packet %lf\n", + rtp_type(rtp_session), ipdv); + } + + if ( rtp_session->stats.inbound.variance < rtp_session->stats.inbound.min_variance || rtp_session->stats.inbound.min_variance == 0 ) { + rtp_session->stats.inbound.min_variance = rtp_session->stats.inbound.variance; + } + + if ( rtp_session->stats.inbound.variance > rtp_session->stats.inbound.max_variance ) { + rtp_session->stats.inbound.max_variance = rtp_session->stats.inbound.variance; + } + + rtp_session->old_mean = rtp_session->stats.inbound.mean_interval; + } +} static int check_rtcp_and_ice(switch_rtp_t *rtp_session) { @@ -1570,7 +1762,6 @@ static int check_rtcp_and_ice(switch_rtp_t *rtp_session) int rtcp_ok = 1; switch_time_t now = switch_micro_time_now(); - if (rtp_session->fir_countdown) { //if (rtp_session->fir_countdown == FIR_COUNTDOWN) { // do_flush(rtp_session, SWITCH_TRUE); @@ -2923,6 +3114,8 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_set_interval(switch_rtp_t *rtp_sessio rtp_session->samples_per_second = (uint32_t) ((double) (1000.0f / (double) (rtp_session->ms_per_packet / 1000)) * (double) rtp_session->samples_per_interval); + rtp_session->one_second = (rtp_session->samples_per_second / rtp_session->samples_per_interval); + return SWITCH_STATUS_SUCCESS; } @@ -3026,6 +3219,8 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session rtp_session->seq = (uint16_t) rand(); rtp_session->ssrc = (uint32_t) ((intptr_t) rtp_session + (uint32_t) switch_epoch_time_now(NULL)); + rtp_session->stats.inbound.R = 100.0; + rtp_session->stats.inbound.mos = 4.5; rtp_session->send_msg.header.ssrc = htonl(rtp_session->ssrc); rtp_session->send_msg.header.ts = 0; rtp_session->send_msg.header.m = 0; @@ -3151,6 +3346,21 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_create(switch_rtp_t **new_rtp_session #endif + /* Jitter */ + rtp_session->stats.inbound.last_proc_time = switch_time_now() / 1000; + rtp_session->stats.inbound.jitter_n = 0; + rtp_session->stats.inbound.jitter_add = 0; + rtp_session->stats.inbound.jitter_addsq = 0; + rtp_session->stats.inbound.min_variance = 0; + rtp_session->stats.inbound.max_variance = 0; + + /* Burst and Packet Loss */ + rtp_session->stats.inbound.lossrate = 0; + rtp_session->stats.inbound.burstrate = 0; + memset(rtp_session->stats.inbound.loss, 0, sizeof(rtp_session->stats.inbound.loss)); + rtp_session->stats.inbound.last_loss = 0; + rtp_session->stats.inbound.last_processed_seq = -1; + rtp_session->ready = 1; *new_rtp_session = rtp_session; @@ -3628,6 +3838,8 @@ SWITCH_DECLARE(void) switch_rtp_destroy(switch_rtp_t **rtp_session) READ_DEC((*rtp_session)); WRITE_DEC((*rtp_session)); + do_mos(*rtp_session, SWITCH_TRUE); + switch_mutex_lock((*rtp_session)->flag_mutex); switch_rtp_kill_socket(*rtp_session); @@ -3804,7 +4016,11 @@ SWITCH_DECLARE(void) switch_rtp_set_flag(switch_rtp_t *rtp_session, switch_rtp_f rtp_session->flags[flag] = 1; switch_mutex_unlock(rtp_session->flag_mutex); - if (flag == SWITCH_RTP_FLAG_AUTOADJ) { + if (flag == SWITCH_RTP_FLAG_DTMF_ON) { + rtp_session->stats.inbound.last_processed_seq = 0; + } else if (flag == SWITCH_RTP_FLAG_FLUSH) { + reset_jitter_seq(rtp_session); + } else if (flag == SWITCH_RTP_FLAG_AUTOADJ) { rtp_session->autoadj_window = 20; rtp_session->autoadj_tally = 0; rtp_flush_read_buffer(rtp_session, SWITCH_RTP_FLUSH_ONCE); @@ -3829,7 +4045,9 @@ SWITCH_DECLARE(void) switch_rtp_clear_flag(switch_rtp_t *rtp_session, switch_rtp rtp_session->flags[flag] = 0; switch_mutex_unlock(rtp_session->flag_mutex); - if (flag == SWITCH_RTP_FLAG_NOBLOCK && rtp_session->sock_input) { + if (flag == SWITCH_RTP_FLAG_DTMF_ON) { + rtp_session->stats.inbound.last_processed_seq = 0; + } else if (flag == SWITCH_RTP_FLAG_NOBLOCK && rtp_session->sock_input) { switch_socket_opt_set(rtp_session->sock_input, SWITCH_SO_NONBLOCK, FALSE); } } @@ -4023,6 +4241,7 @@ SWITCH_DECLARE(void) rtp_flush_read_buffer(switch_rtp_t *rtp_session, switch_rtp if (switch_rtp_ready(rtp_session)) { rtp_session->last_write_ts = RTP_TS_RESET; rtp_session->flags[SWITCH_RTP_FLAG_FLUSH] = 1; + reset_jitter_seq(rtp_session); switch (flush) { case SWITCH_RTP_FLUSH_STICK: @@ -4046,6 +4265,7 @@ static void do_flush(switch_rtp_t *rtp_session, int force) if (!switch_rtp_ready(rtp_session)) { return; } + reset_jitter_seq(rtp_session); if (!force) { if (rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] || @@ -4057,7 +4277,6 @@ static void do_flush(switch_rtp_t *rtp_session, int force) } } - READ_INC(rtp_session); if (switch_rtp_ready(rtp_session)) { @@ -4104,6 +4323,7 @@ static void do_flush(switch_rtp_t *rtp_session, int force) if (rtp_session->jb && flushed) { stfu_n_sync(rtp_session->jb, flushed); + reset_jitter_seq(rtp_session); } if (was_blocking && switch_rtp_ready(rtp_session)) { @@ -4164,6 +4384,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t unsigned char *b = NULL; int sync = 0; switch_time_t now; + switch_size_t xcheck_jitter = 0; switch_assert(bytes); more: @@ -4177,6 +4398,13 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t rtp_session->missed_count = 0; } + + if (*bytes > rtp_header_len && (rtp_session->recv_msg.header.version == 2 && check_recv_payload(rtp_session))) { + xcheck_jitter = *bytes; + check_jitter(rtp_session); + } + + if (check_rtcp_and_ice(rtp_session) == -1) { return SWITCH_STATUS_GENERR; } @@ -4279,6 +4507,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t if (sync) { if (!rtp_session->flags[SWITCH_RTP_FLAG_USE_TIMER] && rtp_session->timer.interval) { switch_core_timer_sync(&rtp_session->timer); + reset_jitter_seq(rtp_session); } rtp_session->hot_hits = 0; goto more; @@ -4384,7 +4613,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t if (!rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] && !rtp_session->flags[SWITCH_RTP_FLAG_UDPTL]) { #ifdef ENABLE_ZRTP /* ZRTP Recv */ - if(zrtp_on) { + if (zrtp_on) { unsigned int sbytes = (int) *bytes; zrtp_status_t stat = 0; @@ -4487,6 +4716,7 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t if (!rtp_session->flags[SWITCH_RTP_FLAG_USE_TIMER] && rtp_session->timer.interval) { switch_core_timer_sync(&rtp_session->timer); + reset_jitter_seq(rtp_session); } if (stfu_n_eat(rtp_session->jb, rtp_session->last_read_ts, @@ -4520,6 +4750,12 @@ static switch_status_t read_rtp_packet(switch_rtp_t *rtp_session, switch_size_t rtp_session->recv_msg.header.pt = jb_frame->pt; rtp_session->recv_msg.header.seq = htons(jb_frame->seq); status = SWITCH_STATUS_SUCCESS; + if (!xcheck_jitter) { + printf("XXX %d\n", __LINE__); + check_jitter(rtp_session); + xcheck_jitter = *bytes; + } + } } @@ -4711,6 +4947,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ int rtcp_fdr = 0; int hot_socket = 0; int read_loops = 0; + int slept = 0; if (!switch_rtp_ready(rtp_session)) { return -1; @@ -4743,6 +4980,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ //!rtp_session->flags[SWITCH_RTP_FLAG_RTCP_MUX] && //!rtp_session->dtls && rtp_session->read_pollfd) { + if (switch_poll(rtp_session->read_pollfd, 1, &fdr, 0) == SWITCH_STATUS_SUCCESS) { status = read_rtp_packet(rtp_session, &bytes, flags, SWITCH_FALSE); if (status == SWITCH_STATUS_GENERR) { @@ -4788,6 +5026,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG10, "%s skip timer once\n", switch_core_session_get_name(rtp_session->session)); rtp_session->sync_packets++; switch_core_timer_sync(&rtp_session->timer); + reset_jitter_seq(rtp_session); } else { if (rtp_session->sync_packets) { @@ -4796,13 +5035,27 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ "%s Auto-Flush catching up %d packets (%d)ms.\n", switch_core_session_get_name(rtp_session->session), rtp_session->sync_packets, (rtp_session->ms_per_packet * rtp_session->sync_packets) / 1000); + if (!rtp_session->flags[SWITCH_RTP_FLAG_PAUSE]) { + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG1, "%s syncing %d %s packet(s)\n", + switch_core_session_get_name(rtp_session->session), + rtp_session->sync_packets, rtp_type(rtp_session)); + + rtp_session->stats.inbound.flaws += rtp_session->sync_packets; + } + switch_core_timer_sync(&rtp_session->timer); + reset_jitter_seq(rtp_session); rtp_session->hot_hits = 0; } else { + if (slept) { + switch_cond_next(); + } else { + switch_core_timer_next(&rtp_session->timer); + slept++; + } - switch_core_timer_next(&rtp_session->timer); } - + rtp_session->sync_packets = 0; } } @@ -4854,6 +5107,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ if (rtp_session->flags[SWITCH_RTP_FLAG_VIDEO] && rtp_session->flags[SWITCH_RTP_FLAG_BREAK]) { switch_rtp_clear_flag(rtp_session, SWITCH_RTP_FLAG_BREAK); bytes = 0; + reset_jitter_seq(rtp_session); return_cng_frame(); } @@ -5048,6 +5302,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ if (!rtp_session->flags[SWITCH_RTP_FLAG_USE_TIMER] && rtp_session->timer.interval) { switch_core_timer_sync(&rtp_session->timer); + reset_jitter_seq(rtp_session); } @@ -5118,6 +5373,7 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ rtp_session->flags[SWITCH_RTP_FLAG_PROXY_MEDIA] || rtp_session->flags[SWITCH_RTP_FLAG_UDPTL] || (bytes && bytes < 5) || (!bytes && poll_loop)) { bytes = 0; + reset_jitter_seq(rtp_session); return_cng_frame(); } } @@ -5306,11 +5562,26 @@ static int rtp_common_read(switch_rtp_t *rtp_session, switch_payload_t *payload_ switch_cond_next(); continue; } + + + if (!rtp_session->flags[SWITCH_RTP_FLAG_PAUSE] && !rtp_session->flags[SWITCH_RTP_FLAG_DTMF_ON] && !rtp_session->dtmf_data.in_digit_ts + && rtp_session->cng_count > (rtp_session->one_second * 2)) { + + switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(rtp_session->session), SWITCH_LOG_DEBUG1, "%s %s timeout\n", + switch_core_session_get_name(rtp_session->session), rtp_type(rtp_session)); + + rtp_session->stats.inbound.flaws++; + do_mos(rtp_session, SWITCH_FALSE); + } + + rtp_session->cng_count++; return_cng_frame(); } } + rtp_session->cng_count = 0; + if (status == SWITCH_STATUS_BREAK || bytes == 0) { if (!(io_flags & SWITCH_IO_FLAG_SINGLE_READ) && rtp_session->flags[SWITCH_RTP_FLAG_DATAWAIT]) { goto do_continue; @@ -5549,7 +5820,7 @@ SWITCH_DECLARE(switch_status_t) switch_rtp_zerocopy_read_frame(switch_rtp_t *rtp frame->seq = (uint16_t) ntohs((uint16_t) rtp_session->recv_msg.header.seq); frame->ssrc = ntohl(rtp_session->recv_msg.header.ssrc); frame->m = rtp_session->recv_msg.header.m ? SWITCH_TRUE : SWITCH_FALSE; - + #ifdef ENABLE_ZRTP if (zrtp_on && rtp_session->flags[SWITCH_ZRTP_FLAG_SECURE_MITM_RECV]) { zrtp_session_info_t zrtp_session_info; @@ -6342,6 +6613,9 @@ SWITCH_DECLARE(switch_rtp_stats_t *) switch_rtp_get_stats(switch_rtp_t *rtp_sess if (rtp_session->jb) { s->inbound.largest_jb_size = stfu_n_get_most_qlen(rtp_session->jb); } + + do_mos(rtp_session, SWITCH_FALSE); + switch_mutex_unlock(rtp_session->flag_mutex); return s;