* Copyright 2008, Ericsson AB
* By Balint Reczey <balint.reczey@ericsson.com>
*
- * most functions are copied from gtk/rtp_stream.c and gtk/rtp_analisys.c
+ * most functions are copied from ui/gtk/rtp_stream.c and ui/gtk/rtp_analisys.c
* Copyright 2003, Alcatel Business Systems
* By Lars Ruoff <lars.ruoff@gmx.net>
*
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
-#ifdef HAVE_CONFIG_H
-# include "config.h"
-#endif
+#include "config.h"
+#include <stdio.h>
#include <math.h>
#include "globals.h"
#include <epan/tap.h>
-#include "register.h"
#include <string.h>
#include <epan/rtp_pt.h>
#include <epan/addr_resolv.h>
#include <epan/dissectors/packet-rtp.h>
-#include "gtk/rtp_stream.h"
+#include "ui/gtk/rtp_stream.h"
#include "tap-rtp-common.h"
-
+/* XXX: are changes needed to properly handle situations where
+ info_all_data_present == FALSE ?
+ E.G., when captured frames are truncated.
+ */
/****************************************************************************/
/* GCompareFunc style comparison function for _rtp_stream_info */
size_t sourcelen;
guint16 port; /* UDP port */
guint16 padding; /* 2 padding bytes */
-
+
fprintf(file, "#!rtpplay%s %s/%u\n", RTPFILE_VERSION,
get_addr_name(&(strinfo->dest_addr)),
strinfo->dest_port);
tmp_strinfo.rtp_stats.time = 0;
tmp_strinfo.rtp_stats.reg_pt = PT_UNDEFINED;
- /* Get the Setup frame number who set this RTP stream */
- p_conv_data = p_get_proto_data(pinfo->fd, proto_get_id_by_filter_name("rtp"));
- if (p_conv_data)
+ /* Get the Setup frame number who set this RTP stream */
+ p_conv_data = p_get_proto_data(pinfo->fd, proto_get_id_by_filter_name("rtp"));
+ if (p_conv_data)
tmp_strinfo.setup_frame_number = p_conv_data->frame_number;
- else
- tmp_strinfo.setup_frame_number = 0xFFFFFFFF;
+ else
+ tmp_strinfo.setup_frame_number = 0xFFFFFFFF;
strinfo = g_malloc(sizeof(rtp_stream_info_t));
*strinfo = tmp_strinfo; /* memberwise copy of struct */
/* increment the packets counter of all streams */
++(tapinfo->npackets);
-
+
return 1; /* refresh output */
}
else if (tapinfo->mode == TAP_SAVE) {
if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0) {
/* XXX - what if rtpinfo->info_all_data_present is
FALSE, so that we don't *have* all the data? */
- sample.header.rec_time =
+ sample.header.rec_time =
(pinfo->fd->abs_ts.nsecs/1000 + 1000000 - tapinfo->filter_stream_fwd->start_usec)/1000
+ (guint32) (pinfo->fd->abs_ts.secs - tapinfo->filter_stream_fwd->start_sec - 1)*1000;
sample.header.frame_length = rtpinfo->info_data_len;
rtp_write_sample(&sample, tapinfo->save_file);
}
}
+#ifdef __GTK_H__
else if (tapinfo->mode == TAP_MARK) {
-
if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0
|| rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_rev)==0)
{
cf_mark_frame(&cfile, pinfo->fd);
}
}
-
+#endif
return 0;
}
if (clock_map[i].key == key)
return clock_map[i].value;
}
- return 1;
+ return 0;
}
typedef struct _mimetype_and_clock {
{"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] */
get_dyn_pt_clock_rate(gchar *payload_type_str)
{
int i;
-
+
/* Search for matching mimetype in reverse order to avoid false matches
* when pt_mime_name_str is the prefix of payload_type_str */
for (i = NUM_DYN_CLOCK_VALUES - 1; i > -1 ; i--) {
return mimetype_and_clock_map[i].value;
}
- return 1;
+ return 0;
}
/****************************************************************************/
int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
- packet_info *pinfo,
- const struct _rtp_info *rtpinfo)
+ packet_info *pinfo,
+ const struct _rtp_info *rtpinfo)
{
double current_time;
double current_jitter;
- double current_diff;
+ double current_diff = 0;
+ 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) {
+ /* Save the MAC address of the first RTP frame */
+ if( pinfo->dl_src.type == AT_ETHER){
+ COPY_ADDRESS(&(statinfo->first_packet_mac_addr), &(pinfo->dl_src));
+ }
+ 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->lastnominaltime = 0;
+ 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 */
+ /* Chek for duplicates (src mac differs from first_packet_mac_addr) */
+ if( pinfo->dl_src.type == AT_ETHER){
+ if(!ADDRESSES_EQUAL(&(statinfo->first_packet_mac_addr), &(pinfo->dl_src))){
+ statinfo->flags |= STAT_FLAG_DUP_PKT;
+ statinfo->delta = current_time-(statinfo->time);
+ return 0;
+ }
+ }
+
+ /* When calculating expected rtp packets the seq number can wrap around
+ * so we have to count the number of cycles
+ * Variable cycles counts the wraps around in forwarding connection and
+ * under is flag that indicates where we are
+ *
+ * XXX How to determine number of cycles with all possible lost, late
+ * and duplicated packets without any doubt? It seems to me, that
+ * because of all possible combination of late, duplicated or lost
+ * packets, this can only be more or less good approximation
+ *
+ * There are some combinations (rare but theoretically possible),
+ * where below code won't work correctly - statistic may be wrong then.
+ */
+
+ /* So if the current sequence number is less than the start one
+ * we assume, that there is another cycle running
+ */
+ if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
+ statinfo->cycles++;
+ statinfo->under = TRUE;
+ }
+ /* what if the start seq nr was 0? Then the above condition will never
+ * be true, so we add another condition. XXX The problem would arise
+ * if one of the packets with seq nr 0 or 65535 would be lost or late
+ */
+ else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
+ (statinfo->under == FALSE)){
+ statinfo->cycles++;
+ statinfo->under = TRUE;
+ }
+ /* the whole round is over, so reset the flag */
+ else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
+ statinfo->under = FALSE;
+ }
+
+ /* Since it is difficult to count lost, duplicate or late packets separately,
+ * we would like to know at least how many times the sequence number was not ok
+ */
+
+ /* If the current seq number equals the last one or if we are here for
+ * the first time, then it is ok, we just store the current one as the last one
+ */
+ if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
+ statinfo->seq_num = rtpinfo->info_seq_num;
+ /* If the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
+ else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
+ statinfo->seq_num = rtpinfo->info_seq_num;
+ /* Lost packets */
+ else if (statinfo->seq_num+1 < rtpinfo->info_seq_num) {
+ statinfo->seq_num = rtpinfo->info_seq_num;
+ statinfo->sequence++;
+ statinfo->flags |= STAT_FLAG_WRONG_SEQ;
+ }
+ /* Late or duplicated */
+ else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
+ statinfo->sequence++;
+ statinfo->flags |= STAT_FLAG_WRONG_SEQ;
+ }
+
+ /* Check payload type */
if (rtpinfo->info_payload_type == PT_CN
|| rtpinfo->info_payload_type == PT_CN_OLD)
statinfo->flags |= STAT_FLAG_PT_CN;
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);
- }else{ /* dynamic PT */
- if ( rtpinfo->info_payload_type_str != NULL )
- clock_rate = get_dyn_pt_clock_rate(rtpinfo-> info_payload_type_str);
- else
- clock_rate = 1;
+ }else{ /* Dynamic PT */
+ if ( rtpinfo->info_payload_type_str != NULL ){
+ /* Is it a "telephone-event" ?
+ * Timestamp is not increased for telepone-event packets impacting
+ * calculation of Jitter Skew and clock drift.
+ * see 2.2.1 of RFC 4733
+ */
+ if (g_ascii_strncasecmp("telephone-event",rtpinfo->info_payload_type_str,(strlen("telephone-event")))==0){
+ clock_rate = 0;
+ statinfo->flags |= STAT_FLAG_PT_T_EVENT;
+ }else{
+ if(rtpinfo->info_payload_rate !=0){
+ clock_rate = rtpinfo->info_payload_rate;
+ }else{
+ clock_rate = get_dyn_pt_clock_rate(rtpinfo-> info_payload_type_str);
+ }
+ }
+ }else{
+ 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);
+ }
- /* 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;
+ /* 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 -> IP header+8bytes(UDP) */
+ statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + pinfo->iphdrlen + 8;
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 */
+
+ /* 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;
statinfo->bw_start_index++;
if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
};
- statinfo->total_bytes += rtpinfo->info_data_len + 28;
+ /* IP hdr + UDP + RTP */
+ statinfo->total_bytes += rtpinfo->info_data_len + pinfo->iphdrlen + 8;
statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
statinfo->bw_index++;
if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
- /* is this the first packet we got in this direction? */
- if (statinfo->first_packet) {
- statinfo->start_seq_nr = rtpinfo->info_seq_num;
- statinfo->start_time = current_time;
- statinfo->delta = 0;
- statinfo->jitter = 0;
- statinfo->diff = 0;
- statinfo->flags |= STAT_FLAG_FIRST;
- statinfo->first_packet = FALSE;
- }
- /* is it a packet with the mark bit set? */
+ /* Is it a packet with the mark bit set? */
if (rtpinfo->info_marker_set) {
statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
if (rtpinfo->info_timestamp > statinfo->timestamp){
statinfo->flags |= STAT_FLAG_WRONG_TIMESTAMP;
}
}
- /* is it a regular packet? */
+ /* Is it a regular packet? */
if (!(statinfo->flags & STAT_FLAG_FIRST)
&& !(statinfo->flags & STAT_FLAG_MARKER)
&& !(statinfo->flags & STAT_FLAG_PT_CN)
&& !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
&& !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
- /* include it in maximum delta calculation */
+ /* Include it in maximum delta calculation */
if (statinfo->delta > statinfo->max_delta) {
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) */
+ /* Regular payload change? (CN ignored) */
if (!(statinfo->flags & STAT_FLAG_FIRST)
&& !(statinfo->flags & STAT_FLAG_PT_CN)) {
if ((statinfo->pt != statinfo->reg_pt)
}
}
- /* set regular payload*/
+ /* Set regular payload*/
if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
statinfo->reg_pt = statinfo->pt;
}
-
- /* When calculating expected rtp packets the seq number can wrap around
- * so we have to count the number of cycles
- * Variable cycles counts the wraps around in forwarding connection and
- * under is flag that indicates where we are
- *
- * XXX how to determine number of cycles with all possible lost, late
- * and duplicated packets without any doubt? It seems to me, that
- * because of all possible combination of late, duplicated or lost
- * packets, this can only be more or less good approximation
- *
- * There are some combinations (rare but theoretically possible),
- * where below code won't work correctly - statistic may be wrong then.
- */
-
- /* so if the current sequence number is less than the start one
- * we assume, that there is another cycle running */
- if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
- statinfo->cycles++;
- statinfo->under = TRUE;
- }
- /* what if the start seq nr was 0? Then the above condition will never
- * be true, so we add another condition. XXX The problem would arise
- * if one of the packets with seq nr 0 or 65535 would be lost or late */
- else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
- (statinfo->under == FALSE)){
- statinfo->cycles++;
- statinfo->under = TRUE;
- }
- /* the whole round is over, so reset the flag */
- else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
- statinfo->under = FALSE;
- }
-
- /* Since it is difficult to count lost, duplicate or late packets separately,
- * we would like to know at least how many times the sequence number was not ok */
-
- /* if the current seq number equals the last one or if we are here for
- * the first time, then it is ok, we just store the current one as the last one */
- if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
- statinfo->seq_num = rtpinfo->info_seq_num;
- /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
- else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
- statinfo->seq_num = rtpinfo->info_seq_num;
- /* lost packets */
- else if (statinfo->seq_num+1 < rtpinfo->info_seq_num) {
- statinfo->seq_num = rtpinfo->info_seq_num;
- statinfo->sequence++;
- statinfo->flags |= STAT_FLAG_WRONG_SEQ;
- }
- /* late or duplicated */
- else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
- statinfo->sequence++;
- statinfo->flags |= STAT_FLAG_WRONG_SEQ;
- }
statinfo->time = current_time;
statinfo->timestamp = rtpinfo->info_timestamp;
statinfo->stop_seq_nr = rtpinfo->info_seq_num;