2 * RTP analysis addition for ethereal
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 * Ethereal - Network traffic analyzer
18 * By Gerald Combs <gerald@ethereal.com>
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 #include "rtp_analysis.h"
44 #include "rtp_stream.h"
45 #include "rtp_stream_dlg.h"
47 #ifdef USE_CONVERSATION_GRAPH
48 #include "../graph/graph.h"
51 #include <epan/epan_dissect.h>
52 #include <epan/filesystem.h>
57 #include <epan/dissectors/packet-rtp.h>
59 #include <epan/rtp_pt.h>
60 #include <epan/addr_resolv.h>
64 #include "gtkglobals.h"
66 #include <epan/stat_cmd_args.h>
67 #include "dlg_utils.h"
68 #include "gui_utils.h"
69 #include "alert_box.h"
70 #include "simple_dialog.h"
71 #include "../stat_menu.h"
72 #include "gui_stat_menu.h"
74 #include "progress_dlg.h"
75 #include "compat_macros.h"
78 #include "image/clist_ascend.xpm"
79 #include "image/clist_descend.xpm"
94 #include "file_util.h"
96 /****************************************************************************/
98 typedef struct column_arrows {
100 GtkWidget *ascend_pm;
101 GtkWidget *descend_pm;
105 #define NUM_GRAPH_ITEMS 100000
106 #define MAX_YSCALE 16
107 #define AUTO_MAX_YSCALE 0
109 #define GRAPH_FWD_JITTER 0
110 #define GRAPH_FWD_DIFF 1
111 #define GRAPH_REV_JITTER 2
112 #define GRAPH_REV_DIFF 3
113 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
115 #define MAX_PIXELS_PER_TICK 4
116 #define DEFAULT_PIXELS_PER_TICK 1
117 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
118 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
120 #define MAX_TICK_VALUES 5
121 #define DEFAULT_TICK_VALUE 1
122 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
123 typedef struct _dialog_graph_graph_item_t {
126 } dialog_graph_graph_item_t;
128 typedef struct _dialog_graph_graph_t {
129 struct _user_data_t *ud;
130 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
133 GtkWidget *display_button;
138 } dialog_graph_graph_t;
141 typedef struct _dialog_graph_t {
142 gboolean needs_redraw;
143 gint32 interval; /* measurement interval in ms */
144 guint32 last_interval;
145 guint32 max_interval; /* XXX max_interval and num_items are redundant */
147 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
149 GtkWidget *draw_area;
151 GtkAdjustment *scrollbar_adjustment;
152 GtkWidget *scrollbar;
160 typedef struct _dialog_data_t {
164 GtkWidget *label_stats_fwd;
165 GtkWidget *label_stats_rev;
166 column_arrows *col_arrows_fwd;
167 column_arrows *col_arrows_rev;
169 GtkCList *selected_clist;
170 GtkWidget *save_voice_as_w;
171 GtkWidget *save_csv_as_w;
172 gint notebook_signal_id;
174 dialog_graph_t dialog_graph;
175 #ifdef USE_CONVERSATION_GRAPH
176 GtkWidget *graph_window;
180 #define OK_TEXT "[ Ok ]"
182 typedef struct _key_value {
188 /* RTP sampling clock rates for fixed payload types as defined in
189 http://www.iana.org/assignments/rtp-parameters */
190 static const key_value clock_map[] = {
196 {PT_DVI4_8000, 8000},
197 {PT_DVI4_16000, 16000},
201 {PT_L16_STEREO, 44100},
202 {PT_L16_MONO, 44100},
208 {PT_DVI4_11025, 11025},
209 {PT_DVI4_22050, 22050},
221 #define NUM_CLOCK_VALUES (sizeof clock_map / sizeof clock_map[0])
224 get_clock_rate(guint32 key)
228 for (i = 0; i < NUM_CLOCK_VALUES; i++) {
229 if (clock_map[i].key == key)
230 return clock_map[i].value;
235 typedef struct _mimetype_and_clock {
236 const gchar *pt_mime_name_str;
238 } mimetype_and_clock;
239 /* RTP sampling clock rates for
240 "In addition to the RTP payload formats (encodings) listed in the RTP
241 Payload Types table, there are additional payload formats that do not
242 have static RTP payload types assigned but instead use dynamic payload
243 type number assignment. Each payload format is named by a registered
245 http://www.iana.org/assignments/rtp-parameters.
247 static const mimetype_and_clock mimetype_and_clock_map[] = {
248 {"AMR", 8000}, /* [RFC3267] */
249 {"AMR-WB", 16000}, /* [RFC3267] */
250 {"EVRC", 8000}, /* [RFC3558] */
251 {"EVRC0", 8000}, /* [RFC3558] */
252 {"G7221", 16000}, /* [RFC3047] */
253 {"G726-16", 8000}, /* [RFC3551] */
254 {"G726-24", 8000}, /* [RFC3551] */
255 {"G726-32", 8000}, /* [RFC3551] */
256 {"G726-40", 8000}, /* [RFC3551] */
257 {"G729D", 8000}, /* [RFC3551] */
258 {"G729E", 8000}, /* [RFC3551] */
259 {"GSM-EFR", 8000}, /* [RFC3551] */
260 {"mpa-robust", 90000}, /* [RFC3119] */
261 {"SMV", 8000}, /* [RFC3558] */
262 {"SMV0", 8000}, /* [RFC3558] */
263 {"red", 1000}, /* [RFC4102] */
264 {"t140", 1000}, /* [RFC4103] */
265 {"BMPEG", 90000}, /* [RFC2343],[RFC3555] */
266 {"BT656", 90000}, /* [RFC2431],[RFC3555] */
267 {"DV", 90000}, /* [RFC3189] */
268 {"H263-1998", 90000}, /* [RFC2429],[RFC3555] */
269 {"H263-2000", 90000}, /* [RFC2429],[RFC3555] */
270 {"MP1S", 90000}, /* [RFC2250],[RFC3555] */
271 {"MP2P", 90000}, /* [RFC2250],[RFC3555] */
272 {"MP4V-ES", 90000}, /* [RFC3016] */
273 {"pointer", 90000}, /* [RFC2862] */
274 {"raw", 90000}, /* [RFC4175] */
277 #define NUM_DYN_CLOCK_VALUES (sizeof mimetype_and_clock_map / sizeof mimetype_and_clock_map[0])
280 get_dyn_pt_clock_rate(gchar *payload_type_str)
284 for (i = 0; i < NUM_DYN_CLOCK_VALUES; i++) {
285 if (strncasecmp(mimetype_and_clock_map[i].pt_mime_name_str,payload_type_str,(strlen(mimetype_and_clock_map[i].pt_mime_name_str))) == 0)
286 return clock_map[i].value;
292 /* type of error when saving voice in a file didn't succeed */
295 TAP_RTP_WRONG_LENGTH,
296 TAP_RTP_PADDING_ERROR,
298 TAP_RTP_FILE_OPEN_ERROR,
302 #if GTK_MAJOR_VERSION < 2
303 GtkRcStyle *rc_style;
304 GdkColormap *colormap;
307 typedef struct _tap_rtp_save_info_t {
310 error_type_t error_type;
312 } tap_rtp_save_info_t;
315 /* structure that holds the information about the forward and reversed direction */
316 struct _info_direction {
317 tap_rtp_stat_t statinfo;
318 tap_rtp_save_info_t saveinfo;
321 #define TMPNAMSIZE 100
323 #define SILENCE_PCMU (guint8)0xFF
324 #define SILENCE_PCMA (guint8)0x55
326 /* structure that holds general information about the connection
327 * and structures for both directions */
328 typedef struct _user_data_t {
329 /* tap associated data*/
331 guint16 port_src_fwd;
333 guint16 port_dst_fwd;
336 guint16 port_src_rev;
338 guint16 port_dst_rev;
341 struct _info_direction forward;
342 struct _info_direction reversed;
344 char f_tempname[TMPNAMSIZE];
345 char r_tempname[TMPNAMSIZE];
347 /* dialog associated data */
350 #ifdef USE_CONVERSATION_GRAPH
351 time_series_t series_fwd;
352 time_series_t series_rev;
358 static const gchar *titles[9] = {
370 #define SAVE_FORWARD_DIRECTION_MASK 0x01
371 #define SAVE_REVERSE_DIRECTION_MASK 0x02
372 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
374 #define SAVE_AU_FORMAT 2
375 #define SAVE_RAW_FORMAT 4
378 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_);
379 /****************************************************************************/
380 static void enable_graph(dialog_graph_graph_t *dgg)
387 static void dialog_graph_reset(user_data_t* user_data);
391 /****************************************************************************/
394 /****************************************************************************/
395 /* when there is a [re]reading of packet's */
397 rtp_reset(void *user_data_arg)
399 user_data_t *user_data = user_data_arg;
400 user_data->forward.statinfo.first_packet = TRUE;
401 user_data->reversed.statinfo.first_packet = TRUE;
402 user_data->forward.statinfo.max_delta = 0;
403 user_data->reversed.statinfo.max_delta = 0;
404 user_data->forward.statinfo.max_jitter = 0;
405 user_data->reversed.statinfo.max_jitter = 0;
406 user_data->forward.statinfo.mean_jitter = 0;
407 user_data->reversed.statinfo.mean_jitter = 0;
408 user_data->forward.statinfo.delta = 0;
409 user_data->reversed.statinfo.delta = 0;
410 user_data->forward.statinfo.diff = 0;
411 user_data->reversed.statinfo.diff = 0;
412 user_data->forward.statinfo.jitter = 0;
413 user_data->reversed.statinfo.jitter = 0;
414 user_data->forward.statinfo.bandwidth = 0;
415 user_data->reversed.statinfo.bandwidth = 0;
416 user_data->forward.statinfo.total_bytes = 0;
417 user_data->reversed.statinfo.total_bytes = 0;
418 user_data->forward.statinfo.bw_start_index = 0;
419 user_data->reversed.statinfo.bw_start_index = 0;
420 user_data->forward.statinfo.bw_index = 0;
421 user_data->reversed.statinfo.bw_index = 0;
422 user_data->forward.statinfo.timestamp = 0;
423 user_data->reversed.statinfo.timestamp = 0;
424 user_data->forward.statinfo.max_nr = 0;
425 user_data->reversed.statinfo.max_nr = 0;
426 user_data->forward.statinfo.total_nr = 0;
427 user_data->reversed.statinfo.total_nr = 0;
428 user_data->forward.statinfo.sequence = 0;
429 user_data->reversed.statinfo.sequence = 0;
430 user_data->forward.statinfo.start_seq_nr = 0;
431 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
432 user_data->forward.statinfo.stop_seq_nr = 0;
433 user_data->reversed.statinfo.stop_seq_nr = 0;
434 user_data->forward.statinfo.cycles = 0;
435 user_data->reversed.statinfo.cycles = 0;
436 user_data->forward.statinfo.under = FALSE;
437 user_data->reversed.statinfo.under = FALSE;
438 user_data->forward.statinfo.start_time = 0;
439 user_data->reversed.statinfo.start_time = 0;
440 user_data->forward.statinfo.time = 0;
441 user_data->reversed.statinfo.time = 0;
442 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
443 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
445 user_data->forward.saveinfo.count = 0;
446 user_data->reversed.saveinfo.count = 0;
447 user_data->forward.saveinfo.saved = FALSE;
448 user_data->reversed.saveinfo.saved = FALSE;
450 /* clear the dialog box clists */
451 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
452 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
454 /* reset graph info */
455 dialog_graph_reset(user_data);
457 #ifdef USE_CONVERSATION_GRAPH
458 if (user_data->dlg.graph_window != NULL)
459 window_destroy(user_data->dlg.graph_window);
461 g_array_free(user_data->series_fwd.value_pairs, TRUE);
462 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
464 g_array_free(user_data->series_rev.value_pairs, TRUE);
465 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
468 /* XXX check for error at fclose? */
469 if (user_data->forward.saveinfo.fp != NULL)
470 fclose(user_data->forward.saveinfo.fp);
471 if (user_data->reversed.saveinfo.fp != NULL)
472 fclose(user_data->reversed.saveinfo.fp);
473 user_data->forward.saveinfo.fp = eth_fopen(user_data->f_tempname, "wb");
474 if (user_data->forward.saveinfo.fp == NULL)
475 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
476 user_data->reversed.saveinfo.fp = eth_fopen(user_data->r_tempname, "wb");
477 if (user_data->reversed.saveinfo.fp == NULL)
478 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
482 /****************************************************************************/
483 static int rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
485 dialog_graph_graph_item_t *it;
489 /* we sometimes get called when dgg is disabled.
490 this is a bug since the tap listener should be removed first */
495 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
498 * Find which interval this is supposed to to in and store the
499 * interval index as idx
501 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
502 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
504 rtp_time = nstime_to_sec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
508 idx = (guint32)(rtp_time*1000)/dgg->ud->dlg.dialog_graph.interval;
510 /* some sanity checks */
511 if((idx<0)||(idx>=NUM_GRAPH_ITEMS)){
515 /* update num_items */
516 if((guint32)idx > dgg->ud->dlg.dialog_graph.num_items){
517 dgg->ud->dlg.dialog_graph.num_items=idx;
518 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
522 * Find the appropriate dialog_graph_graph_item_t structure
527 * Use the max value to highlight RTP problems
529 if (value > it->value) {
532 it->flags = it->flags | statinfo->flags;
537 /****************************************************************************/
538 /* here we can redraw the output */
540 static void rtp_draw(void *prs _U_)
545 /* forward declarations */
546 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
547 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
548 gchar *timeStr, guint32 pkt_len, GdkColor *color);
550 static int rtp_packet_add_info(GtkCList *clist,
551 tap_rtp_stat_t *statinfo, packet_info *pinfo,
552 const struct _rtp_info *rtpinfo);
554 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
555 tap_rtp_stat_t *statinfo,
557 const struct _rtp_info *rtpinfo);
560 /****************************************************************************/
561 /* whenever a RTP packet is seen by the tap listener */
562 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
564 user_data_t *user_data = user_data_arg;
565 const struct _rtp_info *rtpinfo = rtpinfo_arg;
566 #ifdef USE_CONVERSATION_GRAPH
569 /* we ignore packets that are not displayed */
570 if (pinfo->fd->flags.passed_dfilter == 0)
572 /* also ignore RTP Version != 2 */
573 else if (rtpinfo->info_version !=2)
575 /* is it the forward direction? */
576 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
577 && CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
578 && user_data->port_src_fwd == pinfo->srcport
579 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
580 && user_data->port_dst_fwd == pinfo->destport) {
581 #ifdef USE_CONVERSATION_GRAPH
582 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
583 vp.fnumber = pinfo->fd->num;
584 g_array_append_val(user_data->series_fwd.value_pairs, vp);
586 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
587 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));
588 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));
589 rtp_packet_add_info(user_data->dlg.clist_fwd,
590 &(user_data->forward.statinfo), pinfo, rtpinfo);
591 rtp_packet_save_payload(&(user_data->forward.saveinfo),
592 &(user_data->forward.statinfo), pinfo, rtpinfo);
594 /* is it the reversed direction? */
595 else if (user_data->ssrc_rev == rtpinfo->info_sync_src
596 && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
597 && user_data->port_src_rev == pinfo->srcport
598 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
599 && user_data->port_dst_rev == pinfo->destport) {
600 #ifdef USE_CONVERSATION_GRAPH
601 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
602 vp.fnumber = pinfo->fd->num;
603 g_array_append_val(user_data->series_rev.value_pairs, vp);
605 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
606 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));
607 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));
608 rtp_packet_add_info(user_data->dlg.clist_rev,
609 &(user_data->reversed.statinfo), pinfo, rtpinfo);
610 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
611 &(user_data->reversed.statinfo), pinfo, rtpinfo);
618 /****************************************************************************/
619 int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
621 const struct _rtp_info *rtpinfo)
624 double current_jitter;
629 /* check payload type */
630 if (rtpinfo->info_payload_type == PT_CN
631 || rtpinfo->info_payload_type == PT_CN_OLD)
632 statinfo->flags |= STAT_FLAG_PT_CN;
633 if (statinfo->pt == PT_CN
634 || statinfo->pt == PT_CN_OLD)
635 statinfo->flags |= STAT_FLAG_FOLLOW_PT_CN;
636 if (rtpinfo->info_payload_type != statinfo->pt)
637 statinfo->flags |= STAT_FLAG_PT_CHANGE;
638 statinfo->pt = rtpinfo->info_payload_type;
640 * XXX - should "get_clock_rate()" return 0 for unknown
641 * payload types, presumably meaning that we should
642 * just ignore this packet?
644 if (statinfo->pt < 96 ){
645 clock_rate = get_clock_rate(statinfo->pt);
646 }else{ /* dynamic PT */
647 if ( rtpinfo->info_payload_type_str != NULL )
648 clock_rate = get_dyn_pt_clock_rate(rtpinfo-> info_payload_type_str);
651 /* store the current time and calculate the current jitter */
652 current_time = nstime_to_sec(&pinfo->fd->rel_ts);
653 current_diff = fabs (current_time - (statinfo->time) - ((double)(rtpinfo->info_timestamp)-(double)(statinfo->timestamp))/clock_rate);
654 current_jitter = statinfo->jitter + ( current_diff - statinfo->jitter)/16;
655 statinfo->delta = current_time-(statinfo->time);
656 statinfo->jitter = current_jitter;
657 statinfo->diff = current_diff;
659 /* calculate the BW in Kbps adding the IP+UDP header to the RTP -> 20bytes(IP)+8bytes(UDP) = 28bytes */
660 statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + 28;
661 statinfo->bw_history[statinfo->bw_index].time = current_time;
662 /* check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
663 while ((statinfo->bw_history[statinfo->bw_start_index].time+1)<current_time){
664 statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;
665 statinfo->bw_start_index++;
666 if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
668 statinfo->total_bytes += rtpinfo->info_data_len + 28;
669 statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
670 statinfo->bw_index++;
671 if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
674 /* is this the first packet we got in this direction? */
675 if (statinfo->first_packet) {
676 statinfo->start_seq_nr = rtpinfo->info_seq_num;
677 statinfo->start_time = current_time;
679 statinfo->jitter = 0;
681 statinfo->flags |= STAT_FLAG_FIRST;
682 statinfo->first_packet = FALSE;
684 /* is it a packet with the mark bit set? */
685 if (rtpinfo->info_marker_set) {
686 if (rtpinfo->info_timestamp > statinfo->timestamp){
687 statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
688 statinfo->flags |= STAT_FLAG_MARKER;
691 statinfo->flags |= STAT_FLAG_WRONG_TIMESTAMP;
694 /* is it a regular packet? */
695 if (!(statinfo->flags & STAT_FLAG_FIRST)
696 && !(statinfo->flags & STAT_FLAG_MARKER)
697 && !(statinfo->flags & STAT_FLAG_PT_CN)
698 && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
699 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
700 /* include it in maximum delta calculation */
701 if (statinfo->delta > statinfo->max_delta) {
702 statinfo->max_delta = statinfo->delta;
703 statinfo->max_nr = pinfo->fd->num;
705 /* maximum and mean jitter calculation */
706 if (statinfo->jitter > statinfo->max_jitter) {
707 statinfo->max_jitter = statinfo->jitter;
709 statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1);
711 /* regular payload change? (CN ignored) */
712 if (!(statinfo->flags & STAT_FLAG_FIRST)
713 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
714 if ((statinfo->pt != statinfo->reg_pt)
715 && (statinfo->reg_pt != PT_UNDEFINED)) {
716 statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
720 /* set regular payload*/
721 if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
722 statinfo->reg_pt = statinfo->pt;
726 /* When calculating expected rtp packets the seq number can wrap around
727 * so we have to count the number of cycles
728 * Variable cycles counts the wraps around in forwarding connection and
729 * under is flag that indicates where we are
731 * XXX how to determine number of cycles with all possible lost, late
732 * and duplicated packets without any doubt? It seems to me, that
733 * because of all possible combination of late, duplicated or lost
734 * packets, this can only be more or less good approximation
736 * There are some combinations (rare but theoretically possible),
737 * where below code won't work correctly - statistic may be wrong then.
740 /* so if the current sequence number is less than the start one
741 * we assume, that there is another cycle running */
742 if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
744 statinfo->under = TRUE;
746 /* what if the start seq nr was 0? Then the above condition will never
747 * be true, so we add another condition. XXX The problem would arise
748 * if one of the packets with seq nr 0 or 65535 would be lost or late */
749 else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
750 (statinfo->under == FALSE)){
752 statinfo->under = TRUE;
754 /* the whole round is over, so reset the flag */
755 else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
756 statinfo->under = FALSE;
759 /* Since it is difficult to count lost, duplicate or late packets separately,
760 * we would like to know at least how many times the sequence number was not ok */
762 /* if the current seq number equals the last one or if we are here for
763 * the first time, then it is ok, we just store the current one as the last one */
764 if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
765 statinfo->seq_num = rtpinfo->info_seq_num;
766 /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
767 else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
768 statinfo->seq_num = rtpinfo->info_seq_num;
770 else if (statinfo->seq_num+1 < rtpinfo->info_seq_num) {
771 statinfo->seq_num = rtpinfo->info_seq_num;
772 statinfo->sequence++;
773 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
775 /* late or duplicated */
776 else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
777 statinfo->sequence++;
778 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
780 statinfo->time = current_time;
781 statinfo->timestamp = rtpinfo->info_timestamp;
782 statinfo->stop_seq_nr = rtpinfo->info_seq_num;
783 statinfo->total_nr++;
789 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
790 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
791 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
792 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
794 /****************************************************************************/
795 /* adds statistics information from the packet to the clist */
796 static int rtp_packet_add_info(GtkCList *clist,
797 tap_rtp_stat_t *statinfo, packet_info *pinfo,
798 const struct _rtp_info *rtpinfo)
805 GdkColor color = COLOR_DEFAULT;
806 then = pinfo->fd->abs_ts.secs;
807 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
808 tm_tmp = localtime(&then);
809 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
812 tm_tmp->tm_year + 1900,
818 if (statinfo->pt == PT_CN) {
819 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
822 else if (statinfo->pt == PT_CN_OLD) {
823 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
826 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
827 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
830 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
831 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
832 color = COLOR_WARNING;
834 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
835 g_snprintf(status,sizeof(status),"Incorrect timestamp");
836 color = COLOR_WARNING;
838 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
839 && !(statinfo->flags & STAT_FLAG_FIRST)
840 && !(statinfo->flags & STAT_FLAG_PT_CN)
841 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
842 && !(statinfo->flags & STAT_FLAG_MARKER)) {
843 g_snprintf(status,sizeof(status),"Marker missing?");
844 color = COLOR_WARNING;
847 if (statinfo->flags & STAT_FLAG_MARKER) {
848 color = COLOR_WARNING;
850 g_snprintf(status,sizeof(status),OK_TEXT);
852 /* is this the first packet we got in this direction? */
853 if (statinfo->flags & STAT_FLAG_FIRST) {
855 pinfo->fd->num, rtpinfo->info_seq_num,
860 rtpinfo->info_marker_set,
861 timeStr, pinfo->fd->pkt_len,
866 pinfo->fd->num, rtpinfo->info_seq_num,
867 statinfo->delta*1000,
868 statinfo->jitter*1000,
871 rtpinfo->info_marker_set,
872 timeStr, pinfo->fd->pkt_len,
879 /****************************************************************************/
880 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
881 tap_rtp_stat_t *statinfo,
883 const struct _rtp_info *rtpinfo)
889 /* is this the first packet we got in this direction? */
890 if (statinfo->flags & STAT_FLAG_FIRST) {
891 if (saveinfo->fp == NULL) {
892 saveinfo->saved = FALSE;
893 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
896 saveinfo->saved = TRUE;
899 /* save the voice information */
900 /* if there was already an error, we quit */
901 if (saveinfo->saved == FALSE)
904 /* if the captured length and packet length aren't equal, we quit
905 * because there is some information missing */
906 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
907 saveinfo->saved = FALSE;
908 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
912 /* if padding bit is set, but the padding count is bigger
913 * then the whole RTP data - error with padding count */
914 if ( (rtpinfo->info_padding_set != FALSE) &&
915 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
916 saveinfo->saved = FALSE;
917 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
921 /* do we need to insert some silence? */
922 if ((rtpinfo->info_marker_set) &&
923 !(statinfo->flags & STAT_FLAG_FIRST) &&
924 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
925 /* the amount of silence should be the difference between
926 * the last timestamp and the current one minus x
927 * x should equal the amount of information in the last frame
928 * XXX not done yet */
929 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
930 rtpinfo->info_padding_count); i++) {
931 switch (statinfo->reg_pt) {
942 fwrite(&tmp, 1, 1, saveinfo->fp);
945 fflush(saveinfo->fp);
949 if (rtpinfo->info_payload_type == PT_CN
950 || rtpinfo->info_payload_type == PT_CN_OLD) {
952 /*all other payloads*/
954 if (!rtpinfo->info_all_data_present) {
955 /* Not all the data was captured. */
956 saveinfo->saved = FALSE;
957 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
961 /* we put the pointer at the beginning of the RTP
962 * payload, that is, at the beginning of the RTP data
963 * plus the offset of the payload from the beginning
965 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
966 fwrite(data, sizeof(unsigned char), (rtpinfo->info_payload_len - rtpinfo->info_padding_count), saveinfo->fp);
967 saveinfo->count+=(rtpinfo->info_payload_len - rtpinfo->info_padding_count);
969 fflush(saveinfo->fp);
970 saveinfo->saved = TRUE;
978 /****************************************************************************/
981 /****************************************************************************/
982 /* XXX just copied from gtk/rpc_stat.c */
983 void protect_thread_critical_region(void);
984 void unprotect_thread_critical_region(void);
987 /****************************************************************************/
988 /* close the dialog window and remove the tap listener */
989 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
991 /* remove tap listener */
992 protect_thread_critical_region();
993 remove_tap_listener(user_data);
994 unprotect_thread_critical_region();
996 /* close and remove temporary files */
997 if (user_data->forward.saveinfo.fp != NULL)
998 fclose(user_data->forward.saveinfo.fp);
999 if (user_data->reversed.saveinfo.fp != NULL)
1000 fclose(user_data->reversed.saveinfo.fp);
1001 /*XXX: test for error **/
1002 eth_remove(user_data->f_tempname);
1003 eth_remove(user_data->r_tempname);
1005 /* destroy save_voice_as window if open */
1006 if (user_data->dlg.save_voice_as_w != NULL)
1007 window_destroy(user_data->dlg.save_voice_as_w);
1009 /* destroy graph window if open */
1010 if (user_data->dlg.dialog_graph.window != NULL)
1011 window_destroy(user_data->dlg.dialog_graph.window);
1013 #ifdef USE_CONVERSATION_GRAPH
1014 /* destroy graph window if open */
1015 if (user_data->dlg.graph_window != NULL)
1016 window_destroy(user_data->dlg.graph_window);
1019 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exeption using GTK1*/
1020 gtk_signal_disconnect(GTK_OBJECT(user_data->dlg.notebook), user_data->dlg.notebook_signal_id);
1022 g_free(user_data->dlg.col_arrows_fwd);
1023 g_free(user_data->dlg.col_arrows_rev);
1028 /****************************************************************************/
1029 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
1030 GtkNotebookPage *page _U_,
1032 user_data_t *user_data _U_)
1034 user_data->dlg.selected_clist =
1035 (page_num==0) ? user_data->dlg.clist_fwd : user_data->dlg.clist_rev ;
1036 user_data->dlg.selected_row = 0;
1039 /****************************************************************************/
1040 static void on_clist_select_row(GtkCList *clist _U_,
1043 GdkEvent *event _U_,
1044 user_data_t *user_data _U_)
1046 user_data->dlg.selected_clist = clist;
1047 user_data->dlg.selected_row = row;
1051 #ifdef USE_CONVERSATION_GRAPH
1052 /****************************************************************************/
1053 /* when the graph window gets destroyed */
1054 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
1056 /* note that graph window has been destroyed */
1057 user_data->dlg.graph_window = NULL;
1060 /****************************************************************************/
1061 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
1064 GtkCList *clist = NULL;
1065 if (vp.fnumber != 0) {
1066 clist = GTK_CLIST(user_data->dlg.clist_fwd);
1067 row = gtk_clist_find_row_from_data(clist,
1068 GUINT_TO_POINTER(vp.fnumber));
1070 clist = GTK_CLIST(user_data->dlg.clist_rev);
1071 row = gtk_clist_find_row_from_data(clist,
1072 GUINT_TO_POINTER(vp.fnumber));
1075 gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook),
1076 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
1077 gtk_clist_select_row(clist, row, 0);
1078 gtk_clist_moveto(clist, row, 0, 0.5, 0);
1084 /****************************************************************************/
1085 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1091 if (user_data->dlg.graph_window != NULL) {
1092 /* There's already a graph window; reactivate it. */
1093 reactivate_window(user_data->dlg.graph_window);
1096 list = g_list_append(list, &(user_data->series_fwd));
1097 list = g_list_append(list, &(user_data->series_rev));
1099 user_data->series_fwd.color.pixel = 0;
1100 user_data->series_fwd.color.red = 0x80ff;
1101 user_data->series_fwd.color.green = 0xe0ff;
1102 user_data->series_fwd.color.blue = 0xffff;
1103 user_data->series_fwd.yvalue = 0.5;
1105 user_data->series_rev.color.pixel = 0;
1106 user_data->series_rev.color.red = 0x60ff;
1107 user_data->series_rev.color.green = 0xc0ff;
1108 user_data->series_rev.color.blue = 0xffff;
1109 user_data->series_rev.yvalue = -0.5;
1111 g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=%u)",
1112 get_addr_name(&(user_data->ip_src_fwd)),
1113 user_data->port_src_fwd,
1114 get_addr_name(&(user_data->ip_dst_fwd)),
1115 user_data->port_dst_fwd,
1116 user_data->ssrc_fwd);
1118 g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=%u)",
1119 get_addr_name(&(user_data->ip_src_rev)),
1120 user_data->port_src_rev,
1121 get_addr_name(&(user_data->ip_dst_rev)),
1122 user_data->port_dst_rev,
1123 user_data->ssrc_rev);
1125 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
1126 &graph_selection_callback, user_data);
1127 SIGNAL_CONNECT(user_data->dlg.graph_window, "destroy",
1128 on_destroy_graph, user_data);
1130 #endif /*USE_CONVERSATION_GRAPH*/
1132 /****************************************************************************/
1133 static void dialog_graph_set_title(user_data_t* user_data)
1136 if (!user_data->dlg.dialog_graph.window){
1139 title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
1140 get_addr_name(&(user_data->ip_src_fwd)),
1141 user_data->port_src_fwd,
1142 get_addr_name(&(user_data->ip_dst_fwd)),
1143 user_data->port_dst_fwd,
1144 get_addr_name(&(user_data->ip_src_rev)),
1145 user_data->port_src_rev,
1146 get_addr_name(&(user_data->ip_dst_rev)),
1147 user_data->port_dst_rev);
1149 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
1155 /****************************************************************************/
1156 static void dialog_graph_reset(user_data_t* user_data)
1160 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1161 for(i=0;i<MAX_GRAPHS;i++){
1162 for(j=0;j<NUM_GRAPH_ITEMS;j++){
1163 dialog_graph_graph_item_t *dggi;
1164 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
1169 user_data->dlg.dialog_graph.last_interval=0xffffffff;
1170 user_data->dlg.dialog_graph.max_interval=0;
1171 user_data->dlg.dialog_graph.num_items=0;
1173 /* create the color titles near the filter buttons */
1174 for(i=0;i<MAX_GRAPHS;i++){
1177 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=%u)",
1179 get_addr_name(&(user_data->ip_src_fwd)),
1180 user_data->port_src_fwd,
1181 get_addr_name(&(user_data->ip_dst_fwd)),
1182 user_data->port_dst_fwd,
1183 user_data->ssrc_fwd);
1186 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=%u)",
1188 get_addr_name(&(user_data->ip_src_rev)),
1189 user_data->port_src_rev,
1190 get_addr_name(&(user_data->ip_dst_rev)),
1191 user_data->port_dst_rev,
1192 user_data->ssrc_rev);
1196 dialog_graph_set_title(user_data);
1199 /****************************************************************************/
1200 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
1202 dialog_graph_graph_item_t *it;
1204 it=&dgg->items[idx];
1209 /****************************************************************************/
1210 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
1213 g_snprintf(buf, buf_len, "%ds",t/1000000);
1214 } else if(t>=1000000){
1215 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
1216 } else if(t>=10000){
1217 g_snprintf(buf, buf_len, "%dms",t/1000);
1219 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
1221 g_snprintf(buf, buf_len, "%dus",t);
1225 /****************************************************************************/
1226 static void dialog_graph_draw(user_data_t* user_data)
1229 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
1230 gint32 current_interval;
1231 guint32 left_x_border;
1232 guint32 right_x_border;
1233 guint32 top_y_border;
1234 guint32 bottom_y_border;
1235 #if GTK_MAJOR_VERSION < 2
1238 PangoLayout *layout;
1240 guint32 label_width, label_height;
1241 guint32 draw_width, draw_height;
1242 char label_string[15];
1245 guint32 num_time_intervals;
1246 guint32 max_value; /* max value of seen data */
1247 guint32 max_y; /* max value of the Y scale */
1249 #if GTK_MAJOR_VERSION <2
1250 font = user_data->dlg.dialog_graph.draw_area->style->font;
1252 if(!user_data->dlg.dialog_graph.needs_redraw){
1255 user_data->dlg.dialog_graph.needs_redraw=FALSE;
1258 * Find the length of the intervals we have data for
1259 * so we know how large arrays we need to malloc()
1261 num_time_intervals=user_data->dlg.dialog_graph.num_items;
1262 /* if there isnt anything to do, just return */
1263 if(num_time_intervals==0){
1266 num_time_intervals+=1;
1267 /* XXX move this check to _packet() */
1268 if(num_time_intervals>NUM_GRAPH_ITEMS){
1269 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
1274 * find the max value so we can autoscale the y axis
1277 for(i=0;i<MAX_GRAPHS;i++){
1280 if(!user_data->dlg.dialog_graph.graph[i].display){
1283 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
1286 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1288 /* keep track of the max value we have encountered */
1296 * Clear out old plot
1298 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1299 user_data->dlg.dialog_graph.draw_area->style->white_gc,
1302 user_data->dlg.dialog_graph.draw_area->allocation.width,
1303 user_data->dlg.dialog_graph.draw_area->allocation.height);
1307 * Calculate the y scale we should use
1309 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1310 max_y=yscale_max[MAX_YSCALE-1];
1311 for(i=MAX_YSCALE-1;i>0;i--){
1312 if(max_value<yscale_max[i]){
1313 max_y=yscale_max[i];
1317 /* the user had specified an explicit y scale to use */
1318 max_y=user_data->dlg.dialog_graph.max_y_units;
1322 * Calculate size of borders surrounding the plot
1323 * The border on the right side needs to be adjusted depending
1324 * on the width of the text labels. For simplicity we assume that the
1325 * top y scale label will be the widest one
1327 print_time_scale_string(label_string, 15, max_y);
1328 #if GTK_MAJOR_VERSION < 2
1329 label_width=gdk_string_width(font, label_string);
1330 label_height=gdk_string_height(font, label_string);
1332 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1333 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1336 right_x_border=label_width+20;
1338 bottom_y_border=label_height+20;
1342 * Calculate the size of the drawing area for the actual plot
1344 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1345 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1349 * Draw the y axis and labels
1350 * (we always draw the y scale with 11 ticks along the axis)
1352 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1353 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1355 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1356 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1362 /* first, middle and last tick are slightly longer */
1366 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1367 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1368 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1369 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1370 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1371 /* draw the labels */
1373 print_time_scale_string(label_string, 15, (max_y*i/10));
1374 #if GTK_MAJOR_VERSION < 2
1375 lwidth=gdk_string_width(font, label_string);
1376 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1378 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1379 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1380 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1383 pango_layout_set_text(layout, label_string, -1);
1384 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1385 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1386 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1387 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1388 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1393 print_time_scale_string(label_string, 15, (max_y*i/10));
1394 #if GTK_MAJOR_VERSION < 2
1395 lwidth=gdk_string_width(font, label_string);
1396 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1398 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1399 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1400 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1403 pango_layout_set_text(layout, label_string, -1);
1404 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1405 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1406 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1407 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1408 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1413 print_time_scale_string(label_string, 15, (max_y*i/10));
1414 #if GTK_MAJOR_VERSION < 2
1415 lwidth=gdk_string_width(font, label_string);
1416 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1418 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1419 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1420 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1423 pango_layout_set_text(layout, label_string, -1);
1424 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1425 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1426 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1427 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1428 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1437 * if we have not specified the last_interval via the gui,
1438 * then just pick the current end of the capture so that is scrolls
1439 * nicely when doing live captures
1441 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1442 last_interval=user_data->dlg.dialog_graph.max_interval;
1444 last_interval=user_data->dlg.dialog_graph.last_interval;
1451 /* plot the x-scale */
1452 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);
1454 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1455 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1456 first_interval*=user_data->dlg.dialog_graph.interval;
1463 while(interval_delta<((last_interval-first_interval)/10)){
1464 interval_delta*=delta_multiplier;
1465 if(delta_multiplier==5){
1472 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1475 /* if pixels_per_tick is <5, only draw every 10 ticks */
1476 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1480 if(current_interval%interval_delta){
1486 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1487 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1488 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1489 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1490 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1491 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1495 if(user_data->dlg.dialog_graph.interval>=1000){
1496 g_snprintf(label_string, 15, "%ds", current_interval/1000);
1497 } else if(user_data->dlg.dialog_graph.interval>=100){
1498 g_snprintf(label_string, 15, "%d.%1ds", current_interval/1000,(current_interval/100)%10)
1500 } else if(user_data->dlg.dialog_graph.interval>=10){
1501 g_snprintf(label_string, 15, "%d.%2ds", current_interval/1000,(current_interval/10)%100)
1504 g_snprintf(label_string, 15, "%d.%3ds", current_interval/1000,current_interval%1000);
1506 #if GTK_MAJOR_VERSION < 2
1507 lwidth=gdk_string_width(font, label_string);
1508 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1510 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1511 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1512 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20+label_height,
1515 pango_layout_set_text(layout, label_string, -1);
1516 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1517 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1518 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1519 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1520 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1533 * Draw "x" for Sequence Errors and "m" for Marks
1535 /* Draw the labels Fwd and Rev */
1536 strcpy(label_string,"<-Fwd");
1537 #if GTK_MAJOR_VERSION < 2
1538 lwidth=gdk_string_width(font, label_string);
1539 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1541 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1542 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1543 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+label_height,
1546 pango_layout_set_text(layout, label_string, -1);
1547 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1548 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1549 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1550 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1551 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1554 strcpy(label_string,"<-Rev");
1555 #if GTK_MAJOR_VERSION < 2
1556 lwidth=gdk_string_width(font, label_string);
1557 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1559 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1560 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1561 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9+label_height,
1564 pango_layout_set_text(layout, label_string, -1);
1565 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1566 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1567 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1568 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1569 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1573 /* Draw the marks */
1574 for(i=MAX_GRAPHS-1;i>=0;i--){
1576 guint32 x_pos, prev_x_pos;
1578 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1579 if (!user_data->dlg.dialog_graph.graph[i].display){
1582 /* initialize prev x/y to the low left corner of the graph */
1583 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;
1585 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1586 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;
1588 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1590 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1591 strcpy(label_string,"x");
1593 strcpy(label_string,"m");
1596 #if GTK_MAJOR_VERSION < 2
1597 lwidth=gdk_string_width(font, label_string);
1598 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1600 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1602 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2)+label_height,
1605 pango_layout_set_text(layout, label_string, -1);
1606 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1607 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1608 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1610 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1619 #if GTK_MAJOR_VERSION >= 2
1620 g_object_unref(G_OBJECT(layout));
1625 * Loop over all graphs and draw them
1627 for(i=MAX_GRAPHS-1;i>=0;i--){
1629 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1630 if (!user_data->dlg.dialog_graph.graph[i].display){
1633 /* initialize prev x/y to the low left corner of the graph */
1634 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;
1635 prev_y_pos=draw_height-1+top_y_border;
1637 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1639 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;
1640 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1644 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1647 /* dont need to draw anything if the segment
1648 * is entirely above the top of the graph
1650 if( (prev_y_pos==0) && (y_pos==0) ){
1657 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1658 x_pos, draw_height-1+top_y_border,
1668 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1669 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1670 user_data->dlg.dialog_graph.pixmap,
1673 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1676 /* update the scrollbar */
1677 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1678 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1679 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1680 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1681 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1683 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1685 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1686 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1687 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1691 /****************************************************************************/
1692 static void dialog_graph_redraw(user_data_t* user_data)
1694 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1695 dialog_graph_draw(user_data);
1698 /****************************************************************************/
1699 static gint quit(GtkWidget *widget, GdkEventExpose *event _U_)
1701 user_data_t *user_data;
1703 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1705 user_data->dlg.dialog_graph.window = NULL;
1709 /****************************************************************************/
1710 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1712 user_data_t *user_data;
1714 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1720 gdk_draw_pixmap(widget->window,
1721 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1722 user_data->dlg.dialog_graph.pixmap,
1723 event->area.x, event->area.y,
1724 event->area.x, event->area.y,
1725 event->area.width, event->area.height);
1730 /****************************************************************************/
1731 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1733 user_data_t *user_data;
1736 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1742 if(user_data->dlg.dialog_graph.pixmap){
1743 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1744 user_data->dlg.dialog_graph.pixmap=NULL;
1747 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1748 widget->allocation.width,
1749 widget->allocation.height,
1751 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1752 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1754 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1755 widget->style->white_gc,
1758 widget->allocation.width,
1759 widget->allocation.height);
1761 /* set up the colors and the GC structs for this pixmap */
1762 for(i=0;i<MAX_GRAPHS;i++){
1763 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1764 #if GTK_MAJOR_VERSION < 2
1765 colormap = gtk_widget_get_colormap (widget);
1766 if (!gdk_color_alloc (colormap, &user_data->dlg.dialog_graph.graph[i].color)){
1767 g_warning ("Couldn't allocate color");
1770 gdk_gc_set_foreground(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1772 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1776 dialog_graph_redraw(user_data);
1780 /****************************************************************************/
1781 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1783 user_data_t *user_data=(user_data_t *)data;
1786 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1787 if(user_data->dlg.dialog_graph.last_interval==mi){
1790 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1791 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1795 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1797 dialog_graph_redraw(user_data);
1801 /****************************************************************************/
1802 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1804 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1805 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "destroy", quit, user_data);
1806 OBJECT_SET_DATA(user_data->dlg.dialog_graph.draw_area, "user_data_t", user_data);
1808 WIDGET_SET_SIZE(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1810 /* signals needed to handle backing pixmap */
1811 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "expose_event", expose_event, NULL);
1812 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "configure_event", configure_event, user_data);
1814 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1815 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1817 /* create the associated scrollbar */
1818 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1819 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1820 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1821 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1822 SIGNAL_CONNECT(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", scrollbar_changed, user_data);
1825 /****************************************************************************/
1826 static void disable_graph(dialog_graph_graph_t *dgg)
1830 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1835 /****************************************************************************/
1836 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1838 /* this graph is not active, just update display and redraw */
1839 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1841 dialog_graph_redraw(dgg->ud);
1846 cf_retap_packets(&cfile, FALSE);
1847 dialog_graph_redraw(dgg->ud);
1852 /****************************************************************************/
1853 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1859 hbox=gtk_hbox_new(FALSE, 3);
1860 gtk_container_add(GTK_CONTAINER(box), hbox);
1861 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1862 gtk_widget_show(hbox);
1864 g_snprintf(str, 256, "Graph %d", num);
1865 dgg->display_button=gtk_toggle_button_new_with_label(str);
1866 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1867 gtk_widget_show(dgg->display_button);
1868 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1869 SIGNAL_CONNECT(dgg->display_button, "toggled", filter_callback, dgg);
1871 label=gtk_label_new(dgg->title);
1872 gtk_widget_show(label);
1873 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1875 #if GTK_MAJOR_VERSION < 2
1876 /* setting the color of the display button doesn't work */
1877 rc_style = gtk_rc_style_new ();
1878 rc_style->fg[GTK_STATE_NORMAL] = dgg->color;
1879 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG;
1880 rc_style->fg[GTK_STATE_ACTIVE] = dgg->color;
1881 rc_style->color_flags[GTK_STATE_ACTIVE] |= GTK_RC_FG;
1882 rc_style->fg[GTK_STATE_PRELIGHT] = dgg->color;
1883 rc_style->color_flags[GTK_STATE_PRELIGHT] |= GTK_RC_FG;
1884 rc_style->fg[GTK_STATE_SELECTED] = dgg->color;
1885 rc_style->color_flags[GTK_STATE_SELECTED] |= GTK_RC_FG;
1886 rc_style->fg[GTK_STATE_INSENSITIVE] = dgg->color;
1887 rc_style->color_flags[GTK_STATE_INSENSITIVE] |= GTK_RC_FG;
1888 gtk_widget_modify_style (label, rc_style);
1889 gtk_rc_style_unref (rc_style);
1891 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1892 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1893 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1894 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1895 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1901 /****************************************************************************/
1902 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1909 frame=gtk_frame_new("Graphs");
1910 gtk_container_add(GTK_CONTAINER(box), frame);
1911 gtk_widget_show(frame);
1913 vbox=gtk_vbox_new(FALSE, 1);
1914 gtk_container_add(GTK_CONTAINER(frame), vbox);
1915 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
1916 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1917 gtk_widget_show(vbox);
1919 for(i=0;i<MAX_GRAPHS;i++){
1920 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1923 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1924 gtk_widget_show(label);
1925 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1930 /****************************************************************************/
1931 static void yscale_select(GtkWidget *item, gpointer key)
1934 user_data_t *user_data;
1936 user_data=(user_data_t *)key;
1937 val=(int)OBJECT_GET_DATA(item, "yscale_max");
1939 user_data->dlg.dialog_graph.max_y_units=val;
1940 dialog_graph_redraw(user_data);
1943 /****************************************************************************/
1944 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1947 user_data_t *user_data;
1949 user_data=(user_data_t *)key;
1950 val=(int)OBJECT_GET_DATA(item, "pixels_per_tick");
1951 user_data->dlg.dialog_graph.pixels_per_tick=val;
1952 dialog_graph_redraw(user_data);
1955 /****************************************************************************/
1956 static void tick_interval_select(GtkWidget *item, gpointer key)
1959 user_data_t *user_data;
1961 user_data=(user_data_t *)key;
1962 val=(int)OBJECT_GET_DATA(item, "tick_interval");
1964 user_data->dlg.dialog_graph.interval=val;
1965 cf_retap_packets(&cfile, FALSE);
1966 dialog_graph_redraw(user_data);
1969 /****************************************************************************/
1970 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1973 GtkWidget *menu_item;
1976 for(i=0;i<MAX_YSCALE;i++){
1977 if(yscale_max[i]==AUTO_MAX_YSCALE){
1980 g_snprintf(str, 15, "%u ms", yscale_max[i]/1000);
1982 menu_item=gtk_menu_item_new_with_label(str);
1983 OBJECT_SET_DATA(menu_item, "yscale_max",
1984 GUINT_TO_POINTER(yscale_max[i]));
1985 SIGNAL_CONNECT(menu_item, "activate", yscale_select, user_data);
1986 gtk_widget_show(menu_item);
1987 gtk_menu_append(GTK_MENU(menu), menu_item);
1992 /****************************************************************************/
1993 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
1996 GtkWidget *menu_item;
1999 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
2000 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
2001 menu_item=gtk_menu_item_new_with_label(str);
2003 OBJECT_SET_DATA(menu_item, "pixels_per_tick",
2004 GUINT_TO_POINTER(pixels_per_tick[i]));
2005 SIGNAL_CONNECT(menu_item, "activate", pixels_per_tick_select, user_data);
2006 gtk_widget_show(menu_item);
2007 gtk_menu_append(GTK_MENU(menu), menu_item);
2009 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
2014 /****************************************************************************/
2015 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
2018 GtkWidget *menu_item;
2021 for(i=0;i<MAX_TICK_VALUES;i++){
2022 if(tick_interval_values[i]>=1000){
2023 g_snprintf(str, 15, "%u sec", tick_interval_values[i]/1000);
2024 } else if(tick_interval_values[i]>=100){
2025 g_snprintf(str, 15, "0.%1u sec", (tick_interval_values[i]/100)%10);
2026 } else if(tick_interval_values[i]>=10){
2027 g_snprintf(str, 15, "0.%02u sec", (tick_interval_values[i]/10)%10);
2029 g_snprintf(str, 15, "0.%03u sec", (tick_interval_values[i])%10);
2032 menu_item=gtk_menu_item_new_with_label(str);
2033 OBJECT_SET_DATA(menu_item, "tick_interval",
2034 GUINT_TO_POINTER(tick_interval_values[i]));
2035 SIGNAL_CONNECT(menu_item, "activate", tick_interval_select, (gpointer)user_data);
2036 gtk_widget_show(menu_item);
2037 gtk_menu_append(GTK_MENU(menu), menu_item);
2039 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
2043 /****************************************************************************/
2044 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
2048 GtkWidget *option_menu;
2051 hbox=gtk_hbox_new(FALSE, 0);
2052 gtk_container_add(GTK_CONTAINER(box), hbox);
2053 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2054 gtk_widget_show(hbox);
2056 label=gtk_label_new(name);
2057 gtk_widget_show(label);
2058 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2060 option_menu=gtk_option_menu_new();
2061 menu=gtk_menu_new();
2062 (*func)(user_data, menu);
2063 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
2064 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
2065 gtk_widget_show(option_menu);
2068 /****************************************************************************/
2069 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
2071 GtkWidget *frame_vbox;
2075 frame_vbox=gtk_vbox_new(FALSE, 0);
2076 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
2077 gtk_widget_show(frame_vbox);
2079 frame = gtk_frame_new("X Axis");
2080 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
2081 gtk_widget_show(frame);
2083 vbox=gtk_vbox_new(FALSE, 0);
2084 gtk_container_add(GTK_CONTAINER(frame), vbox);
2085 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
2086 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
2087 gtk_widget_show(vbox);
2089 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
2090 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
2092 frame = gtk_frame_new("Y Axis");
2093 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
2094 gtk_widget_show(frame);
2096 vbox=gtk_vbox_new(FALSE, 0);
2097 gtk_container_add(GTK_CONTAINER(frame), vbox);
2098 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
2099 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
2100 gtk_widget_show(vbox);
2102 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
2107 /****************************************************************************/
2108 static void dialog_graph_init_window(user_data_t* user_data)
2112 GtkWidget *bt_close;
2114 /* create the main window */
2115 user_data->dlg.dialog_graph.window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
2117 vbox=gtk_vbox_new(FALSE, 0);
2118 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
2119 gtk_widget_show(vbox);
2121 create_draw_area(user_data, vbox);
2123 hbox=gtk_hbox_new(FALSE, 3);
2124 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2125 gtk_container_border_width(GTK_CONTAINER(hbox), 3);
2126 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2127 gtk_widget_show(hbox);
2129 create_filter_area(user_data, hbox);
2130 create_ctrl_area(user_data, hbox);
2132 dialog_graph_set_title(user_data);
2134 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
2135 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2136 gtk_widget_show(hbox);
2138 bt_close = OBJECT_GET_DATA(hbox, GTK_STOCK_CLOSE);
2139 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
2141 SIGNAL_CONNECT(user_data->dlg.dialog_graph.window, "delete_event", window_delete_event_cb, NULL);
2143 gtk_widget_show(user_data->dlg.dialog_graph.window);
2144 window_present(user_data->dlg.dialog_graph.window);
2149 /****************************************************************************/
2150 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2152 if (user_data->dlg.dialog_graph.window != NULL) {
2153 /* There's already a graph window; reactivate it. */
2154 reactivate_window(user_data->dlg.dialog_graph.window);
2158 dialog_graph_init_window(user_data);
2162 /****************************************************************************/
2163 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2167 if (user_data->dlg.selected_clist!=NULL) {
2168 fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
2169 GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
2170 cf_goto_frame(&cfile, fnumber);
2175 static void draw_stat(user_data_t *user_data);
2177 /****************************************************************************/
2178 /* re-dissects all packets */
2179 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2181 GString *error_string;
2183 /* remove tap listener */
2184 protect_thread_critical_region();
2185 remove_tap_listener(user_data);
2186 unprotect_thread_critical_region();
2188 /* register tap listener */
2189 error_string = register_tap_listener("rtp", user_data, NULL,
2190 rtp_reset, rtp_packet, rtp_draw);
2191 if (error_string != NULL) {
2192 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
2193 g_string_free(error_string, TRUE);
2197 /* retap all packets */
2198 cf_retap_packets(&cfile, FALSE);
2200 /* draw statistics info */
2201 draw_stat(user_data);
2203 gtk_clist_sort(user_data->dlg.clist_fwd);
2204 gtk_clist_sort(user_data->dlg.clist_rev);
2207 /****************************************************************************/
2208 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2213 if (user_data->dlg.selected_clist==NULL)
2216 clist = user_data->dlg.selected_clist;
2217 row = user_data->dlg.selected_row + 1;
2219 while (gtk_clist_get_text(clist,row,6,&text)) {
2220 if (strcmp(text, OK_TEXT) != 0) {
2221 gtk_clist_select_row(clist, row, 0);
2222 gtk_clist_moveto(clist, row, 0, 0.5, 0);
2230 while (gtk_clist_get_text(clist,row,6,&text) && row<user_data->dlg.selected_row) {
2231 if (strcmp(text, OK_TEXT) != 0) {
2232 gtk_clist_select_row(clist, row, 0);
2233 gtk_clist_moveto(clist, row, 0, 0.5, 0);
2240 /****************************************************************************/
2241 /* when we want to save the information */
2242 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
2245 GtkWidget *rev, *forw, *both;
2246 user_data_t *user_data;
2252 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
2254 /* Perhaps the user specified a directory instead of a file.
2255 Check whether they did. */
2256 if (test_for_directory(g_dest) == EISDIR) {
2257 /* It's a directory - set the file selection box to display it. */
2258 set_last_open_dir(g_dest);
2260 file_selection_set_current_folder(fs, get_last_open_dir());
2264 rev = (GtkWidget*)OBJECT_GET_DATA(bt, "reversed_rb");
2265 forw = (GtkWidget*)OBJECT_GET_DATA(bt, "forward_rb");
2266 both = (GtkWidget*)OBJECT_GET_DATA(bt, "both_rb");
2267 user_data = (user_data_t*)OBJECT_GET_DATA(bt, "user_data");
2269 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
2270 fp = eth_fopen(g_dest, "w");
2272 open_failure_alert_box(g_dest, errno, TRUE);
2276 if (GTK_TOGGLE_BUTTON(both)->active) {
2277 fprintf(fp, "Forward\n");
2279 write_failure_alert_box(g_dest, errno);
2285 for(j = 0; j < NUM_COLS; j++) {
2287 fprintf(fp,"%s",titles[j]);
2289 fprintf(fp,",%s",titles[j]);
2294 write_failure_alert_box(g_dest, errno);
2298 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
2299 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
2300 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
2302 fprintf(fp,"%s",columnText);
2304 fprintf(fp,",%s",columnText);
2309 write_failure_alert_box(g_dest, errno);
2315 if (fclose(fp) == EOF) {
2316 write_failure_alert_box(g_dest, errno);
2321 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
2323 if (GTK_TOGGLE_BUTTON(both)->active) {
2324 fp = eth_fopen(g_dest, "a");
2326 open_failure_alert_box(g_dest, errno, TRUE);
2329 fprintf(fp, "\nReverse\n");
2331 write_failure_alert_box(g_dest, errno);
2336 fp = eth_fopen(g_dest, "w");
2338 open_failure_alert_box(g_dest, errno, TRUE);
2342 for(j = 0; j < NUM_COLS; j++) {
2344 fprintf(fp,"%s",titles[j]);
2346 fprintf(fp,",%s",titles[j]);
2351 write_failure_alert_box(g_dest, errno);
2355 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
2356 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
2357 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
2359 fprintf(fp,"%s",columnText);
2361 fprintf(fp,",%s",columnText);
2366 write_failure_alert_box(g_dest, errno);
2371 if (fclose(fp) == EOF) {
2372 write_failure_alert_box(g_dest, errno);
2377 window_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
2380 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2382 user_data->dlg.save_csv_as_w = NULL;
2385 /* when the user wants to save the csv information in a file */
2386 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
2390 GtkWidget *label_format;
2391 GtkWidget *channels_label;
2392 GSList *channels_group = NULL;
2393 GtkWidget *forward_rb;
2394 GtkWidget *reversed_rb;
2398 if (user_data->dlg.save_csv_as_w != NULL) {
2399 /* There's already a Save CSV info dialog box; reactivate it. */
2400 reactivate_window(user_data->dlg.save_csv_as_w);
2404 user_data->dlg.save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV");
2406 /* Container for each row of widgets */
2407 vertb = gtk_vbox_new(FALSE, 0);
2408 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
2409 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
2410 vertb, FALSE, FALSE, 0);
2411 gtk_widget_show (vertb);
2413 table1 = gtk_table_new (2, 4, FALSE);
2414 gtk_widget_show (table1);
2415 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2416 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2417 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2419 label_format = gtk_label_new ("Format: Comma Separated Values");
2420 gtk_widget_show (label_format);
2421 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2422 (GtkAttachOptions) (GTK_FILL),
2423 (GtkAttachOptions) (0), 0, 0);
2426 channels_label = gtk_label_new ("Channels:");
2427 gtk_widget_show (channels_label);
2428 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2429 (GtkAttachOptions) (GTK_FILL),
2430 (GtkAttachOptions) (0), 0, 0);
2431 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2433 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2434 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
2435 gtk_widget_show (forward_rb);
2436 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2437 (GtkAttachOptions) (GTK_FILL),
2438 (GtkAttachOptions) (0), 0, 0);
2440 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2441 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
2442 gtk_widget_show (reversed_rb);
2443 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2444 (GtkAttachOptions) (GTK_FILL),
2445 (GtkAttachOptions) (0), 0, 0);
2447 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2448 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
2449 gtk_widget_show (both_rb);
2450 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2451 (GtkAttachOptions) (GTK_FILL),
2452 (GtkAttachOptions) (0), 0, 0);
2454 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2456 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
2457 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
2458 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
2459 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
2460 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
2461 SIGNAL_CONNECT(ok_bt, "clicked", save_csv_as_ok_cb,
2462 user_data->dlg.save_csv_as_w);
2464 window_set_cancel_button(user_data->dlg.save_csv_as_w,
2465 GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button, window_cancel_button_cb);
2467 SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "delete_event", window_delete_event_cb, NULL);
2468 SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "destroy",
2469 save_csv_as_destroy_cb, user_data);
2471 gtk_widget_show(user_data->dlg.save_csv_as_w);
2472 window_present(user_data->dlg.save_csv_as_w);
2476 /****************************************************************************/
2477 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2479 /* Note that we no longer have a Save voice info dialog box. */
2480 user_data->dlg.save_voice_as_w = NULL;
2483 /****************************************************************************/
2484 /* here we save it into a file that user specified */
2485 /* XXX what about endians here? could go something wrong? */
2486 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2488 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
2493 guint32 f_write_silence = 0;
2494 guint32 r_write_silence = 0;
2496 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2497 gboolean stop_flag = FALSE;
2499 forw_fd = eth_open(user_data->f_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2502 rev_fd = eth_open(user_data->r_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2508 /* open file for saving */
2509 to_fd = eth_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2516 progbar = create_progress_dlg("Saving voice in a file", dest, &stop_flag);
2518 if (format == SAVE_AU_FORMAT) /* au format */
2520 /* First we write the .au header. XXX Hope this is endian independant */
2521 /* the magic word 0x2e736e64 == .snd */
2522 *pd = (unsigned char)0x2e; eth_write(to_fd, pd, 1);
2523 *pd = (unsigned char)0x73; eth_write(to_fd, pd, 1);
2524 *pd = (unsigned char)0x6e; eth_write(to_fd, pd, 1);
2525 *pd = (unsigned char)0x64; eth_write(to_fd, pd, 1);
2526 /* header offset == 24 bytes */
2527 *pd = (unsigned char)0x00; eth_write(to_fd, pd, 1);
2528 eth_write(to_fd, pd, 1);
2529 eth_write(to_fd, pd, 1);
2530 *pd = (unsigned char)0x18; eth_write(to_fd, pd, 1);
2531 /* total length, it is permited to set this to 0xffffffff */
2532 *pd = (unsigned char)0xff; eth_write(to_fd, pd, 1);
2533 eth_write(to_fd, pd, 1);
2534 eth_write(to_fd, pd, 1);
2535 eth_write(to_fd, pd, 1);
2536 /* encoding format == 8 bit ulaw */
2537 *pd = (unsigned char)0x00; eth_write(to_fd, pd, 1);
2538 eth_write(to_fd, pd, 1);
2539 eth_write(to_fd, pd, 1);
2540 *pd = (unsigned char)0x01; eth_write(to_fd, pd, 1);
2541 /* sample rate == 8000 Hz */
2542 *pd = (unsigned char)0x00; eth_write(to_fd, pd, 1);
2543 eth_write(to_fd, pd, 1);
2544 *pd = (unsigned char)0x1f; eth_write(to_fd, pd, 1);
2545 *pd = (unsigned char)0x40; eth_write(to_fd, pd, 1);
2547 *pd = (unsigned char)0x00; eth_write(to_fd, pd, 1);
2548 eth_write(to_fd, pd, 1);
2549 eth_write(to_fd, pd, 1);
2550 *pd = (unsigned char)0x01; eth_write(to_fd, pd, 1);
2554 /* only forward direction */
2555 case SAVE_FORWARD_DIRECTION_MASK: {
2556 progbar_count = user_data->forward.saveinfo.count;
2557 progbar_quantum = user_data->forward.saveinfo.count/100;
2558 while ((fread = read(forw_fd, f_pd, 1)) > 0) {
2561 if((count > progbar_nextstep) && (count <= progbar_count)) {
2562 update_progress_dlg(progbar,
2563 (gfloat) count/progbar_count, "Saving");
2564 progbar_nextstep = progbar_nextstep + progbar_quantum;
2568 if (user_data->forward.statinfo.pt == PT_PCMU){
2571 else if(user_data->forward.statinfo.pt == PT_PCMA){
2572 tmp = (gint16 )alaw2linear(*f_pd);
2573 *pd = (unsigned char)linear2ulaw(tmp);
2579 destroy_progress_dlg(progbar);
2583 fwritten = eth_write(to_fd, pd, 1);
2584 if ((fwritten < fread) || (fwritten < 0) || (fread < 0)) {
2588 destroy_progress_dlg(progbar);
2594 /* only reversed direction */
2595 case SAVE_REVERSE_DIRECTION_MASK: {
2596 progbar_count = user_data->reversed.saveinfo.count;
2597 progbar_quantum = user_data->reversed.saveinfo.count/100;
2598 while ((rread = read(rev_fd, r_pd, 1)) > 0) {
2601 if((count > progbar_nextstep) && (count <= progbar_count)) {
2602 update_progress_dlg(progbar,
2603 (gfloat) count/progbar_count, "Saving");
2604 progbar_nextstep = progbar_nextstep + progbar_quantum;
2608 if (user_data->forward.statinfo.pt == PT_PCMU){
2611 else if(user_data->forward.statinfo.pt == PT_PCMA){
2612 tmp = (gint16 )alaw2linear(*r_pd);
2613 *pd = (unsigned char)linear2ulaw(tmp);
2619 destroy_progress_dlg(progbar);
2623 rwritten = eth_write(to_fd, pd, 1);
2624 if ((rwritten < rread) || (rwritten < 0) || (rread < 0)) {
2628 destroy_progress_dlg(progbar);
2634 /* both directions */
2635 case SAVE_BOTH_DIRECTION_MASK: {
2636 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2637 (progbar_count = user_data->forward.saveinfo.count) :
2638 (progbar_count = user_data->reversed.saveinfo.count);
2639 progbar_quantum = progbar_count/100;
2640 /* since conversation in one way can start later than in the other one,
2641 * we have to write some silence information for one channel */
2642 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2643 f_write_silence = (guint32)
2644 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2646 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2647 r_write_silence = (guint32)
2648 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2653 if((count > progbar_nextstep) && (count <= progbar_count)) {
2654 update_progress_dlg(progbar,
2655 (gfloat) count/progbar_count, "Saving");
2656 progbar_nextstep = progbar_nextstep + progbar_quantum;
2659 if(f_write_silence > 0) {
2660 rread = read(rev_fd, r_pd, 1);
2661 switch (user_data->forward.statinfo.reg_pt) {
2663 *f_pd = SILENCE_PCMU;
2666 *f_pd = SILENCE_PCMA;
2672 else if(r_write_silence > 0) {
2673 fread = read(forw_fd, f_pd, 1);
2674 switch (user_data->forward.statinfo.reg_pt) {
2676 *r_pd = SILENCE_PCMU;
2679 *r_pd = SILENCE_PCMA;
2686 fread = read(forw_fd, f_pd, 1);
2687 rread = read(rev_fd, r_pd, 1);
2689 if ((rread == 0) && (fread == 0))
2691 if ((user_data->forward.statinfo.pt == PT_PCMU) && (user_data->reversed.statinfo.pt == PT_PCMU)){
2692 tmp = ulaw2linear(*r_pd);
2693 tmp += ulaw2linear(*f_pd);
2694 *pd = (unsigned char)linear2ulaw(tmp/2);
2696 else if((user_data->forward.statinfo.pt == PT_PCMA) && (user_data->reversed.statinfo.pt == PT_PCMA)){
2697 tmp = alaw2linear(*r_pd);
2698 tmp += alaw2linear(*f_pd);
2699 *pd = (unsigned char)linear2ulaw(tmp/2);
2706 destroy_progress_dlg(progbar);
2711 rwritten = eth_write(to_fd, pd, 1);
2712 if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
2716 destroy_progress_dlg(progbar);
2723 else if (format == SAVE_RAW_FORMAT) /* raw format */
2727 /* only forward direction */
2728 case SAVE_FORWARD_DIRECTION_MASK: {
2729 progbar_count = user_data->forward.saveinfo.count;
2730 progbar_quantum = user_data->forward.saveinfo.count/100;
2734 /* only reversed direction */
2735 case SAVE_REVERSE_DIRECTION_MASK: {
2736 progbar_count = user_data->reversed.saveinfo.count;
2737 progbar_quantum = user_data->reversed.saveinfo.count/100;
2745 destroy_progress_dlg(progbar);
2752 /* XXX how do you just copy the file? */
2753 while ((rread = read(fd, pd, 1)) > 0) {
2756 if((count > progbar_nextstep) && (count <= progbar_count)) {
2757 update_progress_dlg(progbar,
2758 (gfloat) count/progbar_count, "Saving");
2759 progbar_nextstep = progbar_nextstep + progbar_quantum;
2763 rwritten = eth_write(to_fd, pd, 1);
2765 if ((rwritten < rread) || (rwritten < 0) || (rread < 0)) {
2769 destroy_progress_dlg(progbar);
2775 destroy_progress_dlg(progbar);
2783 /****************************************************************************/
2784 /* the user wants to save in a file */
2785 /* XXX support for different formats is currently commented out */
2786 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
2789 /*GtkWidget *wav, *sw;*/
2790 GtkWidget *au, *raw;
2791 GtkWidget *rev, *forw, *both;
2792 user_data_t *user_data;
2793 gint channels , format;
2795 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
2797 /* Perhaps the user specified a directory instead of a file.
2798 Check whether they did. */
2799 if (test_for_directory(g_dest) == EISDIR) {
2800 /* It's a directory - set the file selection box to display it. */
2801 set_last_open_dir(g_dest);
2803 file_selection_set_current_folder(fs, get_last_open_dir());
2807 /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
2808 sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
2809 au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
2810 raw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "raw_rb");
2811 rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
2812 forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
2813 both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
2814 user_data = (user_data_t *)OBJECT_GET_DATA(ok_bt, "user_data");
2816 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2817 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2818 * disable the ok button or disable the buttons for direction if only one is not ok. The
2819 * problem is if we open the save voice dialog and then click the refresh button and maybe
2820 * the state changes, so we can't save anymore. In this case we should be able to update
2821 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2824 /* we can not save in both directions */
2825 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2826 /* there are many combinations here, we just exit when first matches */
2827 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2828 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2829 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2830 "Can't save in a file: Unsupported codec!");
2831 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2832 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2833 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2834 "Can't save in a file: Wrong length of captured packets!");
2835 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2836 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2837 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2838 "Can't save in a file: RTP data with padding!");
2839 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2840 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2841 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2842 "Can't save in a file: Not all data in all packets was captured!");
2844 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2845 "Can't save in a file: File I/O problem!");
2848 /* we can not save forward direction */
2849 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2850 (GTK_TOGGLE_BUTTON (both)->active))) {
2851 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2852 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2853 "Can't save forward direction in a file: Unsupported codec!");
2854 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2855 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2856 "Can't save forward direction in a file: Wrong length of captured packets!");
2857 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2858 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2859 "Can't save forward direction in a file: RTP data with padding!");
2860 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2861 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2862 "Can't save forward direction in a file: Not all data in all packets was captured!");
2864 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2865 "Can't save forward direction in a file: File I/O problem!");
2868 /* we can not save reversed direction */
2869 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2870 (GTK_TOGGLE_BUTTON (both)->active))) {
2871 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2872 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2873 "Can't save reversed direction in a file: Unsupported codec!");
2874 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2875 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2876 "Can't save reversed direction in a file: Wrong length of captured packets!");
2877 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2878 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2879 "Can't save reversed direction in a file: RTP data with padding!");
2880 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2881 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2882 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2883 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2884 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2885 "Can't save reversed direction in a file: No RTP data!");
2887 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2888 "Can't save reversed direction in a file: File I/O problem!");
2892 /*if (GTK_TOGGLE_BUTTON (wav)->active)
2894 else */if (GTK_TOGGLE_BUTTON (au)->active)
2895 format = SAVE_AU_FORMAT;/*
2896 else if (GTK_TOGGLE_BUTTON (sw)->active)
2898 else if (GTK_TOGGLE_BUTTON (raw)->active)
2899 format =SAVE_RAW_FORMAT;
2902 if (GTK_TOGGLE_BUTTON (rev)->active)
2903 channels = SAVE_REVERSE_DIRECTION_MASK;
2904 else if (GTK_TOGGLE_BUTTON (both)->active)
2905 channels = SAVE_BOTH_DIRECTION_MASK;
2907 channels = SAVE_FORWARD_DIRECTION_MASK;
2909 /* direction/format validity*/
2910 if (format == SAVE_AU_FORMAT)
2912 /* make sure streams are alaw/ulaw */
2913 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != PT_PCMA) && (user_data->forward.statinfo.pt != PT_PCMU)){
2914 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2915 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2918 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != PT_PCMA) && (user_data->reversed.statinfo.pt != PT_PCMU)){
2919 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2920 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2923 /* make sure pt's don't differ */
2924 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2925 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2926 "Can't save in a file: Forward and reverse direction differ in type");
2930 else if (format == SAVE_RAW_FORMAT)
2932 /* can't save raw in both directions */
2933 if (channels == SAVE_BOTH_DIRECTION_MASK){
2934 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2935 "Can't save in a file: Unable to save raw data in both directions");
2941 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2942 "Can't save in a file: Invalid save format");
2946 if(!copy_file(g_dest, channels, format, user_data)) {
2947 /* XXX - report the error type! */
2948 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2949 "An error occured while saving voice in a file!");
2953 window_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
2956 /****************************************************************************/
2957 /* when the user wants to save the voice information in a file */
2958 /* XXX support for different formats is currently commented out */
2959 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2963 GtkWidget *label_format;
2964 GtkWidget *channels_label;
2965 GSList *format_group = NULL;
2966 GSList *channels_group = NULL;
2967 GtkWidget *forward_rb;
2968 GtkWidget *reversed_rb;
2970 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2975 /* if we can't save in a file: wrong codec, cut packets or other errors */
2976 /* shold the error arise here or later when you click ok button ?
2977 * if we do it here, then we must disable the refresh button, so we don't do it here */
2979 if (user_data->dlg.save_voice_as_w != NULL) {
2980 /* There's already a Save voice info dialog box; reactivate it. */
2981 reactivate_window(user_data->dlg.save_voice_as_w);
2985 /* XXX - use file_selection from dlg_utils instead! */
2986 user_data->dlg.save_voice_as_w = gtk_file_selection_new("Ethereal: Save Payload As ...");
2988 /* Container for each row of widgets */
2989 vertb = gtk_vbox_new(FALSE, 0);
2990 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
2991 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
2992 vertb, FALSE, FALSE, 0);
2993 gtk_widget_show (vertb);
2995 table1 = gtk_table_new (2, 4, FALSE);
2996 gtk_widget_show (table1);
2997 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2998 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2999 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
3001 /*label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
3002 gtk_widget_show (label_format);
3003 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
3004 (GtkAttachOptions) (GTK_FILL),
3005 (GtkAttachOptions) (0), 0, 0);*/
3007 label_format = gtk_label_new ("Format: ");
3008 gtk_widget_show (label_format);
3009 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
3010 (GtkAttachOptions) (GTK_FILL),
3011 (GtkAttachOptions) (0), 0, 0);
3013 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5);
3015 raw_rb = gtk_radio_button_new_with_label (format_group, ".raw");
3016 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (raw_rb));
3017 gtk_widget_show (raw_rb);
3018 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
3019 (GtkAttachOptions) (GTK_FILL),
3020 (GtkAttachOptions) (0), 0, 0);
3023 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
3024 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
3025 gtk_widget_show (au_rb);
3026 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
3027 (GtkAttachOptions) (GTK_FILL),
3028 (GtkAttachOptions) (0), 0, 0);
3030 /* we support .au - ulaw*/
3031 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
3032 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
3033 gtk_widget_show (wav_rb);
3034 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
3035 (GtkAttachOptions) (GTK_FILL),
3036 (GtkAttachOptions) (0), 0, 0);
3038 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
3039 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
3040 gtk_widget_show (sw_rb);
3041 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
3042 (GtkAttachOptions) (GTK_FILL),
3043 (GtkAttachOptions) (0), 0, 0);
3044 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
3045 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
3046 gtk_widget_show (au_rb);
3047 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
3048 (GtkAttachOptions) (GTK_FILL),
3049 (GtkAttachOptions) (0), 0, 0);
3053 channels_label = gtk_label_new ("Channels:");
3054 gtk_widget_show (channels_label);
3055 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
3056 (GtkAttachOptions) (GTK_FILL),
3057 (GtkAttachOptions) (0), 0, 0);
3058 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
3060 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
3061 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
3062 gtk_widget_show (forward_rb);
3063 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
3064 (GtkAttachOptions) (GTK_FILL),
3065 (GtkAttachOptions) (0), 0, 0);
3067 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
3068 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
3069 gtk_widget_show (reversed_rb);
3070 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
3071 (GtkAttachOptions) (GTK_FILL),
3072 (GtkAttachOptions) (0), 0, 0);
3074 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
3075 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
3076 gtk_widget_show (both_rb);
3077 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
3078 (GtkAttachOptions) (GTK_FILL),
3079 (GtkAttachOptions) (0), 0, 0);
3081 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
3083 /* if one direction is nok we don't allow saving
3084 XXX this is not ok since the user can click the refresh button and cause changes
3085 but we can not update this window. So we move all the decision on the time the ok
3087 if (user_data->forward.saved == FALSE) {
3088 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
3089 gtk_widget_set_sensitive(forward_rb, FALSE);
3090 gtk_widget_set_sensitive(both_rb, FALSE);
3092 else if (user_data->reversed.saved == FALSE) {
3093 gtk_widget_set_sensitive(reversed_rb, FALSE);
3094 gtk_widget_set_sensitive(both_rb, FALSE);
3098 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
3099 /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);*/
3100 OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
3101 /*OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
3102 OBJECT_SET_DATA(ok_bt, "raw_rb", raw_rb);
3103 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
3104 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
3105 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
3106 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
3107 SIGNAL_CONNECT(ok_bt, "clicked", save_voice_as_ok_cb,
3108 user_data->dlg.save_voice_as_w);
3110 window_set_cancel_button(user_data->dlg.save_voice_as_w,
3111 GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button, window_cancel_button_cb);
3113 SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "delete_event",
3114 window_delete_event_cb, NULL);
3115 SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "destroy",
3116 save_voice_as_destroy_cb, user_data);
3118 gtk_widget_show(user_data->dlg.save_voice_as_w);
3119 window_present(user_data->dlg.save_voice_as_w);
3123 /****************************************************************************/
3124 /* when we are finished with redisection, we add the label for the statistic */
3125 static void draw_stat(user_data_t *user_data)
3127 gchar label_max[200];
3128 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
3129 - user_data->forward.statinfo.start_seq_nr + 1;
3130 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
3131 - user_data->reversed.statinfo.start_seq_nr + 1;
3132 gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
3133 gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
3134 double f_perc, r_perc;
3136 f_perc = (double)(f_lost*100)/(double)f_expected;
3141 r_perc = (double)(r_lost*100)/(double)r_expected;
3146 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
3147 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
3148 " Sequence errors = %u",
3149 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
3150 user_data->forward.statinfo.total_nr,
3151 f_expected, f_lost, f_perc, user_data->forward.statinfo.sequence);
3153 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
3155 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
3156 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
3157 " Sequence errors = %u",
3158 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
3159 user_data->reversed.statinfo.total_nr,
3160 r_expected, r_lost, r_perc, user_data->reversed.statinfo.sequence);
3162 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
3169 /****************************************************************************/
3170 /* append a line to clist */
3171 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
3172 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
3173 gchar *timeStr, guint32 pkt_len, GdkColor *color)
3180 data[0]=&field[0][0];
3181 data[1]=&field[1][0];
3182 data[2]=&field[2][0];
3183 data[3]=&field[3][0];
3184 data[4]=&field[4][0];
3185 data[5]=&field[5][0];
3186 data[6]=&field[6][0];
3187 data[7]=&field[7][0];
3188 data[8]=&field[8][0];
3190 /* save the current locale */
3191 savelocale = setlocale(LC_NUMERIC, NULL);
3192 /* switch to "C" locale to avoid problems with localized decimal separators
3193 in g_snprintf("%f") functions */
3194 setlocale(LC_NUMERIC, "C");
3195 g_snprintf(field[0], 20, "%u", number);
3196 g_snprintf(field[1], 20, "%u", seq_num);
3197 g_snprintf(field[2], 20, "%.2f", delta);
3198 g_snprintf(field[3], 20, "%.2f", jitter);
3199 g_snprintf(field[4], 20, "%.2f", bandwidth);
3200 g_snprintf(field[5], 20, "%s", marker? "SET" : "");
3201 g_snprintf(field[6], 40, "%s", status);
3202 g_snprintf(field[7], 32, "%s", timeStr);
3203 g_snprintf(field[8], 20, "%u", pkt_len);
3204 /* restore previous locale setting */
3205 setlocale(LC_NUMERIC, savelocale);
3207 added_row = gtk_clist_append(GTK_CLIST(clist), data);
3208 gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
3209 gtk_clist_set_background(GTK_CLIST(clist), added_row, color);
3213 /****************************************************************************/
3214 /* callback for sorting columns of clist */
3215 static gint rtp_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
3222 const GtkCListRow *row1 = ptr1;
3223 const GtkCListRow *row2 = ptr2;
3225 text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
3226 text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
3228 switch(clist->sort_column){
3229 /* columns representing strings */
3233 return strcmp (text1, text2);
3234 /* columns representing ints */
3241 /* columns representing floats */
3247 if (fabs(f1-f2)<0.0000005)
3253 g_assert_not_reached();
3258 /****************************************************************************/
3260 click_column_cb(GtkCList *clist, gint column, gpointer data)
3262 column_arrows *col_arrows = (column_arrows *) data;
3265 gtk_clist_freeze(clist);
3267 for (i = 0; i < NUM_COLS; i++) {
3268 gtk_widget_hide(col_arrows[i].ascend_pm);
3269 gtk_widget_hide(col_arrows[i].descend_pm);
3272 if (column == clist->sort_column) {
3273 if (clist->sort_type == GTK_SORT_ASCENDING) {
3274 clist->sort_type = GTK_SORT_DESCENDING;
3275 gtk_widget_show(col_arrows[column].descend_pm);
3277 clist->sort_type = GTK_SORT_ASCENDING;
3278 gtk_widget_show(col_arrows[column].ascend_pm);
3281 clist->sort_type = GTK_SORT_ASCENDING;
3282 gtk_widget_show(col_arrows[column].ascend_pm);
3283 gtk_clist_set_sort_column(clist, column);
3285 gtk_clist_thaw(clist);
3287 gtk_clist_sort(clist);
3291 /****************************************************************************/
3292 /* Add the packet list */
3294 GtkWidget* create_clist(user_data_t* user_data)
3296 GtkWidget* clist_fwd;
3298 /* clist for the information */
3299 clist_fwd = gtk_clist_new(NUM_COLS);
3300 gtk_widget_show(clist_fwd);
3301 SIGNAL_CONNECT(clist_fwd, "select_row", on_clist_select_row, user_data);
3303 gtk_clist_column_titles_show(GTK_CLIST(clist_fwd));
3304 gtk_clist_set_compare_func(GTK_CLIST(clist_fwd), rtp_sort_column);
3305 gtk_clist_set_sort_column(GTK_CLIST(clist_fwd), 0);
3306 gtk_clist_set_sort_type(GTK_CLIST(clist_fwd), GTK_SORT_ASCENDING);
3308 /* hide date and length column */
3309 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
3310 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 8, FALSE);
3312 /* column widths and justification */
3313 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 60);
3314 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 75);
3315 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 75);
3316 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 75);
3317 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 50);
3318 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 5, 75);
3319 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_RIGHT);
3320 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_RIGHT);
3321 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
3322 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
3323 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
3324 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
3325 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 6, GTK_JUSTIFY_CENTER);
3330 /****************************************************************************/
3331 /* Add the sort by column feature for a packet clist */
3333 column_arrows* add_sort_by_column(GtkWidget* window, GtkWidget* clist,
3334 user_data_t* user_data _U_)
3336 column_arrows *col_arrows;
3337 GdkBitmap *ascend_bm, *descend_bm;
3338 GdkPixmap *ascend_pm, *descend_pm;
3339 GtkStyle *win_style;
3340 GtkWidget *column_lb;
3343 col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
3344 win_style = gtk_widget_get_style(window);
3345 ascend_pm = gdk_pixmap_create_from_xpm_d(window->window,
3347 &win_style->bg[GTK_STATE_NORMAL],
3348 (gchar **)clist_ascend_xpm);
3349 descend_pm = gdk_pixmap_create_from_xpm_d(window->window,
3351 &win_style->bg[GTK_STATE_NORMAL],
3352 (gchar **)clist_descend_xpm);
3354 for (i=0; i<NUM_COLS; i++) {
3355 col_arrows[i].table = gtk_table_new(2, 2, FALSE);
3356 gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
3357 column_lb = gtk_label_new(titles[i]);
3358 gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
3359 gtk_widget_show(column_lb);
3361 col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
3362 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
3363 col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
3364 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
3365 /* make packet-nr be the default sort order */
3367 gtk_widget_show(col_arrows[i].ascend_pm);
3369 gtk_clist_set_column_widget(GTK_CLIST(clist), i, col_arrows[i].table);
3370 gtk_widget_show(col_arrows[i].table);
3373 SIGNAL_CONNECT(clist, "click-column", click_column_cb, col_arrows);
3378 /****************************************************************************/
3379 /* Create the dialog box with all widgets */
3380 static void create_rtp_dialog(user_data_t* user_data)
3382 GtkWidget *window = NULL;
3383 GtkWidget *clist_fwd;
3384 GtkWidget *clist_rev;
3385 GtkWidget *label_stats_fwd;
3386 GtkWidget *label_stats_rev;
3387 GtkWidget *notebook;
3389 GtkWidget *main_vb, *page, *page_r;
3391 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3392 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3393 #ifdef USE_CONVERSATION_GRAPH
3394 GtkWidget *graph_bt;
3396 GtkWidget *graph_bt;
3397 gchar label_forward[150];
3398 gchar label_reverse[150];
3400 gchar str_ip_src[16];
3401 gchar str_ip_dst[16];
3402 column_arrows *col_arrows_fwd;
3403 column_arrows *col_arrows_rev;
3405 window = window_new(GTK_WINDOW_TOPLEVEL, "Ethereal: RTP Stream Analysis");
3406 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3408 /* Container for each row of widgets */
3409 main_vb = gtk_vbox_new(FALSE, 2);
3410 gtk_container_border_width(GTK_CONTAINER(main_vb), 2);
3411 gtk_container_add(GTK_CONTAINER(window), main_vb);
3412 gtk_widget_show(main_vb);
3415 strcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)));
3416 strcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)));
3418 g_snprintf(label_forward, 149,
3419 "Analysing stream from %s port %u to %s port %u SSRC = %u",
3420 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3423 strcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)));
3424 strcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)));
3426 g_snprintf(label_reverse, 149,
3427 "Analysing stream from %s port %u to %s port %u SSRC = %u",
3428 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3430 /* Start a notebook for flipping between sets of changes */
3431 notebook = gtk_notebook_new();
3432 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3433 OBJECT_SET_DATA(window, "notebook", notebook);
3435 user_data->dlg.notebook_signal_id = SIGNAL_CONNECT(notebook, "switch_page", on_notebook_switch_page,
3438 /* page for forward connection */
3439 page = gtk_vbox_new(FALSE, 8);
3440 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3442 /* direction label */
3443 label = gtk_label_new(label_forward);
3444 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3446 /* place for some statistics */
3447 label_stats_fwd = gtk_label_new("\n");
3448 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3450 /* scrolled window */
3451 scrolled_window = scrolled_window_new(NULL, NULL);
3454 clist_fwd = create_clist(user_data);
3455 gtk_widget_show(clist_fwd);
3456 gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
3457 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3458 gtk_widget_show(scrolled_window);
3461 label = gtk_label_new(" Forward Direction ");
3462 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3464 /* same page for reversed connection */
3465 page_r = gtk_vbox_new(FALSE, 8);
3466 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3467 label = gtk_label_new(label_reverse);
3468 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3469 label_stats_rev = gtk_label_new("\n");
3470 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3472 scrolled_window_r = scrolled_window_new(NULL, NULL);
3474 clist_rev = create_clist(user_data);
3475 gtk_widget_show(clist_rev);
3476 gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
3477 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3478 gtk_widget_show(scrolled_window_r);
3480 label = gtk_label_new(" Reversed Direction ");
3481 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3483 /* page for help&about or future
3484 page_help = gtk_hbox_new(FALSE, 5);
3485 label = gtk_label_new(" Future ");
3486 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3487 frame = gtk_frame_new("");
3488 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3489 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3490 gtk_container_add(GTK_CONTAINER(frame), text);
3491 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3492 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3495 /* show all notebooks */
3496 gtk_widget_show_all(notebook);
3499 box4 = gtk_hbutton_box_new();
3500 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3501 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3502 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3503 gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0);
3504 gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
3505 gtk_widget_show(box4);
3507 voice_bt = gtk_button_new_with_label("Save payload...");
3508 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3509 gtk_widget_show(voice_bt);
3510 SIGNAL_CONNECT(voice_bt, "clicked", on_save_bt_clicked, user_data);
3512 csv_bt = gtk_button_new_with_label("Save as CSV...");
3513 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3514 gtk_widget_show(csv_bt);
3515 SIGNAL_CONNECT(csv_bt, "clicked", save_csv_as_cb, user_data);
3517 refresh_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_REFRESH);
3518 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3519 gtk_widget_show(refresh_bt);
3520 SIGNAL_CONNECT(refresh_bt, "clicked", on_refresh_bt_clicked, user_data);
3522 goto_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_JUMP_TO);
3523 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3524 gtk_widget_show(goto_bt);
3525 SIGNAL_CONNECT(goto_bt, "clicked", on_goto_bt_clicked, user_data);
3527 graph_bt = gtk_button_new_with_label("Graph");
3528 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3529 gtk_widget_show(graph_bt);
3530 SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
3533 #ifdef USE_CONVERSATION_GRAPH
3534 graph_bt = gtk_button_new_with_label("Graph");
3535 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3536 gtk_widget_show(graph_bt);
3537 SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
3540 next_bt = gtk_button_new_with_label("Next non-Ok");
3541 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3542 gtk_widget_show(next_bt);
3543 SIGNAL_CONNECT(next_bt, "clicked", on_next_bt_clicked, user_data);
3545 close_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
3546 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3547 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3548 gtk_widget_show(close_bt);
3549 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3551 SIGNAL_CONNECT(window, "delete_event", window_delete_event_cb, NULL);
3552 SIGNAL_CONNECT(window, "destroy", on_destroy, user_data);
3554 gtk_widget_show(window);
3555 window_present(window);
3557 /* sort by column feature */
3558 col_arrows_fwd = add_sort_by_column(window, clist_fwd, user_data);
3559 col_arrows_rev = add_sort_by_column(window, clist_rev, user_data);
3561 /* some widget references need to be saved for outside use */
3562 user_data->dlg.window = window;
3563 user_data->dlg.clist_fwd = GTK_CLIST(clist_fwd);
3564 user_data->dlg.clist_rev = GTK_CLIST(clist_rev);
3565 user_data->dlg.label_stats_fwd = label_stats_fwd;
3566 user_data->dlg.label_stats_rev = label_stats_rev;
3567 user_data->dlg.notebook = notebook;
3568 user_data->dlg.selected_clist = GTK_CLIST(clist_fwd);
3569 user_data->dlg.selected_row = 0;
3570 user_data->dlg.col_arrows_fwd = col_arrows_fwd;
3571 user_data->dlg.col_arrows_rev = col_arrows_rev;
3575 /****************************************************************************/
3576 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3577 const gchar* proto_field, guint32* p_result)
3580 proto_node *proto_sibling_node;
3581 header_field_info *hfssrc;
3584 finfo = PITEM_FINFO(ptree_node);
3586 if (hfinformation==(finfo->hfinfo)) {
3587 hfssrc = proto_registrar_get_byname(proto_field);
3590 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3591 ptree_node=ptree_node->next) {
3592 finfo=PITEM_FINFO(ptree_node);
3593 if (hfssrc==finfo->hfinfo) {
3594 if (hfinformation->type==FT_IPv4) {
3595 ipv4 = fvalue_get(&finfo->value);
3596 *p_result = ipv4_get_net_order_addr(ipv4);
3599 *p_result = fvalue_get_integer(&finfo->value);
3606 proto_sibling_node = ptree_node->next;
3608 if (proto_sibling_node) {
3609 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3615 /****************************************************************************/
3616 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3617 const gchar* proto_name,
3618 const gchar* proto_field,
3621 proto_node *ptree_node;
3622 header_field_info *hfinformation;
3624 hfinformation = proto_registrar_get_byname(proto_name);
3625 if (hfinformation == NULL)
3628 ptree_node = ((proto_node *)protocol_tree)->first_child;
3632 return process_node(ptree_node, hfinformation, proto_field, p_result);
3636 /* XXX just copied from gtk/rpc_stat.c */
3637 void protect_thread_critical_region(void);
3638 void unprotect_thread_critical_region(void);
3640 /****************************************************************************/
3642 address *ip_src_fwd,
3643 guint16 port_src_fwd,
3644 address *ip_dst_fwd,
3645 guint16 port_dst_fwd,
3647 address *ip_src_rev,
3648 guint16 port_src_rev,
3649 address *ip_dst_rev,
3650 guint16 port_dst_rev,
3654 user_data_t *user_data;
3657 static color_t col[MAX_GRAPHS] = {
3658 {0, 0x0000, 0x0000, 0x0000},
3659 {0, 0xffff, 0x0000, 0x0000},
3660 {0, 0x0000, 0xffff, 0x0000},
3661 {0, 0x0000, 0x0000, 0xffff}
3665 user_data = g_malloc(sizeof(user_data_t));
3667 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3668 user_data->port_src_fwd = port_src_fwd;
3669 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3670 user_data->port_dst_fwd = port_dst_fwd;
3671 user_data->ssrc_fwd = ssrc_fwd;
3672 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3673 user_data->port_src_rev = port_src_rev;
3674 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3675 user_data->port_dst_rev = port_dst_rev;
3676 user_data->ssrc_rev = ssrc_rev;
3679 /* file names for storing sound data */
3680 /*XXX: check for errors*/
3681 fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
3684 fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
3687 user_data->forward.saveinfo.fp = NULL;
3688 user_data->reversed.saveinfo.fp = NULL;
3689 user_data->dlg.save_voice_as_w = NULL;
3690 user_data->dlg.save_csv_as_w = NULL;
3691 user_data->dlg.dialog_graph.window = NULL;
3693 #ifdef USE_CONVERSATION_GRAPH
3694 user_data->dlg.graph_window = NULL;
3695 user_data->series_fwd.value_pairs = NULL;
3696 user_data->series_rev.value_pairs = NULL;
3699 /* init dialog_graph */
3700 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3701 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3702 user_data->dlg.dialog_graph.draw_area=NULL;
3703 user_data->dlg.dialog_graph.pixmap=NULL;
3704 user_data->dlg.dialog_graph.scrollbar=NULL;
3705 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3706 user_data->dlg.dialog_graph.pixmap_width=500;
3707 user_data->dlg.dialog_graph.pixmap_height=200;
3708 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3709 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3710 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3711 user_data->dlg.dialog_graph.max_interval=0;
3712 user_data->dlg.dialog_graph.num_items=0;
3713 user_data->dlg.dialog_graph.start_time = -1;
3715 for(i=0;i<MAX_GRAPHS;i++){
3716 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3717 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3718 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3719 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3720 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3721 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3722 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3723 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3726 /* create the dialog box */
3727 create_rtp_dialog(user_data);
3729 /* proceed as if the Refresh button would have been pressed */
3730 on_refresh_bt_clicked(NULL, user_data);
3733 /****************************************************************************/
3734 /* entry point from main menu */
3735 static void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3738 guint16 port_src_fwd;
3740 guint16 port_dst_fwd;
3741 guint32 ssrc_fwd = 0;
3743 guint16 port_src_rev;
3745 guint16 port_dst_rev;
3746 guint32 ssrc_rev = 0;
3747 unsigned int version_fwd;
3749 gchar filter_text[256];
3752 epan_dissect_t *edt;
3755 gboolean frame_matched;
3757 GList *strinfo_list;
3758 GList *filtered_list = NULL;
3759 rtp_stream_info_t *strinfo;
3762 /* Try to compile the filter. */
3763 strcpy(filter_text,"rtp && rtp.version && rtp.ssrc && (ip || ipv6)");
3764 if (!dfilter_compile(filter_text, &sfcode)) {
3765 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, dfilter_error_msg);
3768 /* we load the current file into cf variable */
3770 fdata = cf->current_frame;
3772 /* we are on the selected frame now */
3774 return; /* if we exit here it's an error */
3776 /* dissect the current frame */
3777 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3778 cf->pd, fdata->cap_len, &err, &err_info)) {
3779 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3780 cf_read_error_message(err, err_info), cf->filename);
3783 edt = epan_dissect_new(TRUE, FALSE);
3784 epan_dissect_prime_dfilter(edt, sfcode);
3785 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3786 frame_matched = dfilter_apply_edt(sfcode, edt);
3788 /* if it is not an rtp frame, show the rtpstream dialog */
3789 frame_matched = dfilter_apply_edt(sfcode, edt);
3790 if (frame_matched != 1) {
3791 epan_dissect_free(edt);
3792 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3793 "You didn't choose a RTP packet!");
3797 /* ok, it is a RTP frame, so let's get the ip and port values */
3798 COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
3799 COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
3800 port_src_fwd = edt->pi.srcport;
3801 port_dst_fwd = edt->pi.destport;
3803 /* assume the inverse ip/port combination for the reverse direction */
3804 COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
3805 COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
3806 port_src_rev = edt->pi.destport;
3807 port_dst_rev = edt->pi.srcport;
3809 /* check if it is RTP Version 2 */
3810 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3811 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3812 "RTP Version != 2 isn't supported!");
3816 /* now we need the SSRC value of the current frame */
3817 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3818 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3819 "SSRC value couldn't be found!");
3823 /* Scan for rtpstream */
3825 /* search for reversed direction in the global rtp streams list */
3827 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3828 while (strinfo_list)
3830 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3831 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3832 && strinfo->src_port==port_src_fwd
3833 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3834 && strinfo->dest_port==port_dst_fwd)
3836 filtered_list = g_list_prepend(filtered_list, strinfo);
3839 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3840 && strinfo->src_port==port_src_rev
3841 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3842 && strinfo->dest_port==port_dst_rev)
3845 filtered_list = g_list_append(filtered_list, strinfo);
3847 ssrc_rev = strinfo->ssrc;
3850 strinfo_list = g_list_next(strinfo_list);
3853 /* if more than one reverse streams found, we let the user choose the right one */
3855 rtpstream_dlg_show(filtered_list);
3874 /****************************************************************************/
3876 rtp_analysis_init(const char *dummy _U_)
3878 rtp_analysis_cb(NULL, NULL);
3881 /****************************************************************************/
3883 register_tap_listener_rtp_analysis(void)
3885 register_stat_cmd_arg("rtp", rtp_analysis_init);
3887 register_stat_menu_item("RTP/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3888 rtp_analysis_cb, NULL, NULL, NULL);