diff options
-rw-r--r-- | gtk/rtp_analysis.c | 99 | ||||
-rw-r--r-- | gtk/rtp_analysis.h | 11 | ||||
-rw-r--r-- | tap-rtp-common.c | 123 |
3 files changed, 194 insertions, 39 deletions
diff --git a/gtk/rtp_analysis.c b/gtk/rtp_analysis.c index f0a80bf5c0..7a19105a2e 100644 --- a/gtk/rtp_analysis.c +++ b/gtk/rtp_analysis.c @@ -87,6 +87,7 @@ enum SEQUENCE_COLUMN, DELTA_COLUMN, JITTER_COLUMN, + SKEW_COLUMN, IPBW_COLUMN, MARKER_COLUMN, STATUS_COLUMN, @@ -238,11 +239,12 @@ typedef struct _user_data_t { /* Column titles. */ -static const gchar *titles[9] = { +static const gchar *titles[10] = { "Packet", "Sequence", "Delta (ms)", "Jitter (ms)", + "Skew(ms)", "IP BW (kbps)", "Marker", "Status", @@ -430,7 +432,7 @@ static void rtp_draw(void *prs _U_) /* forward declarations */ static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, - double delta, double jitter, double bandwidth, gchar *status, gboolean marker, + double delta, double jitter, double skew ,double bandwidth, gchar *status, gboolean marker, gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags); static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data, @@ -582,6 +584,7 @@ static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data, pinfo->fd->num, rtpinfo->info_seq_num, 0, 0, + 0, statinfo->bandwidth, status, rtpinfo->info_marker_set, @@ -594,6 +597,7 @@ static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data, pinfo->fd->num, rtpinfo->info_seq_num, statinfo->delta, statinfo->jitter, + statinfo->skew, statinfo->bandwidth, status, rtpinfo->info_marker_set, @@ -2885,46 +2889,86 @@ static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data) /* when we are finished with redisection, we add the label for the statistic */ static void draw_stat(user_data_t *user_data) { - gchar label_max[200]; + gchar label_max[300]; guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536) - user_data->forward.statinfo.start_seq_nr + 1; guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536) - user_data->reversed.statinfo.start_seq_nr + 1; - gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr; - gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr; + guint32 f_total_nr = user_data->forward.statinfo.total_nr; + guint32 r_total_nr = user_data->reversed.statinfo.total_nr; + gint32 f_lost = f_expected - f_total_nr; + gint32 r_lost = r_expected - r_total_nr; + double f_sumt = user_data->forward.statinfo.sumt; + double f_sumTS = user_data->forward.statinfo.sumTS; + double f_sumt2 = user_data->forward.statinfo.sumt2; + double f_sumtTS = user_data->forward.statinfo.sumtTS; + + double r_sumt = user_data->reversed.statinfo.sumt; + double r_sumTS = user_data->reversed.statinfo.sumTS; + double r_sumt2 = user_data->reversed.statinfo.sumt2; + double r_sumtTS = user_data->reversed.statinfo.sumtTS; double f_perc, r_perc; + double f_clock_drift = 1.0; + double r_clock_drift = 1.0; + double f_duration = user_data->forward.statinfo.time - user_data->forward.statinfo.start_time; + double r_duration = user_data->reversed.statinfo.time - user_data->reversed.statinfo.start_time; + guint32 f_clock_rate = user_data->forward.statinfo.clock_rate; + guint32 r_clock_rate = user_data->reversed.statinfo.clock_rate; + + if (f_clock_rate == 0){ + f_clock_rate = 1; + } + + if (r_clock_rate == 0){ + r_clock_rate = 1; + } + if (f_expected){ f_perc = (double)(f_lost*100)/(double)f_expected; } else { f_perc = 0; } - if (r_expected){ - r_perc = (double)(r_lost*100)/(double)r_expected; - } else { - r_perc = 0; - } + if (r_expected){ + r_perc = (double)(r_lost*100)/(double)r_expected; + } else { + r_perc = 0; + } + if ((f_total_nr >0)&&(f_sumt2 > 0)){ + f_clock_drift = (f_total_nr * f_sumtTS - f_sumt * f_sumTS) / (f_total_nr * f_sumt2 - f_sumt * f_sumt); + } + if ((r_total_nr >0)&&(r_sumt2 > 0)){ + r_clock_drift = (r_total_nr * r_sumtTS - r_sumt * r_sumTS) / (r_total_nr * r_sumt2 - r_sumt * r_sumt); + } g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n" "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n" + "Max skew = %.2f ms.\n" "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)" - " Sequence errors = %u", + " Sequence errors = %u \n" + "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)", user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr, user_data->forward.statinfo.max_jitter,user_data->forward.statinfo.mean_jitter, - user_data->forward.statinfo.total_nr, f_expected, f_lost, f_perc, - user_data->forward.statinfo.sequence); + user_data->forward.statinfo.max_skew, + f_expected, f_expected, f_lost, f_perc, + user_data->forward.statinfo.sequence, + f_duration/1000,f_duration*(f_clock_drift-1.0),f_clock_drift*f_clock_rate,100.0*(f_clock_drift-1.0)); gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max); + gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_fwd),TRUE); - g_snprintf(label_max, sizeof(label_max), "Max delta = %f ms at packet no. %u \n" + g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n" "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n" "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)" - " Sequence errors = %u", + " Sequence errors = %u \n" + "Duration %.0f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)", user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr, user_data->reversed.statinfo.max_jitter,user_data->reversed.statinfo.mean_jitter, - user_data->reversed.statinfo.total_nr, r_expected, r_lost, r_perc, - user_data->reversed.statinfo.sequence); + r_expected, r_expected, r_lost, r_perc, + user_data->reversed.statinfo.sequence, + r_duration/1000,r_duration*(r_clock_drift-1.0),r_clock_drift*r_clock_rate,100.0*(r_clock_drift-1.0)); gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max); + gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_rev),TRUE); return ; } @@ -2934,7 +2978,7 @@ static void draw_stat(user_data_t *user_data) /****************************************************************************/ /* append a line to list */ static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, - double delta, double jitter, double bandwidth, gchar *status, gboolean marker, + double delta, double jitter,double skew, double bandwidth, gchar *status, gboolean marker, gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags) { GtkListStore *list_store; @@ -2961,6 +3005,7 @@ static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number SEQUENCE_COLUMN, seq_num, DELTA_COLUMN, delta, JITTER_COLUMN, jitter, + SKEW_COLUMN, skew, IPBW_COLUMN, bandwidth, MARKER_COLUMN, marker, STATUS_COLUMN, (char *)status, @@ -3026,6 +3071,7 @@ GtkWidget* create_list(user_data_t* user_data) G_TYPE_UINT, /* Sequence */ G_TYPE_FLOAT, /* Delta(ms) */ G_TYPE_FLOAT, /* Filtered Jitter(ms) */ + G_TYPE_FLOAT, /* Skew(ms) */ G_TYPE_FLOAT, /* IP BW(kbps) */ G_TYPE_BOOLEAN, /* Marker */ G_TYPE_STRING, /* Status */ @@ -3117,6 +3163,23 @@ GtkWidget* create_list(user_data_t* user_data) gtk_tree_view_column_set_min_width(column, 110); gtk_tree_view_append_column (list_view, column); + /* Skew(ms). */ + renderer = gtk_cell_renderer_text_new (); + column = gtk_tree_view_column_new_with_attributes ("Skew(ms)", renderer, + "text", SKEW_COLUMN, + "foreground", FOREGROUND_COLOR_COL, + "background", BACKGROUND_COLOR_COL, + NULL); + + gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func, + GINT_TO_POINTER(SKEW_COLUMN), NULL); + + gtk_tree_view_column_set_sort_column_id(column, SKEW_COLUMN); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED); + gtk_tree_view_column_set_min_width(column, 110); + gtk_tree_view_append_column (list_view, column); + /* IP BW(kbps). */ renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer, diff --git a/gtk/rtp_analysis.h b/gtk/rtp_analysis.h index f84407054d..91e778e0f2 100644 --- a/gtk/rtp_analysis.h +++ b/gtk/rtp_analysis.h @@ -71,19 +71,28 @@ typedef struct _tap_rtp_stat_t { guint32 flags; /* see STAT_FLAG-defines below */ guint16 seq_num; guint32 timestamp; + guint32 first_timestamp; guint32 delta_timestamp; double bandwidth; bw_history_item bw_history[BUFF_BW]; guint16 bw_start_index; guint16 bw_index; guint32 total_bytes; + guint32 clock_rate; double delta; double jitter; double diff; - double time; + double skew; + double sumt; + double sumTS; + double sumt2; + double sumtTS; + double time; /* Unit is ms */ double start_time; + double lastnominaltime; double max_delta; double max_jitter; + double max_skew; double mean_jitter; guint32 max_nr; guint16 start_seq_nr; diff --git a/tap-rtp-common.c b/tap-rtp-common.c index b8d8a60825..45ba9c0c7c 100644 --- a/tap-rtp-common.c +++ b/tap-rtp-common.c @@ -357,7 +357,7 @@ get_clock_rate(guint32 key) if (clock_map[i].key == key) return clock_map[i].value; } - return 1; + return 0; } typedef struct _mimetype_and_clock { @@ -399,7 +399,7 @@ static const mimetype_and_clock mimetype_and_clock_map[] = { {"GSM-EFR", 8000}, /* [RFC3551] */ {"H263-1998", 90000}, /* [RFC2429],[RFC3555] */ {"H263-2000", 90000}, /* [RFC2429],[RFC3555] */ - {"H264", 90000}, /* [RFC3984] */ + {"H264", 90000}, /* [RFC3984] */ {"MP1S", 90000}, /* [RFC2250],[RFC3555] */ {"MP2P", 90000}, /* [RFC2250],[RFC3555] */ {"MP4V-ES", 90000}, /* [RFC3016] */ @@ -427,7 +427,7 @@ get_dyn_pt_clock_rate(gchar *payload_type_str) return mimetype_and_clock_map[i].value; } - return 1; + return 0; } /****************************************************************************/ @@ -438,9 +438,46 @@ int rtp_packet_analyse(tap_rtp_stat_t *statinfo, double current_time; double current_jitter; double current_diff; + double nominaltime; + double arrivaltime; /* Time relative to start_time */ double expected_time; + double absskew; guint32 clock_rate; + /* Store the current time */ + current_time = nstime_to_msec(&pinfo->fd->rel_ts); + + /* Is this the first packet we got in this direction? */ + if (statinfo->first_packet) { + statinfo->start_seq_nr = rtpinfo->info_seq_num; + statinfo->stop_seq_nr = rtpinfo->info_seq_num; + statinfo->seq_num = rtpinfo->info_seq_num; + statinfo->start_time = current_time; + statinfo->timestamp = rtpinfo->info_timestamp; + statinfo->first_timestamp = rtpinfo->info_timestamp; + statinfo->time = current_time; + statinfo->pt = rtpinfo->info_payload_type; + statinfo->reg_pt = rtpinfo->info_payload_type; + statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + 28; + statinfo->bw_history[statinfo->bw_index].time = current_time; + statinfo->bw_index++; + statinfo->total_bytes += rtpinfo->info_data_len + 28; + statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000; + /* Not needed ? initialised to zero? */ + statinfo->delta = 0; + statinfo->jitter = 0; + statinfo->diff = 0; + + statinfo->total_nr++; + statinfo->flags |= STAT_FLAG_FIRST; + if (rtpinfo->info_marker_set) { + statinfo->flags |= STAT_FLAG_MARKER; + } + statinfo->first_packet = FALSE; + return 0; + } + + /* Reset flags */ statinfo->flags = 0; /* check payload type */ @@ -453,10 +490,10 @@ int rtp_packet_analyse(tap_rtp_stat_t *statinfo, if (rtpinfo->info_payload_type != statinfo->pt) statinfo->flags |= STAT_FLAG_PT_CHANGE; statinfo->pt = rtpinfo->info_payload_type; + /* - * XXX - should "get_clock_rate()" return 0 for unknown - * payload types, presumably meaning that we should - * just ignore this packet? + * Return 0 for unknown payload types + * Ignore jitter calculation for clockrate = 0 */ if (statinfo->pt < 96 ){ clock_rate = get_clock_rate(statinfo->pt); @@ -464,23 +501,67 @@ int rtp_packet_analyse(tap_rtp_stat_t *statinfo, if ( rtpinfo->info_payload_type_str != NULL ) clock_rate = get_dyn_pt_clock_rate(rtpinfo-> info_payload_type_str); else - clock_rate = 1; + clock_rate = 0; } - /* Store the current time and calculate the current jitter(in ms) */ - current_time = nstime_to_msec(&pinfo->fd->rel_ts); - /* Expected time is last arrival time + the timestamp difference divided by the sampling clock( /1000 to get ms) */ - expected_time = statinfo->time + ((double)(rtpinfo->info_timestamp)-(double)(statinfo->timestamp))/(clock_rate/1000); - current_diff = fabs(current_time - expected_time); - current_jitter = (15 * statinfo->jitter + current_diff) / 16; + /* Handle wraparound ? */ + arrivaltime = current_time - statinfo->start_time; - statinfo->delta = current_time-(statinfo->time); - statinfo->jitter = current_jitter; - statinfo->diff = current_diff; + if (statinfo->first_timestamp > rtpinfo->info_timestamp){ + /* Handle wraparound */ + nominaltime = (double)(rtpinfo->info_timestamp + 0xffffffff - statinfo->first_timestamp + 1); + }else{ + nominaltime = (double)(rtpinfo->info_timestamp - statinfo->first_timestamp); + } + + /* Can only analyze defined sampling rates */ + if (clock_rate != 0) { + statinfo->clock_rate = clock_rate; + /* Convert from sampling clock to ms */ + nominaltime = nominaltime /(clock_rate/1000); + + /* Calculate the current jitter(in ms) */ + if (!statinfo->first_packet) { + expected_time = statinfo->time + (nominaltime - statinfo->lastnominaltime); + current_diff = fabs(current_time - expected_time); + current_jitter = (15 * statinfo->jitter + current_diff) / 16; + + statinfo->delta = current_time-(statinfo->time); + statinfo->jitter = current_jitter; + statinfo->diff = current_diff; + } + statinfo->lastnominaltime = nominaltime; + /* Calculate skew, i.e. absolute jitter that also catches clock drift + * Skew is positive if TS (nominal) is too fast + */ + statinfo->skew = nominaltime - arrivaltime; + absskew = fabs(statinfo->skew); + if(absskew > fabs(statinfo->max_skew)){ + statinfo->max_skew = statinfo->skew; + } + /* Gather data for calculation of average, minimum and maximum framerate based on timestamp */ +#if 0 + if (numPackets > 0 && (!hardPayloadType || !alternatePayloadType)) { + /* Skip first packet and possibly alternate payload type packets */ + double dt; + dt = nominaltime - statinfo->lastnominaltime; + sumdt += 1.0 * dt; + numdt += (dt != 0 ? 1 : 0); + mindt = (dt < mindt ? dt : mindt); + maxdt = (dt > maxdt ? dt : maxdt); + } +#endif + /* Gather data for calculation of skew least square */ + statinfo->sumt += 1.0 * current_time; + statinfo->sumTS += 1.0 * nominaltime; + statinfo->sumt2 += 1.0 * current_time * current_time; + statinfo->sumtTS += 1.0 * current_time * nominaltime; + } /* calculate the BW in Kbps adding the IP+UDP header to the RTP -> 20bytes(IP)+8bytes(UDP) = 28bytes */ statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + 28; statinfo->bw_history[statinfo->bw_index].time = current_time; + /* check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */ while ((statinfo->bw_history[statinfo->bw_start_index].time+1000/* ms */)<current_time){ statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes; @@ -524,11 +605,13 @@ int rtp_packet_analyse(tap_rtp_stat_t *statinfo, statinfo->max_delta = statinfo->delta; statinfo->max_nr = pinfo->fd->num; } - /* maximum and mean jitter calculation */ - if (statinfo->jitter > statinfo->max_jitter) { - statinfo->max_jitter = statinfo->jitter; + if (clock_rate != 0) { + /* maximum and mean jitter calculation */ + if (statinfo->jitter > statinfo->max_jitter) { + statinfo->max_jitter = statinfo->jitter; + } + statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1); } - statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1); } /* regular payload change? (CN ignored) */ if (!(statinfo->flags & STAT_FLAG_FIRST) |