2 * RTP analysis addition for Wireshark
6 * Copyright 2003, Alcatel Business Systems
7 * By Lars Ruoff <lars.ruoff@gmx.net>
10 * Copyright 2003, Iskratel, Ltd, Kranj
11 * By Miha Jemec <m.jemec@iskratel.si>
13 * Graph. Copyright 2004, Verso Technology
14 * By Alejandro Vaquero <alejandro.vaquero@verso.com>
15 * Based on io_stat.c by Ronnie Sahlberg
17 * Wireshark - Network traffic analyzer
18 * By Gerald Combs <gerald@wireshark.org>
19 * Copyright 1998 Gerald Combs
21 * This program is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU General Public License
23 * as published by the Free Software Foundation; either version 2
24 * of the License, or (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
40 /*do not define this symbol. will be added soon*/
41 /*#define USE_CONVERSATION_GRAPH 1*/
43 #ifdef USE_CONVERSATION_GRAPH
44 #include "../graph/graph.h"
47 #include <epan/epan_dissect.h>
48 #include <epan/filesystem.h>
53 #include <epan/dissectors/packet-rtp.h>
55 #include <epan/rtp_pt.h>
56 #include <epan/addr_resolv.h>
60 #include "gtkglobals.h"
62 #include <epan/stat_cmd_args.h>
63 #include "dlg_utils.h"
65 #include "gui_utils.h"
66 #include "alert_box.h"
67 #include "simple_dialog.h"
68 #include "../stat_menu.h"
69 #include "gui_stat_menu.h"
71 #include "progress_dlg.h"
72 #include "compat_macros.h"
75 #include "image/clist_ascend.xpm"
76 #include "image/clist_descend.xpm"
90 #include "file_util.h"
93 #include "rtp_analysis.h"
94 #include "rtp_stream.h"
95 #include "rtp_stream_dlg.h"
97 #ifdef NEED_G_ASCII_STRCASECMP_H
98 #include "../epan/g_ascii_strcasecmp.h"
102 /****************************************************************************/
104 typedef struct column_arrows {
106 GtkWidget *ascend_pm;
107 GtkWidget *descend_pm;
111 #define NUM_GRAPH_ITEMS 100000
112 #define MAX_YSCALE 16
113 #define AUTO_MAX_YSCALE 0
115 #define GRAPH_FWD_JITTER 0
116 #define GRAPH_FWD_DIFF 1
117 #define GRAPH_REV_JITTER 2
118 #define GRAPH_REV_DIFF 3
119 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
121 #define MAX_PIXELS_PER_TICK 4
122 #define DEFAULT_PIXELS_PER_TICK 1
123 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
124 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
126 #define MAX_TICK_VALUES 5
127 #define DEFAULT_TICK_VALUE 1
128 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
129 typedef struct _dialog_graph_graph_item_t {
132 } dialog_graph_graph_item_t;
134 typedef struct _dialog_graph_graph_t {
135 struct _user_data_t *ud;
136 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
139 GtkWidget *display_button;
144 } dialog_graph_graph_t;
147 typedef struct _dialog_graph_t {
148 gboolean needs_redraw;
149 gint32 interval; /* measurement interval in ms */
150 guint32 last_interval;
151 guint32 max_interval; /* XXX max_interval and num_items are redundant */
153 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
155 GtkWidget *draw_area;
157 GtkAdjustment *scrollbar_adjustment;
158 GtkWidget *scrollbar;
166 typedef struct _dialog_data_t {
170 GtkWidget *label_stats_fwd;
171 GtkWidget *label_stats_rev;
172 column_arrows *col_arrows_fwd;
173 column_arrows *col_arrows_rev;
175 GtkCList *selected_clist;
176 GtkWidget *save_voice_as_w;
177 GtkWidget *save_csv_as_w;
178 gint notebook_signal_id;
180 dialog_graph_t dialog_graph;
181 #ifdef USE_CONVERSATION_GRAPH
182 GtkWidget *graph_window;
186 #define OK_TEXT "[ Ok ]"
188 typedef struct _key_value {
194 /* RTP sampling clock rates for fixed payload types as defined in
195 http://www.iana.org/assignments/rtp-parameters */
196 static const key_value clock_map[] = {
202 {PT_DVI4_8000, 8000},
203 {PT_DVI4_16000, 16000},
207 {PT_L16_STEREO, 44100},
208 {PT_L16_MONO, 44100},
214 {PT_DVI4_11025, 11025},
215 {PT_DVI4_22050, 22050},
227 #define NUM_CLOCK_VALUES (sizeof clock_map / sizeof clock_map[0])
230 get_clock_rate(guint32 key)
234 for (i = 0; i < NUM_CLOCK_VALUES; i++) {
235 if (clock_map[i].key == key)
236 return clock_map[i].value;
241 typedef struct _mimetype_and_clock {
242 const gchar *pt_mime_name_str;
244 } mimetype_and_clock;
245 /* RTP sampling clock rates for
246 "In addition to the RTP payload formats (encodings) listed in the RTP
247 Payload Types table, there are additional payload formats that do not
248 have static RTP payload types assigned but instead use dynamic payload
249 type number assignment. Each payload format is named by a registered
251 http://www.iana.org/assignments/rtp-parameters.
253 static const mimetype_and_clock mimetype_and_clock_map[] = {
254 {"AMR", 8000}, /* [RFC3267] */
255 {"AMR-WB", 16000}, /* [RFC3267] */
256 {"EVRC", 8000}, /* [RFC3558] */
257 {"EVRC0", 8000}, /* [RFC3558] */
258 {"G7221", 16000}, /* [RFC3047] */
259 {"G726-16", 8000}, /* [RFC3551] */
260 {"G726-24", 8000}, /* [RFC3551] */
261 {"G726-32", 8000}, /* [RFC3551] */
262 {"G726-40", 8000}, /* [RFC3551] */
263 {"G729D", 8000}, /* [RFC3551] */
264 {"G729E", 8000}, /* [RFC3551] */
265 {"GSM-EFR", 8000}, /* [RFC3551] */
266 {"mpa-robust", 90000}, /* [RFC3119] */
267 {"SMV", 8000}, /* [RFC3558] */
268 {"SMV0", 8000}, /* [RFC3558] */
269 {"red", 1000}, /* [RFC4102] */
270 {"t140", 1000}, /* [RFC4103] */
271 {"BMPEG", 90000}, /* [RFC2343],[RFC3555] */
272 {"BT656", 90000}, /* [RFC2431],[RFC3555] */
273 {"DV", 90000}, /* [RFC3189] */
274 {"H263-1998", 90000}, /* [RFC2429],[RFC3555] */
275 {"H263-2000", 90000}, /* [RFC2429],[RFC3555] */
276 {"MP1S", 90000}, /* [RFC2250],[RFC3555] */
277 {"MP2P", 90000}, /* [RFC2250],[RFC3555] */
278 {"MP4V-ES", 90000}, /* [RFC3016] */
279 {"pointer", 90000}, /* [RFC2862] */
280 {"raw", 90000}, /* [RFC4175] */
281 {"telephone-event", 8000}, /* [RFC4733] */
284 #define NUM_DYN_CLOCK_VALUES (sizeof mimetype_and_clock_map / sizeof mimetype_and_clock_map[0])
287 get_dyn_pt_clock_rate(gchar *payload_type_str)
291 for (i = 0; i < NUM_DYN_CLOCK_VALUES; i++) {
292 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)
293 return mimetype_and_clock_map[i].value;
299 /* type of error when saving voice in a file didn't succeed */
302 TAP_RTP_WRONG_LENGTH,
303 TAP_RTP_PADDING_ERROR,
305 TAP_RTP_FILE_OPEN_ERROR,
309 #if GTK_MAJOR_VERSION < 2
310 GtkRcStyle *rc_style;
311 GdkColormap *colormap;
314 typedef struct _tap_rtp_save_info_t {
317 error_type_t error_type;
319 } tap_rtp_save_info_t;
322 /* structure that holds the information about the forward and reversed direction */
323 struct _info_direction {
324 tap_rtp_stat_t statinfo;
325 tap_rtp_save_info_t saveinfo;
328 #define TMPNAMSIZE 100
330 #define SILENCE_PCMU (guint8)0xFF
331 #define SILENCE_PCMA (guint8)0x55
333 /* structure that holds general information about the connection
334 * and structures for both directions */
335 typedef struct _user_data_t {
336 /* tap associated data*/
338 guint16 port_src_fwd;
340 guint16 port_dst_fwd;
343 guint16 port_src_rev;
345 guint16 port_dst_rev;
348 struct _info_direction forward;
349 struct _info_direction reversed;
351 char f_tempname[TMPNAMSIZE];
352 char r_tempname[TMPNAMSIZE];
354 /* dialog associated data */
357 #ifdef USE_CONVERSATION_GRAPH
358 time_series_t series_fwd;
359 time_series_t series_rev;
365 static const gchar *titles[9] = {
377 #define SAVE_FORWARD_DIRECTION_MASK 0x01
378 #define SAVE_REVERSE_DIRECTION_MASK 0x02
379 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
381 #define SAVE_NONE_FORMAT 0
382 #define SAVE_WAV_FORMAT 1
383 #define SAVE_AU_FORMAT 2
384 #define SAVE_SW_FORMAT 3
385 #define SAVE_RAW_FORMAT 4
388 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_);
389 /****************************************************************************/
390 static void enable_graph(dialog_graph_graph_t *dgg)
397 static void dialog_graph_reset(user_data_t* user_data);
401 /****************************************************************************/
404 /****************************************************************************/
405 /* when there is a [re]reading of packet's */
407 rtp_reset(void *user_data_arg)
409 user_data_t *user_data = user_data_arg;
410 user_data->forward.statinfo.first_packet = TRUE;
411 user_data->reversed.statinfo.first_packet = TRUE;
412 user_data->forward.statinfo.max_delta = 0;
413 user_data->reversed.statinfo.max_delta = 0;
414 user_data->forward.statinfo.max_jitter = 0;
415 user_data->reversed.statinfo.max_jitter = 0;
416 user_data->forward.statinfo.mean_jitter = 0;
417 user_data->reversed.statinfo.mean_jitter = 0;
418 user_data->forward.statinfo.delta = 0;
419 user_data->reversed.statinfo.delta = 0;
420 user_data->forward.statinfo.diff = 0;
421 user_data->reversed.statinfo.diff = 0;
422 user_data->forward.statinfo.jitter = 0;
423 user_data->reversed.statinfo.jitter = 0;
424 user_data->forward.statinfo.bandwidth = 0;
425 user_data->reversed.statinfo.bandwidth = 0;
426 user_data->forward.statinfo.total_bytes = 0;
427 user_data->reversed.statinfo.total_bytes = 0;
428 user_data->forward.statinfo.bw_start_index = 0;
429 user_data->reversed.statinfo.bw_start_index = 0;
430 user_data->forward.statinfo.bw_index = 0;
431 user_data->reversed.statinfo.bw_index = 0;
432 user_data->forward.statinfo.timestamp = 0;
433 user_data->reversed.statinfo.timestamp = 0;
434 user_data->forward.statinfo.max_nr = 0;
435 user_data->reversed.statinfo.max_nr = 0;
436 user_data->forward.statinfo.total_nr = 0;
437 user_data->reversed.statinfo.total_nr = 0;
438 user_data->forward.statinfo.sequence = 0;
439 user_data->reversed.statinfo.sequence = 0;
440 user_data->forward.statinfo.start_seq_nr = 0;
441 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
442 user_data->forward.statinfo.stop_seq_nr = 0;
443 user_data->reversed.statinfo.stop_seq_nr = 0;
444 user_data->forward.statinfo.cycles = 0;
445 user_data->reversed.statinfo.cycles = 0;
446 user_data->forward.statinfo.under = FALSE;
447 user_data->reversed.statinfo.under = FALSE;
448 user_data->forward.statinfo.start_time = 0;
449 user_data->reversed.statinfo.start_time = 0;
450 user_data->forward.statinfo.time = 0;
451 user_data->reversed.statinfo.time = 0;
452 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
453 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
455 user_data->forward.saveinfo.count = 0;
456 user_data->reversed.saveinfo.count = 0;
457 user_data->forward.saveinfo.saved = FALSE;
458 user_data->reversed.saveinfo.saved = FALSE;
460 /* clear the dialog box clists */
461 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
462 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
464 /* reset graph info */
465 dialog_graph_reset(user_data);
467 #ifdef USE_CONVERSATION_GRAPH
468 if (user_data->dlg.graph_window != NULL)
469 window_destroy(user_data->dlg.graph_window);
471 g_array_free(user_data->series_fwd.value_pairs, TRUE);
472 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
474 g_array_free(user_data->series_rev.value_pairs, TRUE);
475 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
478 /* XXX check for error at fclose? */
479 if (user_data->forward.saveinfo.fp != NULL)
480 fclose(user_data->forward.saveinfo.fp);
481 if (user_data->reversed.saveinfo.fp != NULL)
482 fclose(user_data->reversed.saveinfo.fp);
483 user_data->forward.saveinfo.fp = eth_fopen(user_data->f_tempname, "wb");
484 if (user_data->forward.saveinfo.fp == NULL)
485 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
486 user_data->reversed.saveinfo.fp = eth_fopen(user_data->r_tempname, "wb");
487 if (user_data->reversed.saveinfo.fp == NULL)
488 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
492 /****************************************************************************/
493 static int rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
495 dialog_graph_graph_item_t *it;
499 /* we sometimes get called when dgg is disabled.
500 this is a bug since the tap listener should be removed first */
505 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
508 * Find which interval this is supposed to to in and store the
509 * interval index as idx
511 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
512 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
514 rtp_time = nstime_to_sec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
518 idx = (guint32)(rtp_time*1000)/dgg->ud->dlg.dialog_graph.interval;
520 /* some sanity checks */
521 if((idx<0)||(idx>=NUM_GRAPH_ITEMS)){
525 /* update num_items */
526 if((guint32)idx > dgg->ud->dlg.dialog_graph.num_items){
527 dgg->ud->dlg.dialog_graph.num_items=idx;
528 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
532 * Find the appropriate dialog_graph_graph_item_t structure
537 * Use the max value to highlight RTP problems
539 if (value > it->value) {
542 it->flags = it->flags | statinfo->flags;
547 /****************************************************************************/
548 /* here we can redraw the output */
550 static void rtp_draw(void *prs _U_)
555 /* forward declarations */
556 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
557 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
558 gchar *timeStr, guint32 pkt_len, GdkColor *color);
560 static int rtp_packet_add_info(GtkCList *clist,
561 tap_rtp_stat_t *statinfo, packet_info *pinfo,
562 const struct _rtp_info *rtpinfo);
564 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
565 tap_rtp_stat_t *statinfo,
567 const struct _rtp_info *rtpinfo);
570 /****************************************************************************/
571 /* whenever a RTP packet is seen by the tap listener */
572 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
574 user_data_t *user_data = user_data_arg;
575 const struct _rtp_info *rtpinfo = rtpinfo_arg;
576 #ifdef USE_CONVERSATION_GRAPH
579 /* we ignore packets that are not displayed */
580 if (pinfo->fd->flags.passed_dfilter == 0)
582 /* also ignore RTP Version != 2 */
583 else if (rtpinfo->info_version !=2)
585 /* is it the forward direction? */
586 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
587 && CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
588 && user_data->port_src_fwd == pinfo->srcport
589 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
590 && user_data->port_dst_fwd == pinfo->destport) {
591 #ifdef USE_CONVERSATION_GRAPH
592 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
593 vp.fnumber = pinfo->fd->num;
594 g_array_append_val(user_data->series_fwd.value_pairs, vp);
596 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
597 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]), &(user_data->forward.statinfo), pinfo, (guint32)(user_data->forward.statinfo.jitter*1000000));
598 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]), &(user_data->forward.statinfo), pinfo, (guint32)(user_data->forward.statinfo.diff*1000000));
599 rtp_packet_add_info(user_data->dlg.clist_fwd,
600 &(user_data->forward.statinfo), pinfo, rtpinfo);
601 rtp_packet_save_payload(&(user_data->forward.saveinfo),
602 &(user_data->forward.statinfo), pinfo, rtpinfo);
604 /* is it the reversed direction? */
605 else if (user_data->ssrc_rev == rtpinfo->info_sync_src
606 && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
607 && user_data->port_src_rev == pinfo->srcport
608 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
609 && user_data->port_dst_rev == pinfo->destport) {
610 #ifdef USE_CONVERSATION_GRAPH
611 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
612 vp.fnumber = pinfo->fd->num;
613 g_array_append_val(user_data->series_rev.value_pairs, vp);
615 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
616 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]), &(user_data->reversed.statinfo), pinfo, (guint32)(user_data->reversed.statinfo.jitter*1000000));
617 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]), &(user_data->reversed.statinfo), pinfo, (guint32)(user_data->reversed.statinfo.diff*1000000));
618 rtp_packet_add_info(user_data->dlg.clist_rev,
619 &(user_data->reversed.statinfo), pinfo, rtpinfo);
620 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
621 &(user_data->reversed.statinfo), pinfo, rtpinfo);
628 /****************************************************************************/
629 int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
631 const struct _rtp_info *rtpinfo)
634 double current_jitter;
639 /* check payload type */
640 if (rtpinfo->info_payload_type == PT_CN
641 || rtpinfo->info_payload_type == PT_CN_OLD)
642 statinfo->flags |= STAT_FLAG_PT_CN;
643 if (statinfo->pt == PT_CN
644 || statinfo->pt == PT_CN_OLD)
645 statinfo->flags |= STAT_FLAG_FOLLOW_PT_CN;
646 if (rtpinfo->info_payload_type != statinfo->pt)
647 statinfo->flags |= STAT_FLAG_PT_CHANGE;
648 statinfo->pt = rtpinfo->info_payload_type;
650 * XXX - should "get_clock_rate()" return 0 for unknown
651 * payload types, presumably meaning that we should
652 * just ignore this packet?
654 if (statinfo->pt < 96 ){
655 clock_rate = get_clock_rate(statinfo->pt);
656 }else{ /* dynamic PT */
657 if ( rtpinfo->info_payload_type_str != NULL )
658 clock_rate = get_dyn_pt_clock_rate(rtpinfo-> info_payload_type_str);
663 /* store the current time and calculate the current jitter */
664 current_time = nstime_to_sec(&pinfo->fd->rel_ts);
665 current_diff = fabs (current_time - (statinfo->time) - ((double)(rtpinfo->info_timestamp)-(double)(statinfo->timestamp))/clock_rate);
666 current_jitter = statinfo->jitter + ( current_diff - statinfo->jitter)/16;
667 statinfo->delta = current_time-(statinfo->time);
668 statinfo->jitter = current_jitter;
669 statinfo->diff = current_diff;
671 /* calculate the BW in Kbps adding the IP+UDP header to the RTP -> 20bytes(IP)+8bytes(UDP) = 28bytes */
672 statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + 28;
673 statinfo->bw_history[statinfo->bw_index].time = current_time;
674 /* check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
675 while ((statinfo->bw_history[statinfo->bw_start_index].time+1)<current_time){
676 statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;
677 statinfo->bw_start_index++;
678 if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
680 statinfo->total_bytes += rtpinfo->info_data_len + 28;
681 statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
682 statinfo->bw_index++;
683 if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
686 /* is this the first packet we got in this direction? */
687 if (statinfo->first_packet) {
688 statinfo->start_seq_nr = rtpinfo->info_seq_num;
689 statinfo->start_time = current_time;
691 statinfo->jitter = 0;
693 statinfo->flags |= STAT_FLAG_FIRST;
694 statinfo->first_packet = FALSE;
696 /* is it a packet with the mark bit set? */
697 if (rtpinfo->info_marker_set) {
698 statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
699 if (rtpinfo->info_timestamp > statinfo->timestamp){
700 statinfo->flags |= STAT_FLAG_MARKER;
703 statinfo->flags |= STAT_FLAG_WRONG_TIMESTAMP;
706 /* is it a regular packet? */
707 if (!(statinfo->flags & STAT_FLAG_FIRST)
708 && !(statinfo->flags & STAT_FLAG_MARKER)
709 && !(statinfo->flags & STAT_FLAG_PT_CN)
710 && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
711 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
712 /* include it in maximum delta calculation */
713 if (statinfo->delta > statinfo->max_delta) {
714 statinfo->max_delta = statinfo->delta;
715 statinfo->max_nr = pinfo->fd->num;
717 /* maximum and mean jitter calculation */
718 if (statinfo->jitter > statinfo->max_jitter) {
719 statinfo->max_jitter = statinfo->jitter;
721 statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1);
723 /* regular payload change? (CN ignored) */
724 if (!(statinfo->flags & STAT_FLAG_FIRST)
725 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
726 if ((statinfo->pt != statinfo->reg_pt)
727 && (statinfo->reg_pt != PT_UNDEFINED)) {
728 statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
732 /* set regular payload*/
733 if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
734 statinfo->reg_pt = statinfo->pt;
738 /* When calculating expected rtp packets the seq number can wrap around
739 * so we have to count the number of cycles
740 * Variable cycles counts the wraps around in forwarding connection and
741 * under is flag that indicates where we are
743 * XXX how to determine number of cycles with all possible lost, late
744 * and duplicated packets without any doubt? It seems to me, that
745 * because of all possible combination of late, duplicated or lost
746 * packets, this can only be more or less good approximation
748 * There are some combinations (rare but theoretically possible),
749 * where below code won't work correctly - statistic may be wrong then.
752 /* so if the current sequence number is less than the start one
753 * we assume, that there is another cycle running */
754 if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
756 statinfo->under = TRUE;
758 /* what if the start seq nr was 0? Then the above condition will never
759 * be true, so we add another condition. XXX The problem would arise
760 * if one of the packets with seq nr 0 or 65535 would be lost or late */
761 else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
762 (statinfo->under == FALSE)){
764 statinfo->under = TRUE;
766 /* the whole round is over, so reset the flag */
767 else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
768 statinfo->under = FALSE;
771 /* Since it is difficult to count lost, duplicate or late packets separately,
772 * we would like to know at least how many times the sequence number was not ok */
774 /* if the current seq number equals the last one or if we are here for
775 * the first time, then it is ok, we just store the current one as the last one */
776 if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
777 statinfo->seq_num = rtpinfo->info_seq_num;
778 /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
779 else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
780 statinfo->seq_num = rtpinfo->info_seq_num;
782 else if (statinfo->seq_num+1 < rtpinfo->info_seq_num) {
783 statinfo->seq_num = rtpinfo->info_seq_num;
784 statinfo->sequence++;
785 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
787 /* late or duplicated */
788 else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
789 statinfo->sequence++;
790 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
792 statinfo->time = current_time;
793 statinfo->timestamp = rtpinfo->info_timestamp;
794 statinfo->stop_seq_nr = rtpinfo->info_seq_num;
795 statinfo->total_nr++;
801 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
802 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
803 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
804 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
806 /****************************************************************************/
807 /* adds statistics information from the packet to the clist */
808 static int rtp_packet_add_info(GtkCList *clist,
809 tap_rtp_stat_t *statinfo, packet_info *pinfo,
810 const struct _rtp_info *rtpinfo)
817 GdkColor color = COLOR_DEFAULT;
818 then = pinfo->fd->abs_ts.secs;
819 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
820 tm_tmp = localtime(&then);
821 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
824 tm_tmp->tm_year + 1900,
830 if (statinfo->pt == PT_CN) {
831 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
834 else if (statinfo->pt == PT_CN_OLD) {
835 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
838 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
839 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
842 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
843 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
844 color = COLOR_WARNING;
846 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
847 g_snprintf(status,sizeof(status),"Incorrect timestamp");
848 color = COLOR_WARNING;
850 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
851 && !(statinfo->flags & STAT_FLAG_FIRST)
852 && !(statinfo->flags & STAT_FLAG_PT_CN)
853 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
854 && !(statinfo->flags & STAT_FLAG_MARKER)) {
855 g_snprintf(status,sizeof(status),"Marker missing?");
856 color = COLOR_WARNING;
859 if (statinfo->flags & STAT_FLAG_MARKER) {
860 color = COLOR_WARNING;
862 g_snprintf(status,sizeof(status),OK_TEXT);
864 /* is this the first packet we got in this direction? */
865 if (statinfo->flags & STAT_FLAG_FIRST) {
867 pinfo->fd->num, rtpinfo->info_seq_num,
872 rtpinfo->info_marker_set,
873 timeStr, pinfo->fd->pkt_len,
878 pinfo->fd->num, rtpinfo->info_seq_num,
879 statinfo->delta*1000,
880 statinfo->jitter*1000,
883 rtpinfo->info_marker_set,
884 timeStr, pinfo->fd->pkt_len,
890 #define MAX_SILENCE_TICKS 1000000
891 /****************************************************************************/
892 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
893 tap_rtp_stat_t *statinfo,
895 const struct _rtp_info *rtpinfo)
902 /* is this the first packet we got in this direction? */
903 if (statinfo->flags & STAT_FLAG_FIRST) {
904 if (saveinfo->fp == NULL) {
905 saveinfo->saved = FALSE;
906 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
909 saveinfo->saved = TRUE;
912 /* save the voice information */
913 /* if there was already an error, we quit */
914 if (saveinfo->saved == FALSE)
917 /* if the captured length and packet length aren't equal, we quit
918 * if also the RTP dissector thinks there is some information missing */
919 if ((pinfo->fd->pkt_len != pinfo->fd->cap_len) &&
920 (!rtpinfo->info_all_data_present)) {
921 saveinfo->saved = FALSE;
922 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
926 /* if padding bit is set, but the padding count is bigger
927 * then the whole RTP data - error with padding count */
928 if ( (rtpinfo->info_padding_set != FALSE) &&
929 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
930 saveinfo->saved = FALSE;
931 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
935 /* do we need to insert some silence? */
936 if ((rtpinfo->info_marker_set) &&
937 !(statinfo->flags & STAT_FLAG_FIRST) &&
938 !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) &&
939 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
940 /* the amount of silence should be the difference between
941 * the last timestamp and the current one minus x
942 * x should equal the amount of information in the last frame
943 * XXX not done yet */
944 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
945 rtpinfo->info_padding_count) && i < MAX_SILENCE_TICKS; i++) {
946 switch (statinfo->reg_pt) {
957 nchars=fwrite(&tmp, 1, 1, saveinfo->fp);
960 fflush(saveinfo->fp);
964 if (rtpinfo->info_payload_type == PT_CN
965 || rtpinfo->info_payload_type == PT_CN_OLD) {
967 /*all other payloads*/
969 if (!rtpinfo->info_all_data_present) {
970 /* Not all the data was captured. */
971 saveinfo->saved = FALSE;
972 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
976 /* we put the pointer at the beginning of the RTP
977 * payload, that is, at the beginning of the RTP data
978 * plus the offset of the payload from the beginning
980 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
981 nchars=fwrite(data, sizeof(unsigned char), (rtpinfo->info_payload_len - rtpinfo->info_padding_count), saveinfo->fp);
982 saveinfo->count+=(rtpinfo->info_payload_len - rtpinfo->info_padding_count);
984 fflush(saveinfo->fp);
985 saveinfo->saved = TRUE;
993 /****************************************************************************/
996 /****************************************************************************/
997 /* XXX just copied from gtk/rpc_stat.c */
998 void protect_thread_critical_region(void);
999 void unprotect_thread_critical_region(void);
1002 /****************************************************************************/
1003 /* close the dialog window and remove the tap listener */
1004 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
1006 /* remove tap listener */
1007 protect_thread_critical_region();
1008 remove_tap_listener(user_data);
1009 unprotect_thread_critical_region();
1011 /* close and remove temporary files */
1012 if (user_data->forward.saveinfo.fp != NULL)
1013 fclose(user_data->forward.saveinfo.fp);
1014 if (user_data->reversed.saveinfo.fp != NULL)
1015 fclose(user_data->reversed.saveinfo.fp);
1016 /*XXX: test for error **/
1017 eth_remove(user_data->f_tempname);
1018 eth_remove(user_data->r_tempname);
1020 /* destroy save_voice_as window if open */
1021 if (user_data->dlg.save_voice_as_w != NULL)
1022 window_destroy(user_data->dlg.save_voice_as_w);
1024 /* destroy graph window if open */
1025 if (user_data->dlg.dialog_graph.window != NULL)
1026 window_destroy(user_data->dlg.dialog_graph.window);
1028 #ifdef USE_CONVERSATION_GRAPH
1029 /* destroy graph window if open */
1030 if (user_data->dlg.graph_window != NULL)
1031 window_destroy(user_data->dlg.graph_window);
1034 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
1035 gtk_signal_disconnect(GTK_OBJECT(user_data->dlg.notebook), user_data->dlg.notebook_signal_id);
1037 g_free(user_data->dlg.col_arrows_fwd);
1038 g_free(user_data->dlg.col_arrows_rev);
1043 /****************************************************************************/
1044 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
1045 GtkNotebookPage *page _U_,
1047 user_data_t *user_data _U_)
1049 user_data->dlg.selected_clist =
1050 (page_num==0) ? user_data->dlg.clist_fwd : user_data->dlg.clist_rev ;
1051 user_data->dlg.selected_row = 0;
1054 /****************************************************************************/
1055 static void on_clist_select_row(GtkCList *clist _U_,
1058 GdkEvent *event _U_,
1059 user_data_t *user_data _U_)
1061 user_data->dlg.selected_clist = clist;
1062 user_data->dlg.selected_row = row;
1066 #ifdef USE_CONVERSATION_GRAPH
1067 /****************************************************************************/
1068 /* when the graph window gets destroyed */
1069 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
1071 /* note that graph window has been destroyed */
1072 user_data->dlg.graph_window = NULL;
1075 /****************************************************************************/
1076 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
1079 GtkCList *clist = NULL;
1080 if (vp.fnumber != 0) {
1081 clist = GTK_CLIST(user_data->dlg.clist_fwd);
1082 row = gtk_clist_find_row_from_data(clist,
1083 GUINT_TO_POINTER(vp.fnumber));
1085 clist = GTK_CLIST(user_data->dlg.clist_rev);
1086 row = gtk_clist_find_row_from_data(clist,
1087 GUINT_TO_POINTER(vp.fnumber));
1090 gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook),
1091 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
1092 gtk_clist_select_row(clist, row, 0);
1093 gtk_clist_moveto(clist, row, 0, 0.5, 0);
1099 /****************************************************************************/
1100 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1106 if (user_data->dlg.graph_window != NULL) {
1107 /* There's already a graph window; reactivate it. */
1108 reactivate_window(user_data->dlg.graph_window);
1111 list = g_list_append(list, &(user_data->series_fwd));
1112 list = g_list_append(list, &(user_data->series_rev));
1114 user_data->series_fwd.color.pixel = 0;
1115 user_data->series_fwd.color.red = 0x80ff;
1116 user_data->series_fwd.color.green = 0xe0ff;
1117 user_data->series_fwd.color.blue = 0xffff;
1118 user_data->series_fwd.yvalue = 0.5;
1120 user_data->series_rev.color.pixel = 0;
1121 user_data->series_rev.color.red = 0x60ff;
1122 user_data->series_rev.color.green = 0xc0ff;
1123 user_data->series_rev.color.blue = 0xffff;
1124 user_data->series_rev.yvalue = -0.5;
1126 g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=0x%X)",
1127 get_addr_name(&(user_data->ip_src_fwd)),
1128 user_data->port_src_fwd,
1129 get_addr_name(&(user_data->ip_dst_fwd)),
1130 user_data->port_dst_fwd,
1131 user_data->ssrc_fwd);
1133 g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=0x%X)",
1134 get_addr_name(&(user_data->ip_src_rev)),
1135 user_data->port_src_rev,
1136 get_addr_name(&(user_data->ip_dst_rev)),
1137 user_data->port_dst_rev,
1138 user_data->ssrc_rev);
1140 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
1141 &graph_selection_callback, user_data);
1142 SIGNAL_CONNECT(user_data->dlg.graph_window, "destroy",
1143 on_destroy_graph, user_data);
1145 #endif /*USE_CONVERSATION_GRAPH*/
1147 /****************************************************************************/
1148 static void dialog_graph_set_title(user_data_t* user_data)
1151 if (!user_data->dlg.dialog_graph.window){
1154 title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
1155 get_addr_name(&(user_data->ip_src_fwd)),
1156 user_data->port_src_fwd,
1157 get_addr_name(&(user_data->ip_dst_fwd)),
1158 user_data->port_dst_fwd,
1159 get_addr_name(&(user_data->ip_src_rev)),
1160 user_data->port_src_rev,
1161 get_addr_name(&(user_data->ip_dst_rev)),
1162 user_data->port_dst_rev);
1164 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
1170 /****************************************************************************/
1171 static void dialog_graph_reset(user_data_t* user_data)
1175 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1176 for(i=0;i<MAX_GRAPHS;i++){
1177 for(j=0;j<NUM_GRAPH_ITEMS;j++){
1178 dialog_graph_graph_item_t *dggi;
1179 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
1184 user_data->dlg.dialog_graph.last_interval=0xffffffff;
1185 user_data->dlg.dialog_graph.max_interval=0;
1186 user_data->dlg.dialog_graph.num_items=0;
1188 /* create the color titles near the filter buttons */
1189 for(i=0;i<MAX_GRAPHS;i++){
1192 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=0x%X)",
1194 get_addr_name(&(user_data->ip_src_fwd)),
1195 user_data->port_src_fwd,
1196 get_addr_name(&(user_data->ip_dst_fwd)),
1197 user_data->port_dst_fwd,
1198 user_data->ssrc_fwd);
1201 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=0x%X)",
1203 get_addr_name(&(user_data->ip_src_rev)),
1204 user_data->port_src_rev,
1205 get_addr_name(&(user_data->ip_dst_rev)),
1206 user_data->port_dst_rev,
1207 user_data->ssrc_rev);
1211 dialog_graph_set_title(user_data);
1214 /****************************************************************************/
1215 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
1217 dialog_graph_graph_item_t *it;
1219 it=&dgg->items[idx];
1224 /****************************************************************************/
1225 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
1228 g_snprintf(buf, buf_len, "%ds",t/1000000);
1229 } else if(t>=1000000){
1230 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
1231 } else if(t>=10000){
1232 g_snprintf(buf, buf_len, "%dms",t/1000);
1234 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
1236 g_snprintf(buf, buf_len, "%dus",t);
1240 /****************************************************************************/
1241 static void dialog_graph_draw(user_data_t* user_data)
1244 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
1245 gint32 current_interval;
1246 guint32 left_x_border;
1247 guint32 right_x_border;
1248 guint32 top_y_border;
1249 guint32 bottom_y_border;
1250 #if GTK_MAJOR_VERSION < 2
1253 PangoLayout *layout;
1255 int label_width, label_height;
1256 guint32 draw_width, draw_height;
1257 char label_string[15];
1260 guint32 num_time_intervals;
1261 guint32 max_value; /* max value of seen data */
1262 guint32 max_y; /* max value of the Y scale */
1264 #if GTK_MAJOR_VERSION <2
1265 font = user_data->dlg.dialog_graph.draw_area->style->font;
1267 if(!user_data->dlg.dialog_graph.needs_redraw){
1270 user_data->dlg.dialog_graph.needs_redraw=FALSE;
1273 * Find the length of the intervals we have data for
1274 * so we know how large arrays we need to malloc()
1276 num_time_intervals=user_data->dlg.dialog_graph.num_items;
1277 /* if there isnt anything to do, just return */
1278 if(num_time_intervals==0){
1281 num_time_intervals+=1;
1282 /* XXX move this check to _packet() */
1283 if(num_time_intervals>NUM_GRAPH_ITEMS){
1284 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
1289 * find the max value so we can autoscale the y axis
1292 for(i=0;i<MAX_GRAPHS;i++){
1295 if(!user_data->dlg.dialog_graph.graph[i].display){
1298 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
1301 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1303 /* keep track of the max value we have encountered */
1311 * Clear out old plot
1313 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1314 user_data->dlg.dialog_graph.draw_area->style->white_gc,
1317 user_data->dlg.dialog_graph.draw_area->allocation.width,
1318 user_data->dlg.dialog_graph.draw_area->allocation.height);
1322 * Calculate the y scale we should use
1324 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1325 max_y=yscale_max[MAX_YSCALE-1];
1326 for(i=MAX_YSCALE-1;i>0;i--){
1327 if(max_value<yscale_max[i]){
1328 max_y=yscale_max[i];
1332 /* the user had specified an explicit y scale to use */
1333 max_y=user_data->dlg.dialog_graph.max_y_units;
1337 * Calculate size of borders surrounding the plot
1338 * The border on the right side needs to be adjusted depending
1339 * on the width of the text labels. For simplicity we assume that the
1340 * top y scale label will be the widest one
1342 print_time_scale_string(label_string, 15, max_y);
1343 #if GTK_MAJOR_VERSION < 2
1344 label_width=gdk_string_width(font, label_string);
1345 label_height=gdk_string_height(font, label_string);
1347 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1348 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1351 right_x_border=label_width+20;
1353 bottom_y_border=label_height+20;
1357 * Calculate the size of the drawing area for the actual plot
1359 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1360 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1364 * Draw the y axis and labels
1365 * (we always draw the y scale with 11 ticks along the axis)
1367 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1368 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1370 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1371 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1377 /* first, middle and last tick are slightly longer */
1381 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1382 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1383 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1384 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1385 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1386 /* draw the labels */
1388 print_time_scale_string(label_string, 15, (max_y*i/10));
1389 #if GTK_MAJOR_VERSION < 2
1390 lwidth=gdk_string_width(font, label_string);
1391 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1393 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1394 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1395 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1398 pango_layout_set_text(layout, label_string, -1);
1399 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1400 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1401 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1402 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1403 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1408 print_time_scale_string(label_string, 15, (max_y*i/10));
1409 #if GTK_MAJOR_VERSION < 2
1410 lwidth=gdk_string_width(font, label_string);
1411 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1413 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1414 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1415 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1418 pango_layout_set_text(layout, label_string, -1);
1419 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1420 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1421 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1422 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1423 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1428 print_time_scale_string(label_string, 15, (max_y*i/10));
1429 #if GTK_MAJOR_VERSION < 2
1430 lwidth=gdk_string_width(font, label_string);
1431 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1433 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1434 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1435 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1438 pango_layout_set_text(layout, label_string, -1);
1439 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1440 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1441 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1442 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1443 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1452 * if we have not specified the last_interval via the gui,
1453 * then just pick the current end of the capture so that is scrolls
1454 * nicely when doing live captures
1456 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1457 last_interval=user_data->dlg.dialog_graph.max_interval;
1459 last_interval=user_data->dlg.dialog_graph.last_interval;
1466 /* plot the x-scale */
1467 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc, left_x_border, user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1, user_data->dlg.dialog_graph.pixmap_width-right_x_border+1, user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1);
1469 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1470 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1471 first_interval*=user_data->dlg.dialog_graph.interval;
1478 while(interval_delta<((last_interval-first_interval)/10)){
1479 interval_delta*=delta_multiplier;
1480 if(delta_multiplier==5){
1487 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1490 /* if pixels_per_tick is <5, only draw every 10 ticks */
1491 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1495 if(current_interval%interval_delta){
1501 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1502 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1503 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1504 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1505 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1506 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1510 if(user_data->dlg.dialog_graph.interval>=1000){
1511 g_snprintf(label_string, 15, "%ds", current_interval/1000);
1512 } else if(user_data->dlg.dialog_graph.interval>=100){
1513 g_snprintf(label_string, 15, "%d.%1ds", current_interval/1000,(current_interval/100)%10)
1515 } else if(user_data->dlg.dialog_graph.interval>=10){
1516 g_snprintf(label_string, 15, "%d.%2ds", current_interval/1000,(current_interval/10)%100)
1519 g_snprintf(label_string, 15, "%d.%3ds", current_interval/1000,current_interval%1000);
1521 #if GTK_MAJOR_VERSION < 2
1522 lwidth=gdk_string_width(font, label_string);
1523 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1525 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1526 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1527 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20+label_height,
1530 pango_layout_set_text(layout, label_string, -1);
1531 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1532 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1533 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1534 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1535 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1548 * Draw "x" for Sequence Errors and "m" for Marks
1550 /* Draw the labels Fwd and Rev */
1551 strcpy(label_string,"<-Fwd");
1552 #if GTK_MAJOR_VERSION < 2
1553 lwidth=gdk_string_width(font, label_string);
1554 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1556 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1557 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1558 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+label_height,
1561 pango_layout_set_text(layout, label_string, -1);
1562 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1563 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1564 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1565 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1566 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1569 strcpy(label_string,"<-Rev");
1570 #if GTK_MAJOR_VERSION < 2
1571 lwidth=gdk_string_width(font, label_string);
1572 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1574 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1575 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1576 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9+label_height,
1579 pango_layout_set_text(layout, label_string, -1);
1580 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1581 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1582 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1583 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1584 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1588 /* Draw the marks */
1589 for(i=MAX_GRAPHS-1;i>=0;i--){
1591 guint32 x_pos, prev_x_pos;
1593 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1594 if (!user_data->dlg.dialog_graph.graph[i].display){
1597 /* initialize prev x/y to the low left corner of the graph */
1598 prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1600 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1601 x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1603 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1605 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1606 strcpy(label_string,"x");
1608 strcpy(label_string,"m");
1611 #if GTK_MAJOR_VERSION < 2
1612 lwidth=gdk_string_width(font, label_string);
1613 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1615 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1617 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2)+label_height,
1620 pango_layout_set_text(layout, label_string, -1);
1621 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1622 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1623 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1625 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1634 #if GTK_MAJOR_VERSION >= 2
1635 g_object_unref(G_OBJECT(layout));
1640 * Loop over all graphs and draw them
1642 for(i=MAX_GRAPHS-1;i>=0;i--){
1644 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1645 if (!user_data->dlg.dialog_graph.graph[i].display){
1648 /* initialize prev x/y to the low left corner of the graph */
1649 prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1650 prev_y_pos=draw_height-1+top_y_border;
1652 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1654 x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1655 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1659 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1662 /* dont need to draw anything if the segment
1663 * is entirely above the top of the graph
1665 if( (prev_y_pos==0) && (y_pos==0) ){
1672 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1673 x_pos, draw_height-1+top_y_border,
1683 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1684 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1685 user_data->dlg.dialog_graph.pixmap,
1688 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1691 /* update the scrollbar */
1692 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1693 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1694 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1695 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1696 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1698 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1700 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1701 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1702 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1706 /****************************************************************************/
1707 static void dialog_graph_redraw(user_data_t* user_data)
1709 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1710 dialog_graph_draw(user_data);
1713 /****************************************************************************/
1714 static gint quit(GtkWidget *widget, GdkEventExpose *event _U_)
1716 user_data_t *user_data;
1718 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1720 user_data->dlg.dialog_graph.window = NULL;
1724 /****************************************************************************/
1725 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1727 user_data_t *user_data;
1729 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1735 gdk_draw_pixmap(widget->window,
1736 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1737 user_data->dlg.dialog_graph.pixmap,
1738 event->area.x, event->area.y,
1739 event->area.x, event->area.y,
1740 event->area.width, event->area.height);
1745 /****************************************************************************/
1746 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1748 user_data_t *user_data;
1751 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1757 if(user_data->dlg.dialog_graph.pixmap){
1758 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1759 user_data->dlg.dialog_graph.pixmap=NULL;
1762 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1763 widget->allocation.width,
1764 widget->allocation.height,
1766 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1767 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1769 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1770 widget->style->white_gc,
1773 widget->allocation.width,
1774 widget->allocation.height);
1776 /* set up the colors and the GC structs for this pixmap */
1777 for(i=0;i<MAX_GRAPHS;i++){
1778 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1779 #if GTK_MAJOR_VERSION < 2
1780 colormap = gtk_widget_get_colormap (widget);
1781 if (!gdk_color_alloc (colormap, &user_data->dlg.dialog_graph.graph[i].color)){
1782 g_warning ("Couldn't allocate color");
1785 gdk_gc_set_foreground(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1787 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1791 dialog_graph_redraw(user_data);
1795 /****************************************************************************/
1796 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1798 user_data_t *user_data=(user_data_t *)data;
1801 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1802 if(user_data->dlg.dialog_graph.last_interval==mi){
1805 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1806 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1810 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1812 dialog_graph_redraw(user_data);
1816 /****************************************************************************/
1817 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1819 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1820 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "destroy", quit, user_data);
1821 OBJECT_SET_DATA(user_data->dlg.dialog_graph.draw_area, "user_data_t", user_data);
1823 WIDGET_SET_SIZE(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1825 /* signals needed to handle backing pixmap */
1826 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "expose_event", expose_event, NULL);
1827 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "configure_event", configure_event, user_data);
1829 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1830 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1832 /* create the associated scrollbar */
1833 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1834 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1835 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1836 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1837 SIGNAL_CONNECT(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", scrollbar_changed, user_data);
1840 /****************************************************************************/
1841 static void disable_graph(dialog_graph_graph_t *dgg)
1845 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1850 /****************************************************************************/
1851 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1853 /* this graph is not active, just update display and redraw */
1854 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1856 dialog_graph_redraw(dgg->ud);
1861 cf_retap_packets(&cfile, FALSE);
1862 dialog_graph_redraw(dgg->ud);
1867 /****************************************************************************/
1868 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1874 hbox=gtk_hbox_new(FALSE, 3);
1875 gtk_container_add(GTK_CONTAINER(box), hbox);
1876 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1877 gtk_widget_show(hbox);
1879 g_snprintf(str, 256, "Graph %d", num);
1880 dgg->display_button=gtk_toggle_button_new_with_label(str);
1881 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1882 gtk_widget_show(dgg->display_button);
1883 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1884 SIGNAL_CONNECT(dgg->display_button, "toggled", filter_callback, dgg);
1886 label=gtk_label_new(dgg->title);
1887 gtk_widget_show(label);
1888 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1890 #if GTK_MAJOR_VERSION < 2
1891 /* setting the color of the display button doesn't work */
1892 rc_style = gtk_rc_style_new ();
1893 rc_style->fg[GTK_STATE_NORMAL] = dgg->color;
1894 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG;
1895 rc_style->fg[GTK_STATE_ACTIVE] = dgg->color;
1896 rc_style->color_flags[GTK_STATE_ACTIVE] |= GTK_RC_FG;
1897 rc_style->fg[GTK_STATE_PRELIGHT] = dgg->color;
1898 rc_style->color_flags[GTK_STATE_PRELIGHT] |= GTK_RC_FG;
1899 rc_style->fg[GTK_STATE_SELECTED] = dgg->color;
1900 rc_style->color_flags[GTK_STATE_SELECTED] |= GTK_RC_FG;
1901 rc_style->fg[GTK_STATE_INSENSITIVE] = dgg->color;
1902 rc_style->color_flags[GTK_STATE_INSENSITIVE] |= GTK_RC_FG;
1903 gtk_widget_modify_style (label, rc_style);
1904 gtk_rc_style_unref (rc_style);
1906 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1907 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1908 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1909 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1910 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1916 /****************************************************************************/
1917 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1924 frame=gtk_frame_new("Graphs");
1925 gtk_container_add(GTK_CONTAINER(box), frame);
1926 gtk_widget_show(frame);
1928 vbox=gtk_vbox_new(FALSE, 1);
1929 gtk_container_add(GTK_CONTAINER(frame), vbox);
1930 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
1931 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1932 gtk_widget_show(vbox);
1934 for(i=0;i<MAX_GRAPHS;i++){
1935 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1938 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1939 gtk_widget_show(label);
1940 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1945 /****************************************************************************/
1946 static void yscale_select(GtkWidget *item, gpointer key)
1949 user_data_t *user_data;
1951 user_data=(user_data_t *)key;
1952 val=(long)OBJECT_GET_DATA(item, "yscale_max");
1954 user_data->dlg.dialog_graph.max_y_units=val;
1955 dialog_graph_redraw(user_data);
1958 /****************************************************************************/
1959 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1962 user_data_t *user_data;
1964 user_data=(user_data_t *)key;
1965 val=(long)OBJECT_GET_DATA(item, "pixels_per_tick");
1966 user_data->dlg.dialog_graph.pixels_per_tick=val;
1967 dialog_graph_redraw(user_data);
1970 /****************************************************************************/
1971 static void tick_interval_select(GtkWidget *item, gpointer key)
1974 user_data_t *user_data;
1976 user_data=(user_data_t *)key;
1977 val=(long)OBJECT_GET_DATA(item, "tick_interval");
1979 user_data->dlg.dialog_graph.interval=val;
1980 cf_retap_packets(&cfile, FALSE);
1981 dialog_graph_redraw(user_data);
1984 /****************************************************************************/
1985 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1988 GtkWidget *menu_item;
1991 for(i=0;i<MAX_YSCALE;i++){
1992 if(yscale_max[i]==AUTO_MAX_YSCALE){
1995 g_snprintf(str, 15, "%u ms", yscale_max[i]/1000);
1997 menu_item=gtk_menu_item_new_with_label(str);
1998 OBJECT_SET_DATA(menu_item, "yscale_max",
1999 GUINT_TO_POINTER(yscale_max[i]));
2000 SIGNAL_CONNECT(menu_item, "activate", yscale_select, user_data);
2001 gtk_widget_show(menu_item);
2002 gtk_menu_append(GTK_MENU(menu), menu_item);
2007 /****************************************************************************/
2008 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
2011 GtkWidget *menu_item;
2014 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
2015 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
2016 menu_item=gtk_menu_item_new_with_label(str);
2018 OBJECT_SET_DATA(menu_item, "pixels_per_tick",
2019 GUINT_TO_POINTER(pixels_per_tick[i]));
2020 SIGNAL_CONNECT(menu_item, "activate", pixels_per_tick_select, user_data);
2021 gtk_widget_show(menu_item);
2022 gtk_menu_append(GTK_MENU(menu), menu_item);
2024 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
2029 /****************************************************************************/
2030 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
2033 GtkWidget *menu_item;
2036 for(i=0;i<MAX_TICK_VALUES;i++){
2037 if(tick_interval_values[i]>=1000){
2038 g_snprintf(str, 15, "%u sec", tick_interval_values[i]/1000);
2039 } else if(tick_interval_values[i]>=100){
2040 g_snprintf(str, 15, "0.%1u sec", (tick_interval_values[i]/100)%10);
2041 } else if(tick_interval_values[i]>=10){
2042 g_snprintf(str, 15, "0.%02u sec", (tick_interval_values[i]/10)%10);
2044 g_snprintf(str, 15, "0.%03u sec", (tick_interval_values[i])%10);
2047 menu_item=gtk_menu_item_new_with_label(str);
2048 OBJECT_SET_DATA(menu_item, "tick_interval",
2049 GUINT_TO_POINTER(tick_interval_values[i]));
2050 SIGNAL_CONNECT(menu_item, "activate", tick_interval_select, (gpointer)user_data);
2051 gtk_widget_show(menu_item);
2052 gtk_menu_append(GTK_MENU(menu), menu_item);
2054 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
2058 /****************************************************************************/
2059 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
2063 GtkWidget *option_menu;
2066 hbox=gtk_hbox_new(FALSE, 0);
2067 gtk_container_add(GTK_CONTAINER(box), hbox);
2068 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2069 gtk_widget_show(hbox);
2071 label=gtk_label_new(name);
2072 gtk_widget_show(label);
2073 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2075 option_menu=gtk_option_menu_new();
2076 menu=gtk_menu_new();
2077 (*func)(user_data, menu);
2078 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
2079 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
2080 gtk_widget_show(option_menu);
2083 /****************************************************************************/
2084 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
2086 GtkWidget *frame_vbox;
2090 frame_vbox=gtk_vbox_new(FALSE, 0);
2091 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
2092 gtk_widget_show(frame_vbox);
2094 frame = gtk_frame_new("X Axis");
2095 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
2096 gtk_widget_show(frame);
2098 vbox=gtk_vbox_new(FALSE, 0);
2099 gtk_container_add(GTK_CONTAINER(frame), vbox);
2100 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
2101 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
2102 gtk_widget_show(vbox);
2104 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
2105 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
2107 frame = gtk_frame_new("Y Axis");
2108 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
2109 gtk_widget_show(frame);
2111 vbox=gtk_vbox_new(FALSE, 0);
2112 gtk_container_add(GTK_CONTAINER(frame), vbox);
2113 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
2114 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
2115 gtk_widget_show(vbox);
2117 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
2122 /****************************************************************************/
2123 static void dialog_graph_init_window(user_data_t* user_data)
2127 GtkWidget *bt_close;
2129 /* create the main window */
2130 user_data->dlg.dialog_graph.window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
2132 vbox=gtk_vbox_new(FALSE, 0);
2133 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
2134 gtk_widget_show(vbox);
2136 create_draw_area(user_data, vbox);
2138 hbox=gtk_hbox_new(FALSE, 3);
2139 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2140 gtk_container_border_width(GTK_CONTAINER(hbox), 3);
2141 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2142 gtk_widget_show(hbox);
2144 create_filter_area(user_data, hbox);
2145 create_ctrl_area(user_data, hbox);
2147 dialog_graph_set_title(user_data);
2149 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
2150 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2151 gtk_widget_show(hbox);
2153 bt_close = OBJECT_GET_DATA(hbox, GTK_STOCK_CLOSE);
2154 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
2156 SIGNAL_CONNECT(user_data->dlg.dialog_graph.window, "delete_event", window_delete_event_cb, NULL);
2158 gtk_widget_show(user_data->dlg.dialog_graph.window);
2159 window_present(user_data->dlg.dialog_graph.window);
2164 /****************************************************************************/
2165 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2167 if (user_data->dlg.dialog_graph.window != NULL) {
2168 /* There's already a graph window; reactivate it. */
2169 reactivate_window(user_data->dlg.dialog_graph.window);
2173 dialog_graph_init_window(user_data);
2177 /****************************************************************************/
2178 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2182 if (user_data->dlg.selected_clist!=NULL) {
2183 fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
2184 GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
2185 cf_goto_frame(&cfile, fnumber);
2190 static void draw_stat(user_data_t *user_data);
2192 /****************************************************************************/
2193 /* re-dissects all packets */
2194 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2196 GString *error_string;
2198 /* remove tap listener */
2199 protect_thread_critical_region();
2200 remove_tap_listener(user_data);
2201 unprotect_thread_critical_region();
2203 /* register tap listener */
2204 error_string = register_tap_listener("rtp", user_data, NULL,
2205 rtp_reset, rtp_packet, rtp_draw);
2206 if (error_string != NULL) {
2207 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
2208 g_string_free(error_string, TRUE);
2212 /* retap all packets */
2213 cf_retap_packets(&cfile, FALSE);
2215 /* draw statistics info */
2216 draw_stat(user_data);
2218 gtk_clist_sort(user_data->dlg.clist_fwd);
2219 gtk_clist_sort(user_data->dlg.clist_rev);
2222 /****************************************************************************/
2223 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2228 if (user_data->dlg.selected_clist==NULL)
2231 clist = user_data->dlg.selected_clist;
2232 row = user_data->dlg.selected_row + 1;
2234 while (gtk_clist_get_text(clist,row,6,&text)) {
2235 if (strcmp(text, OK_TEXT) != 0) {
2236 gtk_clist_select_row(clist, row, 0);
2237 gtk_clist_moveto(clist, row, 0, 0.5, 0);
2245 while (gtk_clist_get_text(clist,row,6,&text) && row<user_data->dlg.selected_row) {
2246 if (strcmp(text, OK_TEXT) != 0) {
2247 gtk_clist_select_row(clist, row, 0);
2248 gtk_clist_moveto(clist, row, 0, 0.5, 0);
2255 /****************************************************************************/
2256 /* when we want to save the information */
2257 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
2260 GtkWidget *rev, *forw, *both;
2261 user_data_t *user_data;
2267 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
2269 /* Perhaps the user specified a directory instead of a file.
2270 Check whether they did. */
2271 if (test_for_directory(g_dest) == EISDIR) {
2272 /* It's a directory - set the file selection box to display it. */
2273 set_last_open_dir(g_dest);
2275 file_selection_set_current_folder(fs, get_last_open_dir());
2279 rev = (GtkWidget*)OBJECT_GET_DATA(bt, "reversed_rb");
2280 forw = (GtkWidget*)OBJECT_GET_DATA(bt, "forward_rb");
2281 both = (GtkWidget*)OBJECT_GET_DATA(bt, "both_rb");
2282 user_data = (user_data_t*)OBJECT_GET_DATA(bt, "user_data");
2284 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
2285 fp = eth_fopen(g_dest, "w");
2287 open_failure_alert_box(g_dest, errno, TRUE);
2291 if (GTK_TOGGLE_BUTTON(both)->active) {
2292 fprintf(fp, "Forward\n");
2294 write_failure_alert_box(g_dest, errno);
2300 for(j = 0; j < NUM_COLS; j++) {
2302 fprintf(fp,"%s",titles[j]);
2304 fprintf(fp,",%s",titles[j]);
2309 write_failure_alert_box(g_dest, errno);
2313 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
2314 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
2315 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
2317 fprintf(fp,"%s",columnText);
2319 fprintf(fp,",%s",columnText);
2324 write_failure_alert_box(g_dest, errno);
2330 if (fclose(fp) == EOF) {
2331 write_failure_alert_box(g_dest, errno);
2336 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
2338 if (GTK_TOGGLE_BUTTON(both)->active) {
2339 fp = eth_fopen(g_dest, "a");
2341 open_failure_alert_box(g_dest, errno, TRUE);
2344 fprintf(fp, "\nReverse\n");
2346 write_failure_alert_box(g_dest, errno);
2351 fp = eth_fopen(g_dest, "w");
2353 open_failure_alert_box(g_dest, errno, TRUE);
2357 for(j = 0; j < NUM_COLS; j++) {
2359 fprintf(fp,"%s",titles[j]);
2361 fprintf(fp,",%s",titles[j]);
2366 write_failure_alert_box(g_dest, errno);
2370 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
2371 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
2372 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
2374 fprintf(fp,"%s",columnText);
2376 fprintf(fp,",%s",columnText);
2381 write_failure_alert_box(g_dest, errno);
2386 if (fclose(fp) == EOF) {
2387 write_failure_alert_box(g_dest, errno);
2392 window_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
2395 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2397 user_data->dlg.save_csv_as_w = NULL;
2400 /* when the user wants to save the csv information in a file */
2401 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
2405 GtkWidget *label_format;
2406 GtkWidget *channels_label;
2407 GSList *channels_group = NULL;
2408 GtkWidget *forward_rb;
2409 GtkWidget *reversed_rb;
2413 if (user_data->dlg.save_csv_as_w != NULL) {
2414 /* There's already a Save CSV info dialog box; reactivate it. */
2415 reactivate_window(user_data->dlg.save_csv_as_w);
2419 user_data->dlg.save_csv_as_w = gtk_file_selection_new("Wireshark: Save Data As CSV");
2421 /* Container for each row of widgets */
2422 vertb = gtk_vbox_new(FALSE, 0);
2423 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
2424 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
2425 vertb, FALSE, FALSE, 0);
2426 gtk_widget_show (vertb);
2428 table1 = gtk_table_new (2, 4, FALSE);
2429 gtk_widget_show (table1);
2430 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2431 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2432 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2434 label_format = gtk_label_new ("Format: Comma Separated Values");
2435 gtk_widget_show (label_format);
2436 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2437 (GtkAttachOptions) (GTK_FILL),
2438 (GtkAttachOptions) (0), 0, 0);
2441 channels_label = gtk_label_new ("Channels:");
2442 gtk_widget_show (channels_label);
2443 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2444 (GtkAttachOptions) (GTK_FILL),
2445 (GtkAttachOptions) (0), 0, 0);
2446 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2448 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2449 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
2450 gtk_widget_show (forward_rb);
2451 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2452 (GtkAttachOptions) (GTK_FILL),
2453 (GtkAttachOptions) (0), 0, 0);
2455 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2456 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
2457 gtk_widget_show (reversed_rb);
2458 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2459 (GtkAttachOptions) (GTK_FILL),
2460 (GtkAttachOptions) (0), 0, 0);
2462 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2463 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
2464 gtk_widget_show (both_rb);
2465 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2466 (GtkAttachOptions) (GTK_FILL),
2467 (GtkAttachOptions) (0), 0, 0);
2469 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2471 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
2472 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
2473 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
2474 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
2475 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
2476 SIGNAL_CONNECT(ok_bt, "clicked", save_csv_as_ok_cb,
2477 user_data->dlg.save_csv_as_w);
2479 window_set_cancel_button(user_data->dlg.save_csv_as_w,
2480 GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button, window_cancel_button_cb);
2482 SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "delete_event", window_delete_event_cb, NULL);
2483 SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "destroy",
2484 save_csv_as_destroy_cb, user_data);
2486 gtk_widget_show(user_data->dlg.save_csv_as_w);
2487 window_present(user_data->dlg.save_csv_as_w);
2491 /****************************************************************************/
2492 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2494 /* Note that we no longer have a Save voice info dialog box. */
2495 user_data->dlg.save_voice_as_w = NULL;
2498 /****************************************************************************/
2499 /* here we save it into a file that user specified */
2500 /* XXX what about endians here? could go something wrong? */
2501 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2503 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
2504 gchar f_pd[1] = {0};
2505 gchar r_pd[1] = {0};
2508 guint32 f_write_silence = 0;
2509 guint32 r_write_silence = 0;
2511 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2512 gboolean stop_flag = FALSE;
2515 forw_fd = eth_open(user_data->f_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2518 rev_fd = eth_open(user_data->r_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2524 /* open file for saving */
2525 to_fd = eth_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2532 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE,
2535 if (format == SAVE_AU_FORMAT) /* au format */
2537 /* First we write the .au header. XXX Hope this is endian independant */
2538 /* the magic word 0x2e736e64 == .snd */
2539 *pd = (unsigned char)0x2e; nchars=eth_write(to_fd, pd, 1);
2540 *pd = (unsigned char)0x73; nchars=eth_write(to_fd, pd, 1);
2541 *pd = (unsigned char)0x6e; nchars=eth_write(to_fd, pd, 1);
2542 *pd = (unsigned char)0x64; nchars=eth_write(to_fd, pd, 1);
2543 /* header offset == 24 bytes */
2544 *pd = (unsigned char)0x00; nchars=eth_write(to_fd, pd, 1);
2545 nchars=eth_write(to_fd, pd, 1);
2546 nchars=eth_write(to_fd, pd, 1);
2547 *pd = (unsigned char)0x18; nchars=eth_write(to_fd, pd, 1);
2548 /* total length, it is permited to set this to 0xffffffff */
2549 *pd = (unsigned char)0xff; nchars=eth_write(to_fd, pd, 1);
2550 nchars=eth_write(to_fd, pd, 1);
2551 nchars=eth_write(to_fd, pd, 1);
2552 nchars=eth_write(to_fd, pd, 1);
2553 /* encoding format == 8 bit ulaw */
2554 *pd = (unsigned char)0x00; nchars=eth_write(to_fd, pd, 1);
2555 nchars=eth_write(to_fd, pd, 1);
2556 nchars=eth_write(to_fd, pd, 1);
2557 *pd = (unsigned char)0x01; nchars=eth_write(to_fd, pd, 1);
2558 /* sample rate == 8000 Hz */
2559 *pd = (unsigned char)0x00; nchars=eth_write(to_fd, pd, 1);
2560 nchars=eth_write(to_fd, pd, 1);
2561 *pd = (unsigned char)0x1f; nchars=eth_write(to_fd, pd, 1);
2562 *pd = (unsigned char)0x40; nchars=eth_write(to_fd, pd, 1);
2564 *pd = (unsigned char)0x00; nchars=eth_write(to_fd, pd, 1);
2565 nchars=eth_write(to_fd, pd, 1);
2566 nchars=eth_write(to_fd, pd, 1);
2567 *pd = (unsigned char)0x01; nchars=eth_write(to_fd, pd, 1);
2571 /* only forward direction */
2572 case SAVE_FORWARD_DIRECTION_MASK: {
2573 progbar_count = user_data->forward.saveinfo.count;
2574 progbar_quantum = user_data->forward.saveinfo.count/100;
2575 while ((fread = read(forw_fd, f_pd, 1)) > 0) {
2578 if((count > progbar_nextstep) && (count <= progbar_count)) {
2579 update_progress_dlg(progbar,
2580 (gfloat) count/progbar_count, "Saving");
2581 progbar_nextstep = progbar_nextstep + progbar_quantum;
2585 if (user_data->forward.statinfo.pt == PT_PCMU){
2588 else if(user_data->forward.statinfo.pt == PT_PCMA){
2589 tmp = (gint16 )alaw2linear(*f_pd);
2590 *pd = (unsigned char)linear2ulaw(tmp);
2596 destroy_progress_dlg(progbar);
2600 fwritten = eth_write(to_fd, pd, 1);
2601 if ((fwritten < fread) || (fwritten < 0) || (fread < 0)) {
2605 destroy_progress_dlg(progbar);
2611 /* only reversed direction */
2612 case SAVE_REVERSE_DIRECTION_MASK: {
2613 progbar_count = user_data->reversed.saveinfo.count;
2614 progbar_quantum = user_data->reversed.saveinfo.count/100;
2615 while ((rread = read(rev_fd, r_pd, 1)) > 0) {
2618 if((count > progbar_nextstep) && (count <= progbar_count)) {
2619 update_progress_dlg(progbar,
2620 (gfloat) count/progbar_count, "Saving");
2621 progbar_nextstep = progbar_nextstep + progbar_quantum;
2625 if (user_data->forward.statinfo.pt == PT_PCMU){
2628 else if(user_data->forward.statinfo.pt == PT_PCMA){
2629 tmp = (gint16 )alaw2linear(*r_pd);
2630 *pd = (unsigned char)linear2ulaw(tmp);
2636 destroy_progress_dlg(progbar);
2640 rwritten = eth_write(to_fd, pd, 1);
2641 if ((rwritten < rread) || (rwritten < 0) || (rread < 0)) {
2645 destroy_progress_dlg(progbar);
2651 /* both directions */
2652 case SAVE_BOTH_DIRECTION_MASK: {
2653 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2654 (progbar_count = user_data->forward.saveinfo.count) :
2655 (progbar_count = user_data->reversed.saveinfo.count);
2656 progbar_quantum = progbar_count/100;
2657 /* since conversation in one way can start later than in the other one,
2658 * we have to write some silence information for one channel */
2659 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2660 f_write_silence = (guint32)
2661 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2663 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2664 r_write_silence = (guint32)
2665 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2670 if((count > progbar_nextstep) && (count <= progbar_count)) {
2671 update_progress_dlg(progbar,
2672 (gfloat) count/progbar_count, "Saving");
2673 progbar_nextstep = progbar_nextstep + progbar_quantum;
2676 if(f_write_silence > 0) {
2677 rread = read(rev_fd, r_pd, 1);
2678 switch (user_data->forward.statinfo.reg_pt) {
2680 *f_pd = SILENCE_PCMU;
2683 *f_pd = SILENCE_PCMA;
2689 else if(r_write_silence > 0) {
2690 fread = read(forw_fd, f_pd, 1);
2691 switch (user_data->forward.statinfo.reg_pt) {
2693 *r_pd = SILENCE_PCMU;
2696 *r_pd = SILENCE_PCMA;
2703 fread = read(forw_fd, f_pd, 1);
2704 rread = read(rev_fd, r_pd, 1);
2706 if ((rread == 0) && (fread == 0))
2708 if ((user_data->forward.statinfo.pt == PT_PCMU) && (user_data->reversed.statinfo.pt == PT_PCMU)){
2709 tmp = ulaw2linear(*r_pd);
2710 tmp += ulaw2linear(*f_pd);
2711 *pd = (unsigned char)linear2ulaw(tmp/2);
2713 else if((user_data->forward.statinfo.pt == PT_PCMA) && (user_data->reversed.statinfo.pt == PT_PCMA)){
2714 tmp = alaw2linear(*r_pd);
2715 tmp += alaw2linear(*f_pd);
2716 *pd = (unsigned char)linear2ulaw(tmp/2);
2723 destroy_progress_dlg(progbar);
2728 rwritten = eth_write(to_fd, pd, 1);
2729 if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
2733 destroy_progress_dlg(progbar);
2740 else if (format == SAVE_RAW_FORMAT) /* raw format */
2744 /* only forward direction */
2745 case SAVE_FORWARD_DIRECTION_MASK: {
2746 progbar_count = user_data->forward.saveinfo.count;
2747 progbar_quantum = user_data->forward.saveinfo.count/100;
2751 /* only reversed direction */
2752 case SAVE_REVERSE_DIRECTION_MASK: {
2753 progbar_count = user_data->reversed.saveinfo.count;
2754 progbar_quantum = user_data->reversed.saveinfo.count/100;
2762 destroy_progress_dlg(progbar);
2769 /* XXX how do you just copy the file? */
2770 while ((rread = read(fd, pd, 1)) > 0) {
2773 if((count > progbar_nextstep) && (count <= progbar_count)) {
2774 update_progress_dlg(progbar,
2775 (gfloat) count/progbar_count, "Saving");
2776 progbar_nextstep = progbar_nextstep + progbar_quantum;
2780 rwritten = eth_write(to_fd, pd, 1);
2782 if ((rwritten < rread) || (rwritten < 0) || (rread < 0)) {
2786 destroy_progress_dlg(progbar);
2792 destroy_progress_dlg(progbar);
2800 /****************************************************************************/
2801 /* the user wants to save in a file */
2802 /* XXX support for different formats is currently commented out */
2803 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
2806 /*GtkWidget *wav, *sw;*/
2807 GtkWidget *au, *raw;
2808 GtkWidget *rev, *forw, *both;
2809 user_data_t *user_data;
2810 gint channels , format;
2812 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
2814 /* Perhaps the user specified a directory instead of a file.
2815 Check whether they did. */
2816 if (test_for_directory(g_dest) == EISDIR) {
2817 /* It's a directory - set the file selection box to display it. */
2818 set_last_open_dir(g_dest);
2820 file_selection_set_current_folder(fs, get_last_open_dir());
2824 /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
2825 sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
2826 au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
2827 raw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "raw_rb");
2828 rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
2829 forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
2830 both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
2831 user_data = (user_data_t *)OBJECT_GET_DATA(ok_bt, "user_data");
2833 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2834 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2835 * disable the ok button or disable the buttons for direction if only one is not ok. The
2836 * problem is if we open the save voice dialog and then click the refresh button and maybe
2837 * the state changes, so we can't save anymore. In this case we should be able to update
2838 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2841 /* we can not save in both directions */
2842 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2843 /* there are many combinations here, we just exit when first matches */
2844 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2845 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2846 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2847 "Can't save in a file: Unsupported codec!");
2848 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2849 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2850 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2851 "Can't save in a file: Wrong length of captured packets!");
2852 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2853 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2854 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2855 "Can't save in a file: RTP data with padding!");
2856 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2857 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2858 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2859 "Can't save in a file: Not all data in all packets was captured!");
2861 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2862 "Can't save in a file: File I/O problem!");
2865 /* we can not save forward direction */
2866 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2867 (GTK_TOGGLE_BUTTON (both)->active))) {
2868 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2869 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2870 "Can't save forward direction in a file: Unsupported codec!");
2871 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2872 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2873 "Can't save forward direction in a file: Wrong length of captured packets!");
2874 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2875 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2876 "Can't save forward direction in a file: RTP data with padding!");
2877 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2878 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2879 "Can't save forward direction in a file: Not all data in all packets was captured!");
2881 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2882 "Can't save forward direction in a file: File I/O problem!");
2885 /* we can not save reversed direction */
2886 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2887 (GTK_TOGGLE_BUTTON (both)->active))) {
2888 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2889 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2890 "Can't save reversed direction in a file: Unsupported codec!");
2891 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2892 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2893 "Can't save reversed direction in a file: Wrong length of captured packets!");
2894 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2895 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2896 "Can't save reversed direction in a file: RTP data with padding!");
2897 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2898 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2899 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2900 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2901 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2902 "Can't save reversed direction in a file: No RTP data!");
2904 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2905 "Can't save reversed direction in a file: File I/O problem!");
2909 /*if (GTK_TOGGLE_BUTTON (wav)->active)
2910 format = SAVE_WAV_FORMAT;
2911 else */if (GTK_TOGGLE_BUTTON (au)->active)
2912 format = SAVE_AU_FORMAT;
2913 /*else if (GTK_TOGGLE_BUTTON (sw)->active)
2914 format = SAVE_SW_FORMAT;*/
2915 else if (GTK_TOGGLE_BUTTON (raw)->active)
2916 format = SAVE_RAW_FORMAT;
2918 format = SAVE_NONE_FORMAT;
2920 if (GTK_TOGGLE_BUTTON (rev)->active)
2921 channels = SAVE_REVERSE_DIRECTION_MASK;
2922 else if (GTK_TOGGLE_BUTTON (both)->active)
2923 channels = SAVE_BOTH_DIRECTION_MASK;
2925 channels = SAVE_FORWARD_DIRECTION_MASK;
2927 /* direction/format validity*/
2928 if (format == SAVE_AU_FORMAT)
2930 /* make sure streams are alaw/ulaw */
2931 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != PT_PCMA) && (user_data->forward.statinfo.pt != PT_PCMU)){
2932 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2933 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2936 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != PT_PCMA) && (user_data->reversed.statinfo.pt != PT_PCMU)){
2937 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2938 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2941 /* make sure pt's don't differ */
2942 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2943 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2944 "Can't save in a file: Forward and reverse direction differ in type");
2948 else if (format == SAVE_RAW_FORMAT)
2950 /* can't save raw in both directions */
2951 if (channels == SAVE_BOTH_DIRECTION_MASK){
2952 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2953 "Can't save in a file: Unable to save raw data in both directions");
2959 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2960 "Can't save in a file: Invalid save format");
2964 if(!copy_file(g_dest, channels, format, user_data)) {
2965 /* XXX - report the error type! */
2966 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2967 "An error occurred while saving voice in a file!");
2971 window_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
2974 /****************************************************************************/
2975 /* when the user wants to save the voice information in a file */
2976 /* XXX support for different formats is currently commented out */
2977 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2981 GtkWidget *label_format;
2982 GtkWidget *channels_label;
2983 GSList *format_group = NULL;
2984 GSList *channels_group = NULL;
2985 GtkWidget *forward_rb;
2986 GtkWidget *reversed_rb;
2988 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2993 /* if we can't save in a file: wrong codec, cut packets or other errors */
2994 /* shold the error arise here or later when you click ok button ?
2995 * if we do it here, then we must disable the refresh button, so we don't do it here */
2997 if (user_data->dlg.save_voice_as_w != NULL) {
2998 /* There's already a Save voice info dialog box; reactivate it. */
2999 reactivate_window(user_data->dlg.save_voice_as_w);
3003 /* XXX - use file_selection from dlg_utils instead! */
3004 user_data->dlg.save_voice_as_w = gtk_file_selection_new("Wireshark: Save Payload As ...");
3006 /* Container for each row of widgets */
3007 vertb = gtk_vbox_new(FALSE, 0);
3008 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
3009 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
3010 vertb, FALSE, FALSE, 0);
3011 gtk_widget_show (vertb);
3013 table1 = gtk_table_new (2, 4, FALSE);
3014 gtk_widget_show (table1);
3015 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
3016 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
3017 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
3019 /*label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
3020 gtk_widget_show (label_format);
3021 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
3022 (GtkAttachOptions) (GTK_FILL),
3023 (GtkAttachOptions) (0), 0, 0);*/
3025 label_format = gtk_label_new ("Format: ");
3026 gtk_widget_show (label_format);
3027 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
3028 (GtkAttachOptions) (GTK_FILL),
3029 (GtkAttachOptions) (0), 0, 0);
3031 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5);
3033 raw_rb = gtk_radio_button_new_with_label (format_group, ".raw");
3034 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (raw_rb));
3035 gtk_widget_show (raw_rb);
3036 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
3037 (GtkAttachOptions) (GTK_FILL),
3038 (GtkAttachOptions) (0), 0, 0);
3041 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
3042 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
3043 gtk_widget_show (au_rb);
3044 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
3045 (GtkAttachOptions) (GTK_FILL),
3046 (GtkAttachOptions) (0), 0, 0);
3048 /* we support .au - ulaw*/
3049 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
3050 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
3051 gtk_widget_show (wav_rb);
3052 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
3053 (GtkAttachOptions) (GTK_FILL),
3054 (GtkAttachOptions) (0), 0, 0);
3056 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
3057 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
3058 gtk_widget_show (sw_rb);
3059 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
3060 (GtkAttachOptions) (GTK_FILL),
3061 (GtkAttachOptions) (0), 0, 0);
3062 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
3063 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
3064 gtk_widget_show (au_rb);
3065 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
3066 (GtkAttachOptions) (GTK_FILL),
3067 (GtkAttachOptions) (0), 0, 0);
3071 channels_label = gtk_label_new ("Channels:");
3072 gtk_widget_show (channels_label);
3073 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
3074 (GtkAttachOptions) (GTK_FILL),
3075 (GtkAttachOptions) (0), 0, 0);
3076 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
3078 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
3079 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
3080 gtk_widget_show (forward_rb);
3081 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
3082 (GtkAttachOptions) (GTK_FILL),
3083 (GtkAttachOptions) (0), 0, 0);
3085 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
3086 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
3087 gtk_widget_show (reversed_rb);
3088 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
3089 (GtkAttachOptions) (GTK_FILL),
3090 (GtkAttachOptions) (0), 0, 0);
3092 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
3093 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
3094 gtk_widget_show (both_rb);
3095 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
3096 (GtkAttachOptions) (GTK_FILL),
3097 (GtkAttachOptions) (0), 0, 0);
3099 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
3101 /* if one direction is nok we don't allow saving
3102 XXX this is not ok since the user can click the refresh button and cause changes
3103 but we can not update this window. So we move all the decision on the time the ok
3105 if (user_data->forward.saved == FALSE) {
3106 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
3107 gtk_widget_set_sensitive(forward_rb, FALSE);
3108 gtk_widget_set_sensitive(both_rb, FALSE);
3110 else if (user_data->reversed.saved == FALSE) {
3111 gtk_widget_set_sensitive(reversed_rb, FALSE);
3112 gtk_widget_set_sensitive(both_rb, FALSE);
3116 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
3117 /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);*/
3118 OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
3119 /*OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
3120 OBJECT_SET_DATA(ok_bt, "raw_rb", raw_rb);
3121 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
3122 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
3123 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
3124 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
3125 SIGNAL_CONNECT(ok_bt, "clicked", save_voice_as_ok_cb,
3126 user_data->dlg.save_voice_as_w);
3128 window_set_cancel_button(user_data->dlg.save_voice_as_w,
3129 GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button, window_cancel_button_cb);
3131 SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "delete_event",
3132 window_delete_event_cb, NULL);
3133 SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "destroy",
3134 save_voice_as_destroy_cb, user_data);
3136 gtk_widget_show(user_data->dlg.save_voice_as_w);
3137 window_present(user_data->dlg.save_voice_as_w);
3141 /****************************************************************************/
3142 /* when we are finished with redisection, we add the label for the statistic */
3143 static void draw_stat(user_data_t *user_data)
3145 gchar label_max[200];
3146 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
3147 - user_data->forward.statinfo.start_seq_nr + 1;
3148 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
3149 - user_data->reversed.statinfo.start_seq_nr + 1;
3150 gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
3151 gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
3152 double f_perc, r_perc;
3154 f_perc = (double)(f_lost*100)/(double)f_expected;
3159 r_perc = (double)(r_lost*100)/(double)r_expected;
3164 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
3165 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
3166 " Sequence errors = %u",
3167 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
3168 user_data->forward.statinfo.total_nr,
3169 f_expected, f_lost, f_perc, user_data->forward.statinfo.sequence);
3171 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
3173 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
3174 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
3175 " Sequence errors = %u",
3176 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
3177 user_data->reversed.statinfo.total_nr,
3178 r_expected, r_lost, r_perc, user_data->reversed.statinfo.sequence);
3180 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
3187 /****************************************************************************/
3188 /* append a line to clist */
3189 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
3190 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
3191 gchar *timeStr, guint32 pkt_len, GdkColor *color)
3198 data[0]=&field[0][0];
3199 data[1]=&field[1][0];
3200 data[2]=&field[2][0];
3201 data[3]=&field[3][0];
3202 data[4]=&field[4][0];
3203 data[5]=&field[5][0];
3204 data[6]=&field[6][0];
3205 data[7]=&field[7][0];
3206 data[8]=&field[8][0];
3208 /* save the current locale */
3209 savelocale = setlocale(LC_NUMERIC, NULL);
3210 /* switch to "C" locale to avoid problems with localized decimal separators
3211 in g_snprintf("%f") functions */
3212 setlocale(LC_NUMERIC, "C");
3213 g_snprintf(field[0], 20, "%u", number);
3214 g_snprintf(field[1], 20, "%u", seq_num);
3215 g_snprintf(field[2], 20, "%.2f", delta);
3216 g_snprintf(field[3], 20, "%.2f", jitter);
3217 g_snprintf(field[4], 20, "%.2f", bandwidth);
3218 g_snprintf(field[5], 20, "%s", marker? "SET" : "");
3219 g_snprintf(field[6], 40, "%s", status);
3220 g_snprintf(field[7], 32, "%s", timeStr);
3221 g_snprintf(field[8], 20, "%u", pkt_len);
3222 /* restore previous locale setting */
3223 setlocale(LC_NUMERIC, savelocale);
3225 added_row = gtk_clist_append(GTK_CLIST(clist), data);
3226 gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
3227 gtk_clist_set_background(GTK_CLIST(clist), added_row, color);
3231 /****************************************************************************/
3232 /* callback for sorting columns of clist */
3233 static gint rtp_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
3240 const GtkCListRow *row1 = ptr1;
3241 const GtkCListRow *row2 = ptr2;
3243 text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
3244 text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
3246 switch(clist->sort_column){
3247 /* columns representing strings */
3251 return strcmp (text1, text2);
3252 /* columns representing ints */
3259 /* columns representing floats */
3265 if (fabs(f1-f2)<0.0000005)
3271 g_assert_not_reached();
3276 /****************************************************************************/
3278 click_column_cb(GtkCList *clist, gint column, gpointer data)
3280 column_arrows *col_arrows = (column_arrows *) data;
3283 gtk_clist_freeze(clist);
3285 for (i = 0; i < NUM_COLS; i++) {
3286 gtk_widget_hide(col_arrows[i].ascend_pm);
3287 gtk_widget_hide(col_arrows[i].descend_pm);
3290 if (column == clist->sort_column) {
3291 if (clist->sort_type == GTK_SORT_ASCENDING) {
3292 clist->sort_type = GTK_SORT_DESCENDING;
3293 gtk_widget_show(col_arrows[column].descend_pm);
3295 clist->sort_type = GTK_SORT_ASCENDING;
3296 gtk_widget_show(col_arrows[column].ascend_pm);
3299 clist->sort_type = GTK_SORT_ASCENDING;
3300 gtk_widget_show(col_arrows[column].ascend_pm);
3301 gtk_clist_set_sort_column(clist, column);
3303 gtk_clist_thaw(clist);
3305 gtk_clist_sort(clist);
3309 /****************************************************************************/
3310 /* Add the packet list */
3312 GtkWidget* create_clist(user_data_t* user_data)
3314 GtkWidget* clist_fwd;
3316 /* clist for the information */
3317 clist_fwd = gtk_clist_new(NUM_COLS);
3318 gtk_widget_show(clist_fwd);
3319 SIGNAL_CONNECT(clist_fwd, "select_row", on_clist_select_row, user_data);
3321 gtk_clist_column_titles_show(GTK_CLIST(clist_fwd));
3322 gtk_clist_set_compare_func(GTK_CLIST(clist_fwd), rtp_sort_column);
3323 gtk_clist_set_sort_column(GTK_CLIST(clist_fwd), 0);
3324 gtk_clist_set_sort_type(GTK_CLIST(clist_fwd), GTK_SORT_ASCENDING);
3326 /* hide date and length column */
3327 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
3328 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 8, FALSE);
3330 /* column widths and justification */
3331 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 60);
3332 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 75);
3333 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 75);
3334 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 75);
3335 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 50);
3336 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 5, 75);
3337 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_RIGHT);
3338 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_RIGHT);
3339 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
3340 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
3341 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
3342 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
3343 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 6, GTK_JUSTIFY_CENTER);
3348 /****************************************************************************/
3349 /* Add the sort by column feature for a packet clist */
3351 column_arrows* add_sort_by_column(GtkWidget* window, GtkWidget* clist,
3352 user_data_t* user_data _U_)
3354 column_arrows *col_arrows;
3355 GdkBitmap *ascend_bm, *descend_bm;
3356 GdkPixmap *ascend_pm, *descend_pm;
3357 GtkStyle *win_style;
3358 GtkWidget *column_lb;
3361 col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
3362 win_style = gtk_widget_get_style(window);
3363 ascend_pm = gdk_pixmap_create_from_xpm_d(window->window,
3365 &win_style->bg[GTK_STATE_NORMAL],
3366 (gchar **)clist_ascend_xpm);
3367 descend_pm = gdk_pixmap_create_from_xpm_d(window->window,
3369 &win_style->bg[GTK_STATE_NORMAL],
3370 (gchar **)clist_descend_xpm);
3372 for (i=0; i<NUM_COLS; i++) {
3373 col_arrows[i].table = gtk_table_new(2, 2, FALSE);
3374 gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
3375 column_lb = gtk_label_new(titles[i]);
3376 gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
3377 gtk_widget_show(column_lb);
3379 col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
3380 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
3381 col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
3382 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
3383 /* make packet-nr be the default sort order */
3385 gtk_widget_show(col_arrows[i].ascend_pm);
3387 gtk_clist_set_column_widget(GTK_CLIST(clist), i, col_arrows[i].table);
3388 gtk_widget_show(col_arrows[i].table);
3391 SIGNAL_CONNECT(clist, "click-column", click_column_cb, col_arrows);
3396 /****************************************************************************/
3397 /* Create the dialog box with all widgets */
3398 static void create_rtp_dialog(user_data_t* user_data)
3400 GtkWidget *window = NULL;
3401 GtkWidget *clist_fwd;
3402 GtkWidget *clist_rev;
3403 GtkWidget *label_stats_fwd;
3404 GtkWidget *label_stats_rev;
3405 GtkWidget *notebook;
3407 GtkWidget *main_vb, *page, *page_r;
3409 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3410 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3411 #ifdef USE_CONVERSATION_GRAPH
3412 GtkWidget *graph_bt;
3414 GtkWidget *graph_bt;
3415 gchar label_forward[150];
3416 gchar label_reverse[150];
3418 gchar str_ip_src[16];
3419 gchar str_ip_dst[16];
3420 column_arrows *col_arrows_fwd;
3421 column_arrows *col_arrows_rev;
3423 window = window_new(GTK_WINDOW_TOPLEVEL, "Wireshark: RTP Stream Analysis");
3424 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3426 /* Container for each row of widgets */
3427 main_vb = gtk_vbox_new(FALSE, 2);
3428 gtk_container_border_width(GTK_CONTAINER(main_vb), 2);
3429 gtk_container_add(GTK_CONTAINER(window), main_vb);
3430 gtk_widget_show(main_vb);
3433 strcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)));
3434 strcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)));
3436 g_snprintf(label_forward, 149,
3437 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3438 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3441 strcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)));
3442 strcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)));
3444 g_snprintf(label_reverse, 149,
3445 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3446 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3448 /* Start a notebook for flipping between sets of changes */
3449 notebook = gtk_notebook_new();
3450 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3451 OBJECT_SET_DATA(window, "notebook", notebook);
3453 user_data->dlg.notebook_signal_id = SIGNAL_CONNECT(notebook, "switch_page", on_notebook_switch_page,
3456 /* page for forward connection */
3457 page = gtk_vbox_new(FALSE, 8);
3458 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3460 /* direction label */
3461 label = gtk_label_new(label_forward);
3462 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3464 /* place for some statistics */
3465 label_stats_fwd = gtk_label_new("\n");
3466 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3468 /* scrolled window */
3469 scrolled_window = scrolled_window_new(NULL, NULL);
3472 clist_fwd = create_clist(user_data);
3473 gtk_widget_show(clist_fwd);
3474 gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
3475 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3476 gtk_widget_show(scrolled_window);
3479 label = gtk_label_new(" Forward Direction ");
3480 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3482 /* same page for reversed connection */
3483 page_r = gtk_vbox_new(FALSE, 8);
3484 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3485 label = gtk_label_new(label_reverse);
3486 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3487 label_stats_rev = gtk_label_new("\n");
3488 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3490 scrolled_window_r = scrolled_window_new(NULL, NULL);
3492 clist_rev = create_clist(user_data);
3493 gtk_widget_show(clist_rev);
3494 gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
3495 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3496 gtk_widget_show(scrolled_window_r);
3498 label = gtk_label_new(" Reversed Direction ");
3499 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3501 /* page for help&about or future
3502 page_help = gtk_hbox_new(FALSE, 5);
3503 label = gtk_label_new(" Future ");
3504 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3505 frame = gtk_frame_new("");
3506 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3507 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3508 gtk_container_add(GTK_CONTAINER(frame), text);
3509 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3510 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3513 /* show all notebooks */
3514 gtk_widget_show_all(notebook);
3517 box4 = gtk_hbutton_box_new();
3518 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3519 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3520 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3521 gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0);
3522 gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
3523 gtk_widget_show(box4);
3525 voice_bt = gtk_button_new_with_label("Save payload...");
3526 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3527 gtk_widget_show(voice_bt);
3528 SIGNAL_CONNECT(voice_bt, "clicked", on_save_bt_clicked, user_data);
3530 csv_bt = gtk_button_new_with_label("Save as CSV...");
3531 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3532 gtk_widget_show(csv_bt);
3533 SIGNAL_CONNECT(csv_bt, "clicked", save_csv_as_cb, user_data);
3535 refresh_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_REFRESH);
3536 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3537 gtk_widget_show(refresh_bt);
3538 SIGNAL_CONNECT(refresh_bt, "clicked", on_refresh_bt_clicked, user_data);
3540 goto_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_JUMP_TO);
3541 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3542 gtk_widget_show(goto_bt);
3543 SIGNAL_CONNECT(goto_bt, "clicked", on_goto_bt_clicked, user_data);
3545 graph_bt = gtk_button_new_with_label("Graph");
3546 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3547 gtk_widget_show(graph_bt);
3548 SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
3551 #ifdef USE_CONVERSATION_GRAPH
3552 graph_bt = gtk_button_new_with_label("Graph");
3553 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3554 gtk_widget_show(graph_bt);
3555 SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
3558 next_bt = gtk_button_new_with_label("Next non-Ok");
3559 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3560 gtk_widget_show(next_bt);
3561 SIGNAL_CONNECT(next_bt, "clicked", on_next_bt_clicked, user_data);
3563 close_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
3564 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3565 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3566 gtk_widget_show(close_bt);
3567 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3569 SIGNAL_CONNECT(window, "delete_event", window_delete_event_cb, NULL);
3570 SIGNAL_CONNECT(window, "destroy", on_destroy, user_data);
3572 gtk_widget_show(window);
3573 window_present(window);
3575 /* sort by column feature */
3576 col_arrows_fwd = add_sort_by_column(window, clist_fwd, user_data);
3577 col_arrows_rev = add_sort_by_column(window, clist_rev, user_data);
3579 /* some widget references need to be saved for outside use */
3580 user_data->dlg.window = window;
3581 user_data->dlg.clist_fwd = GTK_CLIST(clist_fwd);
3582 user_data->dlg.clist_rev = GTK_CLIST(clist_rev);
3583 user_data->dlg.label_stats_fwd = label_stats_fwd;
3584 user_data->dlg.label_stats_rev = label_stats_rev;
3585 user_data->dlg.notebook = notebook;
3586 user_data->dlg.selected_clist = GTK_CLIST(clist_fwd);
3587 user_data->dlg.selected_row = 0;
3588 user_data->dlg.col_arrows_fwd = col_arrows_fwd;
3589 user_data->dlg.col_arrows_rev = col_arrows_rev;
3593 /****************************************************************************/
3594 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3595 const gchar* proto_field, guint32* p_result)
3598 proto_node *proto_sibling_node;
3599 header_field_info *hfssrc;
3602 finfo = PITEM_FINFO(ptree_node);
3604 if (hfinformation==(finfo->hfinfo)) {
3605 hfssrc = proto_registrar_get_byname(proto_field);
3608 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3609 ptree_node=ptree_node->next) {
3610 finfo=PITEM_FINFO(ptree_node);
3611 if (hfssrc==finfo->hfinfo) {
3612 if (hfinformation->type==FT_IPv4) {
3613 ipv4 = fvalue_get(&finfo->value);
3614 *p_result = ipv4_get_net_order_addr(ipv4);
3617 *p_result = fvalue_get_uinteger(&finfo->value);
3626 proto_sibling_node = ptree_node->next;
3628 if (proto_sibling_node) {
3629 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3635 /****************************************************************************/
3636 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3637 const gchar* proto_name,
3638 const gchar* proto_field,
3641 proto_node *ptree_node;
3642 header_field_info *hfinformation;
3644 hfinformation = proto_registrar_get_byname(proto_name);
3645 if (hfinformation == NULL)
3648 ptree_node = ((proto_node *)protocol_tree)->first_child;
3652 return process_node(ptree_node, hfinformation, proto_field, p_result);
3656 /* XXX just copied from gtk/rpc_stat.c */
3657 void protect_thread_critical_region(void);
3658 void unprotect_thread_critical_region(void);
3660 /****************************************************************************/
3662 address *ip_src_fwd,
3663 guint16 port_src_fwd,
3664 address *ip_dst_fwd,
3665 guint16 port_dst_fwd,
3667 address *ip_src_rev,
3668 guint16 port_src_rev,
3669 address *ip_dst_rev,
3670 guint16 port_dst_rev,
3674 user_data_t *user_data;
3677 static color_t col[MAX_GRAPHS] = {
3678 {0, 0x0000, 0x0000, 0x0000},
3679 {0, 0xffff, 0x0000, 0x0000},
3680 {0, 0x0000, 0xffff, 0x0000},
3681 {0, 0x0000, 0x0000, 0xffff}
3685 user_data = g_malloc(sizeof(user_data_t));
3687 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3688 user_data->port_src_fwd = port_src_fwd;
3689 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3690 user_data->port_dst_fwd = port_dst_fwd;
3691 user_data->ssrc_fwd = ssrc_fwd;
3692 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3693 user_data->port_src_rev = port_src_rev;
3694 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3695 user_data->port_dst_rev = port_dst_rev;
3696 user_data->ssrc_rev = ssrc_rev;
3699 /* file names for storing sound data */
3700 /*XXX: check for errors*/
3701 fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
3704 fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
3707 user_data->forward.saveinfo.fp = NULL;
3708 user_data->reversed.saveinfo.fp = NULL;
3709 user_data->dlg.save_voice_as_w = NULL;
3710 user_data->dlg.save_csv_as_w = NULL;
3711 user_data->dlg.dialog_graph.window = NULL;
3713 #ifdef USE_CONVERSATION_GRAPH
3714 user_data->dlg.graph_window = NULL;
3715 user_data->series_fwd.value_pairs = NULL;
3716 user_data->series_rev.value_pairs = NULL;
3719 /* init dialog_graph */
3720 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3721 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3722 user_data->dlg.dialog_graph.draw_area=NULL;
3723 user_data->dlg.dialog_graph.pixmap=NULL;
3724 user_data->dlg.dialog_graph.scrollbar=NULL;
3725 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3726 user_data->dlg.dialog_graph.pixmap_width=500;
3727 user_data->dlg.dialog_graph.pixmap_height=200;
3728 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3729 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3730 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3731 user_data->dlg.dialog_graph.max_interval=0;
3732 user_data->dlg.dialog_graph.num_items=0;
3733 user_data->dlg.dialog_graph.start_time = -1;
3735 for(i=0;i<MAX_GRAPHS;i++){
3736 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3737 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3738 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3739 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3740 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3741 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3742 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3743 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3746 /* create the dialog box */
3747 create_rtp_dialog(user_data);
3749 /* proceed as if the Refresh button would have been pressed */
3750 on_refresh_bt_clicked(NULL, user_data);
3753 /****************************************************************************/
3754 /* entry point from main menu */
3755 static void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3758 guint16 port_src_fwd;
3760 guint16 port_dst_fwd;
3761 guint32 ssrc_fwd = 0;
3763 guint16 port_src_rev;
3765 guint16 port_dst_rev;
3766 guint32 ssrc_rev = 0;
3767 unsigned int version_fwd;
3769 gchar filter_text[256];
3772 epan_dissect_t *edt;
3775 gboolean frame_matched;
3777 GList *strinfo_list;
3778 GList *filtered_list = NULL;
3779 rtp_stream_info_t *strinfo;
3782 /* Try to compile the filter. */
3783 strcpy(filter_text,"rtp && rtp.version && rtp.ssrc && (ip || ipv6)");
3784 if (!dfilter_compile(filter_text, &sfcode)) {
3785 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, dfilter_error_msg);
3788 /* we load the current file into cf variable */
3790 fdata = cf->current_frame;
3792 /* we are on the selected frame now */
3794 return; /* if we exit here it's an error */
3796 /* dissect the current frame */
3797 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3798 cf->pd, fdata->cap_len, &err, &err_info)) {
3799 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3800 cf_read_error_message(err, err_info), cf->filename);
3803 edt = epan_dissect_new(TRUE, FALSE);
3804 epan_dissect_prime_dfilter(edt, sfcode);
3805 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3806 frame_matched = dfilter_apply_edt(sfcode, edt);
3808 /* if it is not an rtp frame, show the rtpstream dialog */
3809 frame_matched = dfilter_apply_edt(sfcode, edt);
3810 if (frame_matched != 1) {
3811 epan_dissect_free(edt);
3812 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3813 "You didn't choose a RTP packet!");
3817 /* ok, it is a RTP frame, so let's get the ip and port values */
3818 COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
3819 COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
3820 port_src_fwd = edt->pi.srcport;
3821 port_dst_fwd = edt->pi.destport;
3823 /* assume the inverse ip/port combination for the reverse direction */
3824 COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
3825 COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
3826 port_src_rev = edt->pi.destport;
3827 port_dst_rev = edt->pi.srcport;
3829 /* check if it is RTP Version 2 */
3830 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3831 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3832 "RTP Version != 2 isn't supported!");
3836 /* now we need the SSRC value of the current frame */
3837 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3838 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3839 "SSRC value couldn't be found!");
3843 /* Scan for rtpstream */
3845 /* search for reversed direction in the global rtp streams list */
3847 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3848 while (strinfo_list)
3850 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3851 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3852 && strinfo->src_port==port_src_fwd
3853 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3854 && strinfo->dest_port==port_dst_fwd)
3856 filtered_list = g_list_prepend(filtered_list, strinfo);
3859 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3860 && strinfo->src_port==port_src_rev
3861 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3862 && strinfo->dest_port==port_dst_rev)
3865 filtered_list = g_list_append(filtered_list, strinfo);
3867 ssrc_rev = strinfo->ssrc;
3870 strinfo_list = g_list_next(strinfo_list);
3873 /* if more than one reverse streams found, we let the user choose the right one */
3875 rtpstream_dlg_show(filtered_list);
3894 /****************************************************************************/
3896 rtp_analysis_init(const char *dummy _U_,void* userdata _U_)
3898 rtp_analysis_cb(NULL, NULL);
3901 /****************************************************************************/
3903 register_tap_listener_rtp_analysis(void)
3905 register_stat_cmd_arg("rtp", rtp_analysis_init,NULL);
3907 register_stat_menu_item("RTP/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3908 rtp_analysis_cb, NULL, NULL, NULL);