nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* tap-rtp-common.c |
2 | * RTP stream handler functions used by tshark and wireshark |
||
3 | * |
||
4 | * Copyright 2008, Ericsson AB |
||
5 | * By Balint Reczey <balint.reczey@ericsson.com> |
||
6 | * |
||
7 | * most functions are copied from ui/gtk/rtp_stream.c and ui/gtk/rtp_analysis.c |
||
8 | * Copyright 2003, Alcatel Business Systems |
||
9 | * By Lars Ruoff <lars.ruoff@gmx.net> |
||
10 | * |
||
11 | * Wireshark - Network traffic analyzer |
||
12 | * By Gerald Combs <gerald@wireshark.org> |
||
13 | * Copyright 1998 Gerald Combs |
||
14 | * |
||
15 | * This program is free software; you can redistribute it and/or |
||
16 | * modify it under the terms of the GNU General Public License |
||
17 | * as published by the Free Software Foundation; either version 2 |
||
18 | * of the License, or (at your option) any later version. |
||
19 | * |
||
20 | * This program is distributed in the hope that it will be useful, |
||
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
23 | * GNU General Public License for more details. |
||
24 | * |
||
25 | * You should have received a copy of the GNU General Public License |
||
26 | * along with this program; if not, write to the Free Software |
||
27 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
||
28 | */ |
||
29 | |||
30 | #include "config.h" |
||
31 | |||
32 | #include <glib.h> |
||
33 | |||
34 | #include <math.h> |
||
35 | #include "globals.h" |
||
36 | |||
37 | #include <string.h> |
||
38 | #include <epan/rtp_pt.h> |
||
39 | #include <epan/addr_resolv.h> |
||
40 | #include <epan/proto_data.h> |
||
41 | #include <epan/dissectors/packet-rtp.h> |
||
42 | #include "rtp_stream.h" |
||
43 | #include "tap-rtp-common.h" |
||
44 | |||
45 | /* XXX: are changes needed to properly handle situations where |
||
46 | info_all_data_present == FALSE ? |
||
47 | E.G., when captured frames are truncated. |
||
48 | */ |
||
49 | |||
50 | /****************************************************************************/ |
||
51 | /* Type for storing and writing rtpdump information */ |
||
52 | typedef struct st_rtpdump_info { |
||
53 | double rec_time; /**< milliseconds since start of recording */ |
||
54 | guint16 num_samples; /**< number of bytes in *frame */ |
||
55 | const guint8 *samples; /**< data bytes */ |
||
56 | } rtpdump_info_t; |
||
57 | |||
58 | /****************************************************************************/ |
||
59 | /* GCompareFunc style comparison function for _rtp_stream_info */ |
||
60 | static gint rtp_stream_info_cmp(gconstpointer aa, gconstpointer bb) |
||
61 | { |
||
62 | const struct _rtp_stream_info* a = (const struct _rtp_stream_info*)aa; |
||
63 | const struct _rtp_stream_info* b = (const struct _rtp_stream_info*)bb; |
||
64 | |||
65 | if (a==b) |
||
66 | return 0; |
||
67 | if (a==NULL || b==NULL) |
||
68 | return 1; |
||
69 | if (addresses_equal(&(a->src_addr), &(b->src_addr)) |
||
70 | && (a->src_port == b->src_port) |
||
71 | && addresses_equal(&(a->dest_addr), &(b->dest_addr)) |
||
72 | && (a->dest_port == b->dest_port) |
||
73 | && (a->ssrc == b->ssrc)) |
||
74 | return 0; |
||
75 | else |
||
76 | return 1; |
||
77 | } |
||
78 | |||
79 | |||
80 | /****************************************************************************/ |
||
81 | /* when there is a [re]reading of packet's */ |
||
82 | void rtpstream_reset(rtpstream_tapinfo_t *tapinfo) |
||
83 | { |
||
84 | GList* list; |
||
85 | |||
86 | if (tapinfo->mode == TAP_ANALYSE) { |
||
87 | /* free the data items first */ |
||
88 | list = g_list_first(tapinfo->strinfo_list); |
||
89 | while (list) |
||
90 | { |
||
91 | g_free(list->data); |
||
92 | list = g_list_next(list); |
||
93 | } |
||
94 | g_list_free(tapinfo->strinfo_list); |
||
95 | tapinfo->strinfo_list = NULL; |
||
96 | tapinfo->nstreams = 0; |
||
97 | tapinfo->npackets = 0; |
||
98 | } |
||
99 | |||
100 | return; |
||
101 | } |
||
102 | |||
103 | void rtpstream_reset_cb(void *arg) |
||
104 | { |
||
105 | rtpstream_tapinfo_t *ti =(rtpstream_tapinfo_t *)arg; |
||
106 | if (ti->tap_reset) { |
||
107 | /* Give listeners a chance to cleanup references. */ |
||
108 | ti->tap_reset(ti); |
||
109 | } |
||
110 | rtpstream_reset(ti); |
||
111 | } |
||
112 | |||
113 | /* |
||
114 | * rtpdump file format |
||
115 | * |
||
116 | * The file starts with the tool to be used for playing this file, |
||
117 | * the multicast/unicast receive address and the port. |
||
118 | * |
||
119 | * #!rtpplay1.0 224.2.0.1/3456\n |
||
120 | * |
||
121 | * This is followed by one binary header (RD_hdr_t) and one RD_packet_t |
||
122 | * structure for each received packet. All fields are in network byte |
||
123 | * order. We don't need the source IP address since we can do mapping |
||
124 | * based on SSRC. This saves (a little) space, avoids non-IPv4 |
||
125 | * problems and privacy/security concerns. The header is followed by |
||
126 | * the RTP/RTCP header and (optionally) the actual payload. |
||
127 | */ |
||
128 | |||
129 | #define RTPFILE_VERSION "1.0" |
||
130 | |||
131 | /* |
||
132 | * Write a header to the current output file. |
||
133 | * The header consists of an identifying string, followed |
||
134 | * by a binary structure. |
||
135 | */ |
||
136 | void rtp_write_header(rtp_stream_info_t *strinfo, FILE *file) |
||
137 | { |
||
138 | guint32 start_sec; /* start of recording (GMT) (seconds) */ |
||
139 | guint32 start_usec; /* start of recording (GMT) (microseconds)*/ |
||
140 | guint32 source; /* network source (multicast address) */ |
||
141 | size_t sourcelen; |
||
142 | guint16 port; /* UDP port */ |
||
143 | guint16 padding; /* 2 padding bytes */ |
||
144 | char* addr_str = address_to_display(NULL, &(strinfo->dest_addr)); |
||
145 | |||
146 | fprintf(file, "#!rtpplay%s %s/%u\n", RTPFILE_VERSION, |
||
147 | addr_str, |
||
148 | strinfo->dest_port); |
||
149 | wmem_free(NULL, addr_str); |
||
150 | |||
151 | start_sec = g_htonl(strinfo->start_fd->abs_ts.secs); |
||
152 | start_usec = g_htonl(strinfo->start_fd->abs_ts.nsecs / 1000000); |
||
153 | /* rtpdump only accepts guint32 as source, will be fake for IPv6 */ |
||
154 | memset(&source, 0, sizeof source); |
||
155 | sourcelen = strinfo->src_addr.len; |
||
156 | if (sourcelen > sizeof source) |
||
157 | sourcelen = sizeof source; |
||
158 | memcpy(&source, strinfo->src_addr.data, sourcelen); |
||
159 | port = g_htons(strinfo->src_port); |
||
160 | padding = 0; |
||
161 | |||
162 | if (fwrite(&start_sec, 4, 1, file) == 0) |
||
163 | return; |
||
164 | if (fwrite(&start_usec, 4, 1, file) == 0) |
||
165 | return; |
||
166 | if (fwrite(&source, 4, 1, file) == 0) |
||
167 | return; |
||
168 | if (fwrite(&port, 2, 1, file) == 0) |
||
169 | return; |
||
170 | if (fwrite(&padding, 2, 1, file) == 0) |
||
171 | return; |
||
172 | } |
||
173 | |||
174 | /* utility function for writing a sample to file in rtpdump -F dump format (.rtp)*/ |
||
175 | static void rtp_write_sample(rtpdump_info_t* rtpdump_info, FILE* file) |
||
176 | { |
||
177 | guint16 length; /* length of packet, including this header (may |
||
178 | be smaller than plen if not whole packet recorded) */ |
||
179 | guint16 plen; /* actual header+payload length for RTP, 0 for RTCP */ |
||
180 | guint32 offset; /* milliseconds since the start of recording */ |
||
181 | |||
182 | length = g_htons(rtpdump_info->num_samples + 8); |
||
183 | plen = g_htons(rtpdump_info->num_samples); |
||
184 | offset = g_htonl(rtpdump_info->rec_time); |
||
185 | |||
186 | if (fwrite(&length, 2, 1, file) == 0) |
||
187 | return; |
||
188 | if (fwrite(&plen, 2, 1, file) == 0) |
||
189 | return; |
||
190 | if (fwrite(&offset, 4, 1, file) == 0) |
||
191 | return; |
||
192 | if (fwrite(rtpdump_info->samples, rtpdump_info->num_samples, 1, file) == 0) |
||
193 | return; |
||
194 | } |
||
195 | |||
196 | |||
197 | /****************************************************************************/ |
||
198 | /* whenever a RTP packet is seen by the tap listener */ |
||
199 | int rtpstream_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *arg2) |
||
200 | { |
||
201 | rtpstream_tapinfo_t *tapinfo = (rtpstream_tapinfo_t *)arg; |
||
202 | const struct _rtp_info *rtpinfo = (const struct _rtp_info *)arg2; |
||
203 | rtp_stream_info_t new_stream_info; |
||
204 | rtp_stream_info_t *stream_info = NULL; |
||
205 | GList* list; |
||
206 | rtpdump_info_t rtpdump_info; |
||
207 | |||
208 | struct _rtp_conversation_info *p_conv_data = NULL; |
||
209 | |||
210 | /* gather infos on the stream this packet is part of */ |
||
211 | memset(&new_stream_info, 0, sizeof(rtp_stream_info_t)); |
||
212 | copy_address(&(new_stream_info.src_addr), &(pinfo->src)); |
||
213 | new_stream_info.src_port = pinfo->srcport; |
||
214 | copy_address(&(new_stream_info.dest_addr), &(pinfo->dst)); |
||
215 | new_stream_info.dest_port = pinfo->destport; |
||
216 | new_stream_info.ssrc = rtpinfo->info_sync_src; |
||
217 | new_stream_info.payload_type = rtpinfo->info_payload_type; |
||
218 | new_stream_info.payload_type_name = g_strdup(rtpinfo->info_payload_type_str); |
||
219 | |||
220 | if (tapinfo->mode == TAP_ANALYSE) { |
||
221 | /* check whether we already have a stream with these parameters in the list */ |
||
222 | list = g_list_first(tapinfo->strinfo_list); |
||
223 | while (list) |
||
224 | { |
||
225 | if (rtp_stream_info_cmp(&new_stream_info, (rtp_stream_info_t*)(list->data))==0) |
||
226 | { |
||
227 | stream_info = (rtp_stream_info_t*)(list->data); /*found!*/ |
||
228 | break; |
||
229 | } |
||
230 | list = g_list_next(list); |
||
231 | } |
||
232 | |||
233 | /* not in the list? then create a new entry */ |
||
234 | if (!stream_info) { |
||
235 | new_stream_info.start_fd = pinfo->fd; |
||
236 | new_stream_info.start_rel_time = pinfo->rel_ts; |
||
237 | |||
238 | /* reset RTP stats */ |
||
239 | new_stream_info.rtp_stats.first_packet = TRUE; |
||
240 | new_stream_info.rtp_stats.reg_pt = PT_UNDEFINED; |
||
241 | |||
242 | /* Get the Setup frame number who set this RTP stream */ |
||
243 | p_conv_data = (struct _rtp_conversation_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_get_id_by_filter_name("rtp"), 0); |
||
244 | if (p_conv_data) |
||
245 | new_stream_info.setup_frame_number = p_conv_data->frame_number; |
||
246 | else |
||
247 | new_stream_info.setup_frame_number = 0xFFFFFFFF; |
||
248 | |||
249 | stream_info = g_new(rtp_stream_info_t,1); |
||
250 | *stream_info = new_stream_info; /* memberwise copy of struct */ |
||
251 | tapinfo->strinfo_list = g_list_append(tapinfo->strinfo_list, stream_info); |
||
252 | } |
||
253 | |||
254 | /* get RTP stats for the packet */ |
||
255 | rtp_packet_analyse(&(stream_info->rtp_stats), pinfo, rtpinfo); |
||
256 | if (stream_info->rtp_stats.flags & STAT_FLAG_WRONG_TIMESTAMP |
||
257 | || stream_info->rtp_stats.flags & STAT_FLAG_WRONG_SEQ) |
||
258 | stream_info->problem = TRUE; |
||
259 | |||
260 | |||
261 | /* increment the packets counter for this stream */ |
||
262 | ++(stream_info->packet_count); |
||
263 | stream_info->stop_rel_time = pinfo->rel_ts; |
||
264 | |||
265 | /* increment the packets counter of all streams */ |
||
266 | ++(tapinfo->npackets); |
||
267 | |||
268 | return 1; /* refresh output */ |
||
269 | } |
||
270 | else if (tapinfo->mode == TAP_SAVE) { |
||
271 | if (rtp_stream_info_cmp(&new_stream_info, tapinfo->filter_stream_fwd)==0) { |
||
272 | /* XXX - what if rtpinfo->info_all_data_present is |
||
273 | FALSE, so that we don't *have* all the data? */ |
||
274 | rtpdump_info.rec_time = nstime_to_msec(&pinfo->abs_ts) - |
||
275 | nstime_to_msec(&tapinfo->filter_stream_fwd->start_fd->abs_ts); |
||
276 | rtpdump_info.num_samples = rtpinfo->info_data_len; |
||
277 | rtpdump_info.samples = rtpinfo->info_data; |
||
278 | rtp_write_sample(&rtpdump_info, tapinfo->save_file); |
||
279 | } |
||
280 | } |
||
281 | else if (tapinfo->mode == TAP_MARK && tapinfo->tap_mark_packet) { |
||
282 | if (rtp_stream_info_cmp(&new_stream_info, tapinfo->filter_stream_fwd)==0 |
||
283 | || rtp_stream_info_cmp(&new_stream_info, tapinfo->filter_stream_rev)==0) |
||
284 | { |
||
285 | tapinfo->tap_mark_packet(tapinfo, pinfo->fd); |
||
286 | } |
||
287 | } |
||
288 | return 0; |
||
289 | } |
||
290 | |||
291 | |||
292 | typedef struct _key_value { |
||
293 | guint32 key; |
||
294 | guint32 value; |
||
295 | } key_value; |
||
296 | |||
297 | |||
298 | /* RTP sampling clock rates for fixed payload types as defined in |
||
299 | http://www.iana.org/assignments/rtp-parameters */ |
||
300 | static const key_value clock_map[] = { |
||
301 | {PT_PCMU, 8000}, |
||
302 | {PT_1016, 8000}, |
||
303 | {PT_G721, 8000}, |
||
304 | {PT_GSM, 8000}, |
||
305 | {PT_G723, 8000}, |
||
306 | {PT_DVI4_8000, 8000}, |
||
307 | {PT_DVI4_16000, 16000}, |
||
308 | {PT_LPC, 8000}, |
||
309 | {PT_PCMA, 8000}, |
||
310 | {PT_G722, 8000}, |
||
311 | {PT_L16_STEREO, 44100}, |
||
312 | {PT_L16_MONO, 44100}, |
||
313 | {PT_QCELP, 8000}, |
||
314 | {PT_CN, 8000}, |
||
315 | {PT_MPA, 90000}, |
||
316 | {PT_G728, 8000}, |
||
317 | {PT_G728, 8000}, |
||
318 | {PT_DVI4_11025, 11025}, |
||
319 | {PT_DVI4_22050, 22050}, |
||
320 | {PT_G729, 8000}, |
||
321 | {PT_CN_OLD, 8000}, |
||
322 | {PT_CELB, 90000}, |
||
323 | {PT_JPEG, 90000}, |
||
324 | {PT_NV, 90000}, |
||
325 | {PT_H261, 90000}, |
||
326 | {PT_MPV, 90000}, |
||
327 | {PT_MP2T, 90000}, |
||
328 | {PT_H263, 90000}, |
||
329 | }; |
||
330 | |||
331 | #define NUM_CLOCK_VALUES (sizeof clock_map / sizeof clock_map[0]) |
||
332 | |||
333 | static guint32 |
||
334 | get_clock_rate(guint32 key) |
||
335 | { |
||
336 | size_t i; |
||
337 | |||
338 | for (i = 0; i < NUM_CLOCK_VALUES; i++) { |
||
339 | if (clock_map[i].key == key) |
||
340 | return clock_map[i].value; |
||
341 | } |
||
342 | return 0; |
||
343 | } |
||
344 | |||
345 | typedef struct _mimetype_and_clock { |
||
346 | const gchar *pt_mime_name_str; |
||
347 | guint32 value; |
||
348 | } mimetype_and_clock; |
||
349 | /* RTP sampling clock rates for |
||
350 | "In addition to the RTP payload formats (encodings) listed in the RTP |
||
351 | Payload Types table, there are additional payload formats that do not |
||
352 | have static RTP payload types assigned but instead use dynamic payload |
||
353 | type number assignment. Each payload format is named by a registered |
||
354 | MIME subtype" |
||
355 | http://www.iana.org/assignments/rtp-parameters. |
||
356 | |||
357 | NOTE: Please keep the mimetypes in case insensitive alphabetical order. |
||
358 | */ |
||
359 | static const mimetype_and_clock mimetype_and_clock_map[] = { |
||
360 | {"AMR", 8000}, /* [RFC4867][RFC3267] */ |
||
361 | {"AMR-WB", 16000}, /* [RFC4867][RFC3267] */ |
||
362 | {"BMPEG", 90000}, /* [RFC2343],[RFC3555] */ |
||
363 | {"BT656", 90000}, /* [RFC2431],[RFC3555] */ |
||
364 | {"DV", 90000}, /* [RFC3189] */ |
||
365 | {"EVRC", 8000}, /* [RFC3558] */ |
||
366 | {"EVRC0", 8000}, /* [RFC4788] */ |
||
367 | {"EVRC1", 8000}, /* [RFC4788] */ |
||
368 | {"EVRCB", 8000}, /* [RFC4788] */ |
||
369 | {"EVRCB0", 8000}, /* [RFC4788] */ |
||
370 | {"EVRCB1", 8000}, /* [RFC4788] */ |
||
371 | {"EVRCWB", 16000}, /* [RFC5188] */ |
||
372 | {"EVRCWB0", 16000}, /* [RFC5188] */ |
||
373 | {"EVRCWB1", 16000}, /* [RFC5188] */ |
||
374 | {"G7221", 16000}, /* [RFC3047] */ |
||
375 | {"G726-16", 8000}, /* [RFC3551][RFC4856] */ |
||
376 | {"G726-24", 8000}, /* [RFC3551][RFC4856] */ |
||
377 | {"G726-32", 8000}, /* [RFC3551][RFC4856] */ |
||
378 | {"G726-40", 8000}, /* [RFC3551][RFC4856] */ |
||
379 | {"G729D", 8000}, /* [RFC3551][RFC4856] */ |
||
380 | {"G729E", 8000}, /* [RFC3551][RFC4856] */ |
||
381 | {"GSM-EFR", 8000}, /* [RFC3551] */ |
||
382 | {"H263-1998", 90000}, /* [RFC2429],[RFC3555] */ |
||
383 | {"H263-2000", 90000}, /* [RFC2429],[RFC3555] */ |
||
384 | {"H264", 90000}, /* [RFC3984] */ |
||
385 | {"MP1S", 90000}, /* [RFC2250],[RFC3555] */ |
||
386 | {"MP2P", 90000}, /* [RFC2250],[RFC3555] */ |
||
387 | {"MP4V-ES", 90000}, /* [RFC3016] */ |
||
388 | {"mpa-robust", 90000}, /* [RFC3119] */ |
||
389 | {"pointer", 90000}, /* [RFC2862] */ |
||
390 | {"raw", 90000}, /* [RFC4175] */ |
||
391 | {"red", 1000}, /* [RFC4102] */ |
||
392 | {"SMV", 8000}, /* [RFC3558] */ |
||
393 | {"SMV0", 8000}, /* [RFC3558] */ |
||
394 | {"t140", 1000}, /* [RFC4103] */ |
||
395 | {"telephone-event", 8000}, /* [RFC4733] */ |
||
396 | }; |
||
397 | |||
398 | #define NUM_DYN_CLOCK_VALUES (sizeof mimetype_and_clock_map / sizeof mimetype_and_clock_map[0]) |
||
399 | |||
400 | static guint32 |
||
401 | get_dyn_pt_clock_rate(const gchar *payload_type_str) |
||
402 | { |
||
403 | int i; |
||
404 | |||
405 | /* Search for matching mimetype in reverse order to avoid false matches |
||
406 | * when pt_mime_name_str is the prefix of payload_type_str */ |
||
407 | for (i = NUM_DYN_CLOCK_VALUES - 1; i > -1 ; i--) { |
||
408 | if (g_ascii_strncasecmp(mimetype_and_clock_map[i].pt_mime_name_str,payload_type_str,(strlen(mimetype_and_clock_map[i].pt_mime_name_str))) == 0) |
||
409 | return mimetype_and_clock_map[i].value; |
||
410 | } |
||
411 | |||
412 | return 0; |
||
413 | } |
||
414 | |||
415 | /****************************************************************************/ |
||
416 | void |
||
417 | rtp_packet_analyse(tap_rtp_stat_t *statinfo, |
||
418 | packet_info *pinfo, |
||
419 | const struct _rtp_info *rtpinfo) |
||
420 | { |
||
421 | double current_time; |
||
422 | double current_jitter; |
||
423 | double current_diff = 0; |
||
424 | double nominaltime; |
||
425 | double arrivaltime; /* Time relative to start_time */ |
||
426 | double expected_time; |
||
427 | double absskew; |
||
428 | guint32 clock_rate; |
||
429 | |||
430 | /* Store the current time */ |
||
431 | current_time = nstime_to_msec(&pinfo->rel_ts); |
||
432 | |||
433 | /* Is this the first packet we got in this direction? */ |
||
434 | if (statinfo->first_packet) { |
||
435 | /* Save the MAC address of the first RTP frame */ |
||
436 | if( pinfo->dl_src.type == AT_ETHER){ |
||
437 | copy_address(&(statinfo->first_packet_mac_addr), &(pinfo->dl_src)); |
||
438 | } |
||
439 | statinfo->start_seq_nr = rtpinfo->info_seq_num; |
||
440 | statinfo->stop_seq_nr = rtpinfo->info_seq_num; |
||
441 | statinfo->seq_num = rtpinfo->info_seq_num; |
||
442 | statinfo->start_time = current_time; |
||
443 | statinfo->timestamp = rtpinfo->info_timestamp; |
||
444 | statinfo->first_timestamp = rtpinfo->info_timestamp; |
||
445 | statinfo->time = current_time; |
||
446 | statinfo->lastnominaltime = 0; |
||
447 | statinfo->pt = rtpinfo->info_payload_type; |
||
448 | statinfo->reg_pt = rtpinfo->info_payload_type; |
||
449 | statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + 28; |
||
450 | statinfo->bw_history[statinfo->bw_index].time = current_time; |
||
451 | statinfo->bw_index++; |
||
452 | statinfo->total_bytes += rtpinfo->info_data_len + 28; |
||
453 | statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000; |
||
454 | /* Not needed ? initialised to zero? */ |
||
455 | statinfo->delta = 0; |
||
456 | statinfo->jitter = 0; |
||
457 | statinfo->diff = 0; |
||
458 | |||
459 | statinfo->total_nr++; |
||
460 | statinfo->flags |= STAT_FLAG_FIRST; |
||
461 | if (rtpinfo->info_marker_set) { |
||
462 | statinfo->flags |= STAT_FLAG_MARKER; |
||
463 | } |
||
464 | statinfo->first_packet = FALSE; |
||
465 | return; |
||
466 | } |
||
467 | |||
468 | /* Reset flags */ |
||
469 | statinfo->flags = 0; |
||
470 | |||
471 | /* Chek for duplicates (src mac differs from first_packet_mac_addr) */ |
||
472 | if( pinfo->dl_src.type == AT_ETHER){ |
||
473 | if(!addresses_equal(&(statinfo->first_packet_mac_addr), &(pinfo->dl_src))){ |
||
474 | statinfo->flags |= STAT_FLAG_DUP_PKT; |
||
475 | statinfo->delta = current_time-(statinfo->time); |
||
476 | return; |
||
477 | } |
||
478 | } |
||
479 | |||
480 | /* When calculating expected rtp packets the seq number can wrap around |
||
481 | * so we have to count the number of cycles |
||
482 | * Variable cycles counts the wraps around in forwarding connection and |
||
483 | * under is flag that indicates where we are |
||
484 | * |
||
485 | * XXX How to determine number of cycles with all possible lost, late |
||
486 | * and duplicated packets without any doubt? It seems to me, that |
||
487 | * because of all possible combination of late, duplicated or lost |
||
488 | * packets, this can only be more or less good approximation |
||
489 | * |
||
490 | * There are some combinations (rare but theoretically possible), |
||
491 | * where below code won't work correctly - statistic may be wrong then. |
||
492 | */ |
||
493 | |||
494 | /* So if the current sequence number is less than the start one |
||
495 | * we assume, that there is another cycle running |
||
496 | */ |
||
497 | if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){ |
||
498 | statinfo->cycles++; |
||
499 | statinfo->under = TRUE; |
||
500 | } |
||
501 | /* what if the start seq nr was 0? Then the above condition will never |
||
502 | * be true, so we add another condition. XXX The problem would arise |
||
503 | * if one of the packets with seq nr 0 or 65535 would be lost or late |
||
504 | */ |
||
505 | else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) && |
||
506 | (statinfo->under == FALSE)){ |
||
507 | statinfo->cycles++; |
||
508 | statinfo->under = TRUE; |
||
509 | } |
||
510 | /* the whole round is over, so reset the flag */ |
||
511 | else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) { |
||
512 | statinfo->under = FALSE; |
||
513 | } |
||
514 | |||
515 | /* Since it is difficult to count lost, duplicate or late packets separately, |
||
516 | * we would like to know at least how many times the sequence number was not ok |
||
517 | */ |
||
518 | |||
519 | /* If the current seq number equals the last one or if we are here for |
||
520 | * the first time, then it is ok, we just store the current one as the last one |
||
521 | */ |
||
522 | if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) ) |
||
523 | statinfo->seq_num = rtpinfo->info_seq_num; |
||
524 | /* If the first one is 65535 we wrap */ |
||
525 | else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) ) |
||
526 | statinfo->seq_num = rtpinfo->info_seq_num; |
||
527 | /* Lost packets. If the prev seq is enormously larger than the cur seq |
||
528 | * we assume that instead of being massively late we lost the packet(s) |
||
529 | * that would have indicated the sequence number wrapping. An imprecise |
||
530 | * heuristic at best, but it seems to work well enough. |
||
531 | * https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=5958 */ |
||
532 | else if (statinfo->seq_num+1 < rtpinfo->info_seq_num || statinfo->seq_num - rtpinfo->info_seq_num > 0xFF00) { |
||
533 | statinfo->seq_num = rtpinfo->info_seq_num; |
||
534 | statinfo->sequence++; |
||
535 | statinfo->flags |= STAT_FLAG_WRONG_SEQ; |
||
536 | } |
||
537 | /* Late or duplicated */ |
||
538 | else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) { |
||
539 | statinfo->sequence++; |
||
540 | statinfo->flags |= STAT_FLAG_WRONG_SEQ; |
||
541 | } |
||
542 | |||
543 | /* Check payload type */ |
||
544 | if (rtpinfo->info_payload_type == PT_CN |
||
545 | || rtpinfo->info_payload_type == PT_CN_OLD) |
||
546 | statinfo->flags |= STAT_FLAG_PT_CN; |
||
547 | if (statinfo->pt == PT_CN |
||
548 | || statinfo->pt == PT_CN_OLD) |
||
549 | statinfo->flags |= STAT_FLAG_FOLLOW_PT_CN; |
||
550 | if (rtpinfo->info_payload_type != statinfo->pt) |
||
551 | statinfo->flags |= STAT_FLAG_PT_CHANGE; |
||
552 | statinfo->pt = rtpinfo->info_payload_type; |
||
553 | |||
554 | /* |
||
555 | * Return for unknown payload types |
||
556 | * Ignore jitter calculation for clockrate = 0 |
||
557 | */ |
||
558 | if (statinfo->pt < 96 ){ |
||
559 | clock_rate = get_clock_rate(statinfo->pt); |
||
560 | }else{ /* Dynamic PT */ |
||
561 | if ( rtpinfo->info_payload_type_str != NULL ){ |
||
562 | /* Is it a "telephone-event" ? |
||
563 | * Timestamp is not increased for telepone-event packets impacting |
||
564 | * calculation of Jitter Skew and clock drift. |
||
565 | * see 2.2.1 of RFC 4733 |
||
566 | */ |
||
567 | if (g_ascii_strncasecmp("telephone-event",rtpinfo->info_payload_type_str,(strlen("telephone-event")))==0){ |
||
568 | clock_rate = 0; |
||
569 | statinfo->flags |= STAT_FLAG_PT_T_EVENT; |
||
570 | }else{ |
||
571 | if(rtpinfo->info_payload_rate !=0){ |
||
572 | clock_rate = rtpinfo->info_payload_rate; |
||
573 | }else{ |
||
574 | clock_rate = get_dyn_pt_clock_rate(rtpinfo->info_payload_type_str); |
||
575 | } |
||
576 | } |
||
577 | }else{ |
||
578 | clock_rate = 0; |
||
579 | } |
||
580 | } |
||
581 | |||
582 | /* Handle wraparound ? */ |
||
583 | arrivaltime = current_time - statinfo->start_time; |
||
584 | |||
585 | if (statinfo->first_timestamp > rtpinfo->info_timestamp){ |
||
586 | /* Handle wraparound */ |
||
587 | nominaltime = (double)(rtpinfo->info_timestamp + 0xffffffff - statinfo->first_timestamp + 1); |
||
588 | }else{ |
||
589 | nominaltime = (double)(rtpinfo->info_timestamp - statinfo->first_timestamp); |
||
590 | } |
||
591 | |||
592 | /* Can only analyze defined sampling rates */ |
||
593 | if (clock_rate != 0) { |
||
594 | statinfo->clock_rate = clock_rate; |
||
595 | /* Convert from sampling clock to ms */ |
||
596 | nominaltime = nominaltime /(clock_rate/1000); |
||
597 | |||
598 | /* Calculate the current jitter(in ms) */ |
||
599 | if (!statinfo->first_packet) { |
||
600 | expected_time = statinfo->time + (nominaltime - statinfo->lastnominaltime); |
||
601 | current_diff = fabs(current_time - expected_time); |
||
602 | current_jitter = (15 * statinfo->jitter + current_diff) / 16; |
||
603 | |||
604 | statinfo->delta = current_time-(statinfo->time); |
||
605 | statinfo->jitter = current_jitter; |
||
606 | statinfo->diff = current_diff; |
||
607 | } |
||
608 | statinfo->lastnominaltime = nominaltime; |
||
609 | /* Calculate skew, i.e. absolute jitter that also catches clock drift |
||
610 | * Skew is positive if TS (nominal) is too fast |
||
611 | */ |
||
612 | statinfo->skew = nominaltime - arrivaltime; |
||
613 | absskew = fabs(statinfo->skew); |
||
614 | if(absskew > fabs(statinfo->max_skew)){ |
||
615 | statinfo->max_skew = statinfo->skew; |
||
616 | } |
||
617 | /* Gather data for calculation of average, minimum and maximum framerate based on timestamp */ |
||
618 | #if 0 |
||
619 | if (numPackets > 0 && (!hardPayloadType || !alternatePayloadType)) { |
||
620 | /* Skip first packet and possibly alternate payload type packets */ |
||
621 | double dt; |
||
622 | dt = nominaltime - statinfo->lastnominaltime; |
||
623 | sumdt += 1.0 * dt; |
||
624 | numdt += (dt != 0 ? 1 : 0); |
||
625 | mindt = (dt < mindt ? dt : mindt); |
||
626 | maxdt = (dt > maxdt ? dt : maxdt); |
||
627 | } |
||
628 | #endif |
||
629 | /* Gather data for calculation of skew least square */ |
||
630 | statinfo->sumt += 1.0 * current_time; |
||
631 | statinfo->sumTS += 1.0 * nominaltime; |
||
632 | statinfo->sumt2 += 1.0 * current_time * current_time; |
||
633 | statinfo->sumtTS += 1.0 * current_time * nominaltime; |
||
634 | } |
||
635 | |||
636 | /* Calculate the BW in Kbps adding the IP+UDP header to the RTP -> 20bytes(IP) + 8bytes(UDP) */ |
||
637 | statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + 28; |
||
638 | statinfo->bw_history[statinfo->bw_index].time = current_time; |
||
639 | |||
640 | /* Check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */ |
||
641 | while ((statinfo->bw_history[statinfo->bw_start_index].time+1000/* ms */)<current_time){ |
||
642 | statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes; |
||
643 | statinfo->bw_start_index++; |
||
644 | if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0; |
||
645 | }; |
||
646 | /* IP hdr + UDP + RTP */ |
||
647 | statinfo->total_bytes += rtpinfo->info_data_len + 28; |
||
648 | statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000; |
||
649 | statinfo->bw_index++; |
||
650 | if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0; |
||
651 | |||
652 | |||
653 | /* Is it a packet with the mark bit set? */ |
||
654 | if (rtpinfo->info_marker_set) { |
||
655 | statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp; |
||
656 | if (rtpinfo->info_timestamp > statinfo->timestamp){ |
||
657 | statinfo->flags |= STAT_FLAG_MARKER; |
||
658 | } |
||
659 | else{ |
||
660 | statinfo->flags |= STAT_FLAG_WRONG_TIMESTAMP; |
||
661 | } |
||
662 | } |
||
663 | /* Is it a regular packet? */ |
||
664 | if (!(statinfo->flags & STAT_FLAG_FIRST) |
||
665 | && !(statinfo->flags & STAT_FLAG_MARKER) |
||
666 | && !(statinfo->flags & STAT_FLAG_PT_CN) |
||
667 | && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) |
||
668 | && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) { |
||
669 | /* Include it in maximum delta calculation */ |
||
670 | if (statinfo->delta > statinfo->max_delta) { |
||
671 | statinfo->max_delta = statinfo->delta; |
||
672 | statinfo->max_nr = pinfo->num; |
||
673 | } |
||
674 | if (clock_rate != 0) { |
||
675 | /* Maximum and mean jitter calculation */ |
||
676 | if (statinfo->jitter > statinfo->max_jitter) { |
||
677 | statinfo->max_jitter = statinfo->jitter; |
||
678 | } |
||
679 | statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1); |
||
680 | } |
||
681 | } |
||
682 | /* Regular payload change? (CN ignored) */ |
||
683 | if (!(statinfo->flags & STAT_FLAG_FIRST) |
||
684 | && !(statinfo->flags & STAT_FLAG_PT_CN)) { |
||
685 | if ((statinfo->pt != statinfo->reg_pt) |
||
686 | && (statinfo->reg_pt != PT_UNDEFINED)) { |
||
687 | statinfo->flags |= STAT_FLAG_REG_PT_CHANGE; |
||
688 | } |
||
689 | } |
||
690 | |||
691 | /* Set regular payload*/ |
||
692 | if (!(statinfo->flags & STAT_FLAG_PT_CN)) { |
||
693 | statinfo->reg_pt = statinfo->pt; |
||
694 | } |
||
695 | |||
696 | statinfo->time = current_time; |
||
697 | statinfo->timestamp = rtpinfo->info_timestamp; |
||
698 | statinfo->stop_seq_nr = rtpinfo->info_seq_num; |
||
699 | statinfo->total_nr++; |
||
700 | |||
701 | return; |
||
702 | } |
||
703 | |||
704 | /* |
||
705 | * Editor modelines - http://www.wireshark.org/tools/modelines.html |
||
706 | * |
||
707 | * Local variables: |
||
708 | * c-basic-offset: 8 |
||
709 | * tab-width: 8 |
||
710 | * indent-tabs-mode: t |
||
711 | * End: |
||
712 | * |
||
713 | * vi: set shiftwidth=8 tabstop=8 noexpandtab: |
||
714 | * :indentSize=8:tabSize=8:noTabs=false: |
||
715 | */ |