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>
63 #include "gtkglobals.h"
65 #include "dlg_utils.h"
67 #include "alert_box.h"
68 #include "simple_dialog.h"
71 #include "progress_dlg.h"
72 #include "compat_macros.h"
75 #include "image/clist_ascend.xpm"
76 #include "image/clist_descend.xpm"
91 #include <io.h> /* open/close on win32 */
94 /* Win32 needs the O_BINARY flag for open() */
99 /****************************************************************************/
101 typedef struct column_arrows {
103 GtkWidget *ascend_pm;
104 GtkWidget *descend_pm;
108 #define NUM_GRAPH_ITEMS 100000
109 #define MAX_YSCALE 16
110 #define AUTO_MAX_YSCALE 0
112 #define GRAPH_FWD_JITTER 0
113 #define GRAPH_FWD_DIFF 1
114 #define GRAPH_REV_JITTER 2
115 #define GRAPH_REV_DIFF 3
116 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
118 #define MAX_PIXELS_PER_TICK 4
119 #define DEFAULT_PIXELS_PER_TICK 1
120 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
121 static char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
123 #define MAX_TICK_VALUES 5
124 #define DEFAULT_TICK_VALUE 1
125 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
126 typedef struct _dialog_graph_graph_item_t {
129 } dialog_graph_graph_item_t;
131 typedef struct _dialog_graph_graph_t {
132 struct _user_data_t *ud;
133 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
136 GtkWidget *display_button;
141 } dialog_graph_graph_t;
144 typedef struct _dialog_graph_t {
145 gboolean needs_redraw;
146 gint32 interval; /* measurement interval in ms */
147 guint32 last_interval;
148 guint32 max_interval; /* XXX max_interval and num_items are redundant */
150 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
152 GtkWidget *draw_area;
154 GtkAdjustment *scrollbar_adjustment;
155 GtkWidget *scrollbar;
163 typedef struct _dialog_data_t {
167 GtkWidget *label_stats_fwd;
168 GtkWidget *label_stats_rev;
169 column_arrows *col_arrows_fwd;
170 column_arrows *col_arrows_rev;
172 GtkCList *selected_clist;
173 GtkWidget *save_voice_as_w;
174 GtkWidget *save_csv_as_w;
175 gint notebook_signal_id;
177 dialog_graph_t dialog_graph;
178 #ifdef USE_CONVERSATION_GRAPH
179 GtkWidget *graph_window;
183 #define OK_TEXT "[ Ok ]"
184 #define PT_UNDEFINED -1
187 typedef struct _key_value {
193 /* RTP sampling clock rates for fixed payload types as defined in
194 http://www.iana.org/assignments/rtp-parameters */
195 static const key_value clock_map[] = {
201 {PT_DVI4_8000, 8000},
202 {PT_DVI4_16000, 16000},
206 {PT_L16_STEREO, 44100},
207 {PT_L16_MONO, 44100},
213 {PT_DVI4_11025, 11025},
214 {PT_DVI4_22050, 22050},
226 #define NUM_CLOCK_VALUES (sizeof clock_map / sizeof clock_map[0])
229 get_clock_rate(guint32 key)
233 for (i = 0; i < NUM_CLOCK_VALUES; i++) {
234 if (clock_map[i].key == key)
235 return clock_map[i].value;
241 /* type of error when saving voice in a file didn't succeed */
244 TAP_RTP_WRONG_LENGTH,
245 TAP_RTP_PADDING_ERROR,
247 TAP_RTP_FILE_OPEN_ERROR,
251 #if GTK_MAJOR_VERSION < 2
252 GtkRcStyle *rc_style;
253 GdkColormap *colormap;
256 /****************************************************************************/
257 /* structure that holds the information about the forward and reversed direction */
258 typedef struct _bw_history_item {
263 typedef struct _tap_rtp_stat_t {
264 gboolean first_packet; /* do not use in code that is called after rtp_packet_analyse */
265 /* use (flags & STAT_FLAG_FIRST) instead */
266 /* all of the following fields will be initialized after
267 rtp_packet_analyse has been called */
268 guint32 flags; /* see STAT_FLAG-defines below */
271 guint32 delta_timestamp;
273 bw_history_item bw_history[BUFF_BW];
274 guint16 bw_start_index;
284 guint16 start_seq_nr;
294 /* status flags for the flags parameter in tap_rtp_stat_t */
295 #define STAT_FLAG_FIRST 0x01
296 #define STAT_FLAG_MARKER 0x02
297 #define STAT_FLAG_WRONG_SEQ 0x04
298 #define STAT_FLAG_PT_CHANGE 0x08
299 #define STAT_FLAG_PT_CN 0x10
300 #define STAT_FLAG_FOLLOW_PT_CN 0x20
301 #define STAT_FLAG_REG_PT_CHANGE 0x40
302 #define STAT_FLAG_WRONG_TIMESTAMP 0x80
304 typedef struct _tap_rtp_save_info_t {
307 error_type_t error_type;
309 } tap_rtp_save_info_t;
312 /* structure that holds the information about the forward and reversed direction */
313 struct _info_direction {
314 tap_rtp_stat_t statinfo;
315 tap_rtp_save_info_t saveinfo;
318 #define TMPNAMSIZE 100
320 /* structure that holds general information about the connection
321 * and structures for both directions */
322 typedef struct _user_data_t {
323 /* tap associated data*/
325 guint16 port_src_fwd;
327 guint16 port_dst_fwd;
330 guint16 port_src_rev;
332 guint16 port_dst_rev;
335 struct _info_direction forward;
336 struct _info_direction reversed;
338 char f_tempname[TMPNAMSIZE];
339 char r_tempname[TMPNAMSIZE];
341 /* dialog associated data */
344 #ifdef USE_CONVERSATION_GRAPH
345 time_series_t series_fwd;
346 time_series_t series_rev;
352 static gchar *titles[9] = {
365 typedef const guint8 * ip_addr_p;
367 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_);
368 /****************************************************************************/
369 static void enable_graph(dialog_graph_graph_t *dgg)
376 static void dialog_graph_reset(user_data_t* user_data);
380 /****************************************************************************/
383 /****************************************************************************/
384 /* when there is a [re]reading of packet's */
386 rtp_reset(void *user_data_arg)
388 user_data_t *user_data = user_data_arg;
389 user_data->forward.statinfo.first_packet = TRUE;
390 user_data->reversed.statinfo.first_packet = TRUE;
391 user_data->forward.statinfo.max_delta = 0;
392 user_data->reversed.statinfo.max_delta = 0;
393 user_data->forward.statinfo.delta = 0;
394 user_data->reversed.statinfo.delta = 0;
395 user_data->forward.statinfo.diff = 0;
396 user_data->reversed.statinfo.diff = 0;
397 user_data->forward.statinfo.jitter = 0;
398 user_data->reversed.statinfo.jitter = 0;
399 user_data->forward.statinfo.bandwidth = 0;
400 user_data->reversed.statinfo.bandwidth = 0;
401 user_data->forward.statinfo.total_bytes = 0;
402 user_data->reversed.statinfo.total_bytes = 0;
403 user_data->forward.statinfo.bw_start_index = 0;
404 user_data->reversed.statinfo.bw_start_index = 0;
405 user_data->forward.statinfo.bw_index = 0;
406 user_data->reversed.statinfo.bw_index = 0;
407 user_data->forward.statinfo.timestamp = 0;
408 user_data->reversed.statinfo.timestamp = 0;
409 user_data->forward.statinfo.max_nr = 0;
410 user_data->reversed.statinfo.max_nr = 0;
411 user_data->forward.statinfo.total_nr = 0;
412 user_data->reversed.statinfo.total_nr = 0;
413 user_data->forward.statinfo.sequence = 0;
414 user_data->reversed.statinfo.sequence = 0;
415 user_data->forward.statinfo.start_seq_nr = 0;
416 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
417 user_data->forward.statinfo.stop_seq_nr = 0;
418 user_data->reversed.statinfo.stop_seq_nr = 0;
419 user_data->forward.statinfo.cycles = 0;
420 user_data->reversed.statinfo.cycles = 0;
421 user_data->forward.statinfo.under = FALSE;
422 user_data->reversed.statinfo.under = FALSE;
423 user_data->forward.statinfo.start_time = 0;
424 user_data->reversed.statinfo.start_time = 0;
425 user_data->forward.statinfo.time = 0;
426 user_data->reversed.statinfo.time = 0;
427 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
428 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
430 user_data->forward.saveinfo.count = 0;
431 user_data->reversed.saveinfo.count = 0;
432 user_data->forward.saveinfo.saved = FALSE;
433 user_data->reversed.saveinfo.saved = FALSE;
435 /* clear the dialog box clists */
436 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
437 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
439 /* reset graph info */
440 dialog_graph_reset(user_data);
442 #ifdef USE_CONVERSATION_GRAPH
443 if (user_data->dlg.graph_window != NULL)
444 window_destroy(user_data->dlg.graph_window);
446 g_array_free(user_data->series_fwd.value_pairs, TRUE);
447 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
449 g_array_free(user_data->series_rev.value_pairs, TRUE);
450 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
453 /* XXX check for error at fclose? */
454 if (user_data->forward.saveinfo.fp != NULL)
455 fclose(user_data->forward.saveinfo.fp);
456 if (user_data->reversed.saveinfo.fp != NULL)
457 fclose(user_data->reversed.saveinfo.fp);
458 user_data->forward.saveinfo.fp = fopen(user_data->f_tempname, "wb");
459 if (user_data->forward.saveinfo.fp == NULL)
460 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
461 user_data->reversed.saveinfo.fp = fopen(user_data->r_tempname, "wb");
462 if (user_data->reversed.saveinfo.fp == NULL)
463 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
467 /****************************************************************************/
468 static int rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
470 dialog_graph_graph_item_t *it;
474 /* we sometimes get called when dgg is disabled.
475 this is a bug since the tap listener should be removed first */
480 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
483 * Find which interval this is supposed to to in and store the
484 * interval index as idx
486 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
487 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
489 rtp_time = ((double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000) - dgg->ud->dlg.dialog_graph.start_time;
493 idx = (guint32)(rtp_time*1000)/dgg->ud->dlg.dialog_graph.interval;
495 /* some sanity checks */
496 if((idx<0)||(idx>=NUM_GRAPH_ITEMS)){
500 /* update num_items */
501 if((guint32)idx > dgg->ud->dlg.dialog_graph.num_items){
502 dgg->ud->dlg.dialog_graph.num_items=idx;
503 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
507 * Find the appropriate dialog_graph_graph_item_t structure
512 * Use the max value to highlight RTP problems
514 if (value > it->value) {
517 it->flags = it->flags | statinfo->flags;
522 /****************************************************************************/
523 /* here we can redraw the output */
525 static void rtp_draw(void *prs _U_)
530 /* forward declarations */
531 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
532 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
533 gchar *timeStr, guint32 pkt_len, GdkColor *color);
535 static int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
536 packet_info *pinfo, struct _rtp_info *rtpinfo);
537 static int rtp_packet_add_info(GtkCList *clist,
538 tap_rtp_stat_t *statinfo, packet_info *pinfo, struct _rtp_info *rtpinfo);
539 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
540 tap_rtp_stat_t *statinfo,
541 packet_info *pinfo, struct _rtp_info *rtpinfo);
544 /****************************************************************************/
545 /* whenever a RTP packet is seen by the tap listener */
546 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, void *rtpinfo_arg)
548 user_data_t *user_data = user_data_arg;
549 struct _rtp_info *rtpinfo = rtpinfo_arg;
550 #ifdef USE_CONVERSATION_GRAPH
553 /* we ignore packets that are not displayed */
554 if (pinfo->fd->flags.passed_dfilter == 0)
556 /* also ignore RTP Version != 2 */
557 else if (rtpinfo->info_version !=2)
559 /* is it the forward direction? */
560 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src) {
561 #ifdef USE_CONVERSATION_GRAPH
562 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
563 vp.fnumber = pinfo->fd->num;
564 g_array_append_val(user_data->series_fwd.value_pairs, vp);
566 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
567 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));
568 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));
569 rtp_packet_add_info(user_data->dlg.clist_fwd,
570 &(user_data->forward.statinfo), pinfo, rtpinfo);
571 rtp_packet_save_payload(&(user_data->forward.saveinfo),
572 &(user_data->forward.statinfo), pinfo, rtpinfo);
574 /* is it the reversed direction? */
575 else if (user_data->ssrc_rev == rtpinfo->info_sync_src) {
576 #ifdef USE_CONVERSATION_GRAPH
577 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
578 vp.fnumber = pinfo->fd->num;
579 g_array_append_val(user_data->series_rev.value_pairs, vp);
581 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
582 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));
583 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));
584 rtp_packet_add_info(user_data->dlg.clist_rev,
585 &(user_data->reversed.statinfo), pinfo, rtpinfo);
586 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
587 &(user_data->reversed.statinfo), pinfo, rtpinfo);
594 /****************************************************************************/
595 static int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
596 packet_info *pinfo, struct _rtp_info *rtpinfo)
599 double current_jitter;
604 /* check payload type */
605 if (rtpinfo->info_payload_type == PT_CN
606 || rtpinfo->info_payload_type == PT_CN_OLD)
607 statinfo->flags |= STAT_FLAG_PT_CN;
608 if (statinfo->pt == PT_CN
609 || statinfo->pt == PT_CN_OLD)
610 statinfo->flags |= STAT_FLAG_FOLLOW_PT_CN;
611 if (rtpinfo->info_payload_type != statinfo->pt)
612 statinfo->flags |= STAT_FLAG_PT_CHANGE;
613 statinfo->pt = rtpinfo->info_payload_type;
615 * XXX - should "get_clock_rate()" return 0 for unknown
616 * payload types, presumably meaning that we should
617 * just ignore this packet?
619 clock_rate = get_clock_rate(statinfo->pt);
621 /* store the current time and calculate the current jitter */
622 current_time = (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
623 current_diff = fabs (current_time - (statinfo->time) - ((double)(rtpinfo->info_timestamp)-(double)(statinfo->timestamp))/clock_rate);
624 current_jitter = statinfo->jitter + ( current_diff - statinfo->jitter)/16;
625 statinfo->delta = current_time-(statinfo->time);
626 statinfo->jitter = current_jitter;
627 statinfo->diff = current_diff;
629 /* calculate the BW in Kbps adding the IP+UDP header to the RTP -> 20bytes(IP)+8bytes(UDP) = 28bytes */
630 statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + 28;
631 statinfo->bw_history[statinfo->bw_index].time = current_time;
632 /* check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
633 while ((statinfo->bw_history[statinfo->bw_start_index].time+1)<current_time){
634 statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;
635 statinfo->bw_start_index++;
636 if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
638 statinfo->total_bytes += rtpinfo->info_data_len + 28;
639 statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
640 statinfo->bw_index++;
641 if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
644 /* is this the first packet we got in this direction? */
645 if (statinfo->first_packet) {
646 statinfo->start_seq_nr = rtpinfo->info_seq_num;
647 statinfo->start_time = current_time;
649 statinfo->jitter = 0;
651 statinfo->flags |= STAT_FLAG_FIRST;
652 statinfo->first_packet = FALSE;
654 /* is it a packet with the mark bit set? */
655 if (rtpinfo->info_marker_set) {
656 if (rtpinfo->info_timestamp > statinfo->timestamp){
657 statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
658 statinfo->flags |= STAT_FLAG_MARKER;
661 statinfo->flags |= STAT_FLAG_WRONG_TIMESTAMP;
664 /* is it a regular packet? */
665 if (!(statinfo->flags & STAT_FLAG_FIRST)
666 && !(statinfo->flags & STAT_FLAG_MARKER)
667 && !(statinfo->flags & STAT_FLAG_PT_CN)
668 && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
669 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
670 /* include it in maximum delta calculation */
671 if (statinfo->delta > statinfo->max_delta) {
672 statinfo->max_delta = statinfo->delta;
673 statinfo->max_nr = pinfo->fd->num;
676 /* regular payload change? (CN ignored) */
677 if (!(statinfo->flags & STAT_FLAG_FIRST)
678 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
679 if ((statinfo->pt != statinfo->reg_pt)
680 && (statinfo->reg_pt != PT_UNDEFINED)) {
681 statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
685 /* set regular payload*/
686 if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
687 statinfo->reg_pt = statinfo->pt;
691 /* When calculating expected rtp packets the seq number can wrap around
692 * so we have to count the number of cycles
693 * Variable cycles counts the wraps around in forwarding connection and
694 * under is flag that indicates where we are
696 * XXX how to determine number of cycles with all possible lost, late
697 * and duplicated packets without any doubt? It seems to me, that
698 * because of all possible combination of late, duplicated or lost
699 * packets, this can only be more or less good approximation
701 * There are some combinations (rare but theoretically possible),
702 * where below code won't work correctly - statistic may be wrong then.
705 /* so if the current sequence number is less than the start one
706 * we assume, that there is another cycle running */
707 if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
709 statinfo->under = TRUE;
711 /* what if the start seq nr was 0? Then the above condition will never
712 * be true, so we add another condition. XXX The problem would arise
713 * if one of the packets with seq nr 0 or 65535 would be lost or late */
714 else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
715 (statinfo->under == FALSE)){
717 statinfo->under = TRUE;
719 /* the whole round is over, so reset the flag */
720 else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
721 statinfo->under = FALSE;
724 /* Since it is difficult to count lost, duplicate or late packets separately,
725 * we would like to know at least how many times the sequence number was not ok */
727 /* if the current seq number equals the last one or if we are here for
728 * the first time, then it is ok, we just store the current one as the last one */
729 if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
730 statinfo->seq_num = rtpinfo->info_seq_num;
731 /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
732 else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
733 statinfo->seq_num = rtpinfo->info_seq_num;
735 else if (statinfo->seq_num+1 < rtpinfo->info_seq_num) {
736 statinfo->seq_num = rtpinfo->info_seq_num;
737 statinfo->sequence++;
738 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
740 /* late or duplicated */
741 else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
742 statinfo->sequence++;
743 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
745 statinfo->time = current_time;
746 statinfo->timestamp = rtpinfo->info_timestamp;
747 statinfo->stop_seq_nr = rtpinfo->info_seq_num;
748 statinfo->total_nr++;
754 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
755 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
756 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
757 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
759 /****************************************************************************/
760 /* adds statistics information from the packet to the clist */
761 static int rtp_packet_add_info(GtkCList *clist,
762 tap_rtp_stat_t *statinfo, packet_info *pinfo, struct _rtp_info *rtpinfo)
769 GdkColor color = COLOR_DEFAULT;
770 then = pinfo->fd->abs_secs;
771 msecs = (guint16)(pinfo->fd->abs_usecs/1000);
772 tm_tmp = localtime(&then);
773 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
776 tm_tmp->tm_year + 1900,
782 if (statinfo->pt == PT_CN) {
783 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
786 else if (statinfo->pt == PT_CN_OLD) {
787 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
790 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
791 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
794 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
795 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
796 color = COLOR_WARNING;
798 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
799 g_snprintf(status,sizeof(status),"Incorrect timestamp");
800 color = COLOR_WARNING;
802 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
803 && !(statinfo->flags & STAT_FLAG_FIRST)
804 && !(statinfo->flags & STAT_FLAG_PT_CN)
805 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
806 && !(statinfo->flags & STAT_FLAG_MARKER)) {
807 g_snprintf(status,sizeof(status),"Marker missing?");
808 color = COLOR_WARNING;
811 if (statinfo->flags & STAT_FLAG_MARKER) {
812 color = COLOR_WARNING;
814 g_snprintf(status,sizeof(status),OK_TEXT);
816 /* is this the first packet we got in this direction? */
817 if (statinfo->flags & STAT_FLAG_FIRST) {
819 pinfo->fd->num, rtpinfo->info_seq_num,
824 rtpinfo->info_marker_set,
825 timeStr, pinfo->fd->pkt_len,
830 pinfo->fd->num, rtpinfo->info_seq_num,
831 statinfo->delta*1000,
832 statinfo->jitter*1000,
835 rtpinfo->info_marker_set,
836 timeStr, pinfo->fd->pkt_len,
843 /****************************************************************************/
844 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
845 tap_rtp_stat_t *statinfo,
846 packet_info *pinfo, struct _rtp_info *rtpinfo)
852 /* is this the first packet we got in this direction? */
853 if (statinfo->flags & STAT_FLAG_FIRST) {
854 if (saveinfo->fp == NULL) {
855 saveinfo->saved = FALSE;
856 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
859 saveinfo->saved = TRUE;
862 /* save the voice information */
863 /* if there was already an error, we quit */
864 if (saveinfo->saved == FALSE)
867 /* if the captured length and packet length aren't equal, we quit
868 * because there is some information missing */
869 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
870 saveinfo->saved = FALSE;
871 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
875 /* if padding bit is set, but the padding count is bigger
876 * then the whole RTP data - error with padding count */
877 if ( (rtpinfo->info_padding_set != FALSE) &&
878 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
879 saveinfo->saved = FALSE;
880 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
884 /* do we need to insert some silence? */
885 if ((rtpinfo->info_marker_set) &&
886 !(statinfo->flags & STAT_FLAG_FIRST) &&
887 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
888 /* the amount of silence should be the difference between
889 * the last timestamp and the current one minus x
890 * x should equal the amount of information in the last frame
891 * XXX not done yet */
892 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
893 rtpinfo->info_padding_count); i++) {
894 tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
895 fwrite(&tmp, 2, 1, saveinfo->fp);
898 fflush(saveinfo->fp);
902 if (rtpinfo->info_payload_type == PT_PCMU) {
903 if (!rtpinfo->info_all_data_present) {
904 /* Not all the data was captured. */
905 saveinfo->saved = FALSE;
906 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
910 /* we put the pointer at the beginning of the RTP
911 * payload, that is, at the beginning of the RTP data
912 * plus the offset of the payload from the beginning
914 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
915 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
916 tmp = (gint16 )ulaw2linear((unsigned char)*data);
917 fwrite(&tmp, 2, 1, saveinfo->fp);
920 fflush(saveinfo->fp);
921 saveinfo->saved = TRUE;
926 else if (rtpinfo->info_payload_type == PT_PCMA) {
927 if (!rtpinfo->info_all_data_present) {
928 /* Not all the data was captured. */
929 saveinfo->saved = FALSE;
930 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
934 /* we put the pointer at the beginning of the RTP
935 * payload, that is, at the beginning of the RTP data
936 * plus the offset of the payload from the beginning
938 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
939 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
940 tmp = (gint16 )alaw2linear((unsigned char)*data);
941 fwrite(&tmp, 2, 1, saveinfo->fp);
944 fflush(saveinfo->fp);
945 saveinfo->saved = TRUE;
948 /* comfort noise? - do nothing */
949 else if (rtpinfo->info_payload_type == PT_CN
950 || rtpinfo->info_payload_type == PT_CN_OLD) {
952 /* unsupported codec or XXX other error */
954 saveinfo->saved = FALSE;
955 saveinfo->error_type = TAP_RTP_WRONG_CODEC;
963 /****************************************************************************/
966 /****************************************************************************/
967 /* XXX just copied from gtk/rpc_stat.c */
968 void protect_thread_critical_region(void);
969 void unprotect_thread_critical_region(void);
972 /****************************************************************************/
973 /* close the dialog window and remove the tap listener */
974 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
976 /* remove tap listener */
977 protect_thread_critical_region();
978 remove_tap_listener(user_data);
979 unprotect_thread_critical_region();
981 /* close and remove temporary files */
982 if (user_data->forward.saveinfo.fp != NULL)
983 fclose(user_data->forward.saveinfo.fp);
984 if (user_data->reversed.saveinfo.fp != NULL)
985 fclose(user_data->reversed.saveinfo.fp);
986 /*XXX: test for error **/
987 remove(user_data->f_tempname);
988 remove(user_data->r_tempname);
990 /* destroy save_voice_as window if open */
991 if (user_data->dlg.save_voice_as_w != NULL)
992 window_destroy(user_data->dlg.save_voice_as_w);
994 /* destroy graph window if open */
995 if (user_data->dlg.dialog_graph.window != NULL)
996 window_destroy(user_data->dlg.dialog_graph.window);
998 #ifdef USE_CONVERSATION_GRAPH
999 /* destroy graph window if open */
1000 if (user_data->dlg.graph_window != NULL)
1001 window_destroy(user_data->dlg.graph_window);
1004 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exeption using GTK1*/
1005 gtk_signal_disconnect(GTK_OBJECT(user_data->dlg.notebook), user_data->dlg.notebook_signal_id);
1007 g_free(user_data->dlg.col_arrows_fwd);
1008 g_free(user_data->dlg.col_arrows_rev);
1013 /****************************************************************************/
1014 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
1015 GtkNotebookPage *page _U_,
1017 user_data_t *user_data _U_)
1019 user_data->dlg.selected_clist =
1020 (page_num==0) ? user_data->dlg.clist_fwd : user_data->dlg.clist_rev ;
1021 user_data->dlg.selected_row = 0;
1024 /****************************************************************************/
1025 static void on_clist_select_row(GtkCList *clist _U_,
1028 GdkEvent *event _U_,
1029 user_data_t *user_data _U_)
1031 user_data->dlg.selected_clist = clist;
1032 user_data->dlg.selected_row = row;
1036 #ifdef USE_CONVERSATION_GRAPH
1037 /****************************************************************************/
1038 /* when the graph window gets destroyed */
1039 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
1041 /* note that graph window has been destroyed */
1042 user_data->dlg.graph_window = NULL;
1045 /****************************************************************************/
1046 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
1049 GtkCList *clist = NULL;
1050 if (vp.fnumber != 0) {
1051 clist = GTK_CLIST(user_data->dlg.clist_fwd);
1052 row = gtk_clist_find_row_from_data(clist,
1053 GUINT_TO_POINTER(vp.fnumber));
1055 clist = GTK_CLIST(user_data->dlg.clist_rev);
1056 row = gtk_clist_find_row_from_data(clist,
1057 GUINT_TO_POINTER(vp.fnumber));
1060 gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook),
1061 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
1062 gtk_clist_select_row(clist, row, 0);
1063 gtk_clist_moveto(clist, row, 0, 0.5, 0);
1069 /****************************************************************************/
1070 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1076 if (user_data->dlg.graph_window != NULL) {
1077 /* There's already a graph window; reactivate it. */
1078 reactivate_window(user_data->dlg.graph_window);
1081 list = g_list_append(list, &(user_data->series_fwd));
1082 list = g_list_append(list, &(user_data->series_rev));
1084 user_data->series_fwd.color.pixel = 0;
1085 user_data->series_fwd.color.red = 0x80ff;
1086 user_data->series_fwd.color.green = 0xe0ff;
1087 user_data->series_fwd.color.blue = 0xffff;
1088 user_data->series_fwd.yvalue = 0.5;
1090 user_data->series_rev.color.pixel = 0;
1091 user_data->series_rev.color.red = 0x60ff;
1092 user_data->series_rev.color.green = 0xc0ff;
1093 user_data->series_rev.color.blue = 0xffff;
1094 user_data->series_rev.yvalue = -0.5;
1096 g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=%u)",
1097 address_to_str_w_none(&(user_data->ip_src_fwd)),
1098 user_data->port_src_fwd,
1099 address_to_str_w_none(&(user_data->ip_dst_fwd)),
1100 user_data->port_dst_fwd,
1101 user_data->ssrc_fwd);
1103 g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=%u)",
1104 address_to_str_w_none(&(user_data->ip_src_rev)),
1105 user_data->port_src_rev,
1106 address_to_str_w_none(&(user_data->ip_dst_rev)),
1107 user_data->port_dst_rev,
1108 user_data->ssrc_rev);
1110 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
1111 &graph_selection_callback, user_data);
1112 SIGNAL_CONNECT(user_data->dlg.graph_window, "destroy",
1113 on_destroy_graph, user_data);
1115 #endif /*USE_CONVERSATION_GRAPH*/
1117 /****************************************************************************/
1118 static void dialog_graph_set_title(user_data_t* user_data)
1121 if (!user_data->dlg.dialog_graph.window){
1124 title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
1125 address_to_str_w_none(&(user_data->ip_src_fwd)),
1126 user_data->port_src_fwd,
1127 address_to_str_w_none(&(user_data->ip_dst_fwd)),
1128 user_data->port_dst_fwd,
1129 address_to_str_w_none(&(user_data->ip_src_rev)),
1130 user_data->port_src_rev,
1131 address_to_str_w_none(&(user_data->ip_dst_rev)),
1132 user_data->port_dst_rev);
1134 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
1140 /****************************************************************************/
1141 static void dialog_graph_reset(user_data_t* user_data)
1145 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1146 for(i=0;i<MAX_GRAPHS;i++){
1147 for(j=0;j<NUM_GRAPH_ITEMS;j++){
1148 dialog_graph_graph_item_t *dggi;
1149 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
1154 user_data->dlg.dialog_graph.last_interval=0xffffffff;
1155 user_data->dlg.dialog_graph.max_interval=0;
1156 user_data->dlg.dialog_graph.num_items=0;
1158 /* create the color titles near the filter buttons */
1159 for(i=0;i<MAX_GRAPHS;i++){
1162 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=%u)",
1164 address_to_str_w_none(&(user_data->ip_src_fwd)),
1165 user_data->port_src_fwd,
1166 address_to_str_w_none(&(user_data->ip_dst_fwd)),
1167 user_data->port_dst_fwd,
1168 user_data->ssrc_fwd);
1171 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=%u)",
1173 address_to_str_w_none(&(user_data->ip_src_rev)),
1174 user_data->port_src_rev,
1175 address_to_str_w_none(&(user_data->ip_dst_rev)),
1176 user_data->port_dst_rev,
1177 user_data->ssrc_rev);
1181 dialog_graph_set_title(user_data);
1184 /****************************************************************************/
1185 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
1187 dialog_graph_graph_item_t *it;
1189 it=&dgg->items[idx];
1194 /****************************************************************************/
1195 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
1198 g_snprintf(buf, buf_len, "%ds",t/1000000);
1199 } else if(t>=1000000){
1200 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
1201 } else if(t>=10000){
1202 g_snprintf(buf, buf_len, "%dms",t/1000);
1204 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
1206 g_snprintf(buf, buf_len, "%dus",t);
1210 /****************************************************************************/
1211 static void dialog_graph_draw(user_data_t* user_data)
1214 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
1215 gint32 current_interval;
1216 guint32 left_x_border;
1217 guint32 right_x_border;
1218 guint32 top_y_border;
1219 guint32 bottom_y_border;
1220 #if GTK_MAJOR_VERSION < 2
1223 PangoLayout *layout;
1225 guint32 label_width, label_height;
1226 guint32 draw_width, draw_height;
1227 char label_string[15];
1230 guint32 num_time_intervals;
1231 guint32 max_value; /* max value of seen data */
1232 guint32 max_y; /* max value of the Y scale */
1234 #if GTK_MAJOR_VERSION <2
1235 font = user_data->dlg.dialog_graph.draw_area->style->font;
1237 if(!user_data->dlg.dialog_graph.needs_redraw){
1240 user_data->dlg.dialog_graph.needs_redraw=FALSE;
1243 * Find the length of the intervals we have data for
1244 * so we know how large arrays we need to malloc()
1246 num_time_intervals=user_data->dlg.dialog_graph.num_items;
1247 /* if there isnt anything to do, just return */
1248 if(num_time_intervals==0){
1251 num_time_intervals+=1;
1252 /* XXX move this check to _packet() */
1253 if(num_time_intervals>NUM_GRAPH_ITEMS){
1254 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
1259 * find the max value so we can autoscale the y axis
1262 for(i=0;i<MAX_GRAPHS;i++){
1265 if(!user_data->dlg.dialog_graph.graph[i].display){
1268 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
1271 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1273 /* keep track of the max value we have encountered */
1281 * Clear out old plot
1283 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1284 user_data->dlg.dialog_graph.draw_area->style->white_gc,
1287 user_data->dlg.dialog_graph.draw_area->allocation.width,
1288 user_data->dlg.dialog_graph.draw_area->allocation.height);
1292 * Calculate the y scale we should use
1294 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1295 max_y=yscale_max[MAX_YSCALE-1];
1296 for(i=MAX_YSCALE-1;i>0;i--){
1297 if(max_value<yscale_max[i]){
1298 max_y=yscale_max[i];
1302 /* the user had specified an explicit y scale to use */
1303 max_y=user_data->dlg.dialog_graph.max_y_units;
1307 * Calculate size of borders surrounding the plot
1308 * The border on the right side needs to be adjusted depending
1309 * on the width of the text labels. For simplicity we assume that the
1310 * top y scale label will be the widest one
1312 print_time_scale_string(label_string, 15, max_y);
1313 #if GTK_MAJOR_VERSION < 2
1314 label_width=gdk_string_width(font, label_string);
1315 label_height=gdk_string_height(font, label_string);
1317 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1318 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1321 right_x_border=label_width+20;
1323 bottom_y_border=label_height+20;
1327 * Calculate the size of the drawing area for the actual plot
1329 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1330 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1334 * Draw the y axis and labels
1335 * (we always draw the y scale with 11 ticks along the axis)
1337 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1338 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1340 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1341 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1347 /* first, middle and last tick are slightly longer */
1351 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1352 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1353 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1354 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1355 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1356 /* draw the labels */
1358 print_time_scale_string(label_string, 15, (max_y*i/10));
1359 #if GTK_MAJOR_VERSION < 2
1360 lwidth=gdk_string_width(font, label_string);
1361 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1363 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1364 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1365 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1368 pango_layout_set_text(layout, label_string, -1);
1369 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1370 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1371 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1372 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1373 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1378 print_time_scale_string(label_string, 15, (max_y*i/10));
1379 #if GTK_MAJOR_VERSION < 2
1380 lwidth=gdk_string_width(font, label_string);
1381 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1383 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1384 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1385 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1388 pango_layout_set_text(layout, label_string, -1);
1389 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1390 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1391 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1392 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1393 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1398 print_time_scale_string(label_string, 15, (max_y*i/10));
1399 #if GTK_MAJOR_VERSION < 2
1400 lwidth=gdk_string_width(font, label_string);
1401 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1403 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1404 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1405 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1408 pango_layout_set_text(layout, label_string, -1);
1409 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1410 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1411 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1412 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1413 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1422 * if we have not specified the last_interval via the gui,
1423 * then just pick the current end of the capture so that is scrolls
1424 * nicely when doing live captures
1426 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1427 last_interval=user_data->dlg.dialog_graph.max_interval;
1429 last_interval=user_data->dlg.dialog_graph.last_interval;
1436 /* plot the x-scale */
1437 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);
1439 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1440 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1441 first_interval*=user_data->dlg.dialog_graph.interval;
1448 while(interval_delta<((last_interval-first_interval)/10)){
1449 interval_delta*=delta_multiplier;
1450 if(delta_multiplier==5){
1457 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1460 /* if pixels_per_tick is <5, only draw every 10 ticks */
1461 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1465 if(current_interval%interval_delta){
1471 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1472 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1473 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1474 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1475 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1476 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1480 if(user_data->dlg.dialog_graph.interval>=1000){
1481 g_snprintf(label_string, 15, "%ds", current_interval/1000);
1482 } else if(user_data->dlg.dialog_graph.interval>=100){
1483 g_snprintf(label_string, 15, "%d.%1ds", current_interval/1000,(current_interval/100)%10)
1485 } else if(user_data->dlg.dialog_graph.interval>=10){
1486 g_snprintf(label_string, 15, "%d.%2ds", current_interval/1000,(current_interval/10)%100)
1489 g_snprintf(label_string, 15, "%d.%3ds", current_interval/1000,current_interval%1000);
1491 #if GTK_MAJOR_VERSION < 2
1492 lwidth=gdk_string_width(font, label_string);
1493 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1495 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1496 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1497 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20+label_height,
1500 pango_layout_set_text(layout, label_string, -1);
1501 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1502 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1503 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1504 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1505 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1518 * Draw "x" for Sequence Errors and "m" for Marks
1520 /* Draw the labels Fwd and Rev */
1521 strcpy(label_string,"<-Fwd");
1522 #if GTK_MAJOR_VERSION < 2
1523 lwidth=gdk_string_width(font, label_string);
1524 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1526 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1527 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1528 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+label_height,
1531 pango_layout_set_text(layout, label_string, -1);
1532 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1533 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1534 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1535 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1536 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1539 strcpy(label_string,"<-Rev");
1540 #if GTK_MAJOR_VERSION < 2
1541 lwidth=gdk_string_width(font, label_string);
1542 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1544 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1545 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1546 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9+label_height,
1549 pango_layout_set_text(layout, label_string, -1);
1550 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1551 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1552 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1553 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1554 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1558 /* Draw the marks */
1559 for(i=MAX_GRAPHS-1;i>=0;i--){
1561 guint32 x_pos, prev_x_pos;
1563 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1564 if (!user_data->dlg.dialog_graph.graph[i].display){
1567 /* initialize prev x/y to the low left corner of the graph */
1568 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;
1570 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1571 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;
1573 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1575 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1576 strcpy(label_string,"x");
1578 strcpy(label_string,"m");
1581 #if GTK_MAJOR_VERSION < 2
1582 lwidth=gdk_string_width(font, label_string);
1583 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1585 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1587 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2)+label_height,
1590 pango_layout_set_text(layout, label_string, -1);
1591 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1592 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1593 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1595 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1604 #if GTK_MAJOR_VERSION >= 2
1605 g_object_unref(G_OBJECT(layout));
1610 * Loop over all graphs and draw them
1612 for(i=MAX_GRAPHS-1;i>=0;i--){
1614 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1615 if (!user_data->dlg.dialog_graph.graph[i].display){
1618 /* initialize prev x/y to the low left corner of the graph */
1619 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;
1620 prev_y_pos=draw_height-1+top_y_border;
1622 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1624 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;
1625 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1629 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1632 /* dont need to draw anything if the segment
1633 * is entirely above the top of the graph
1635 if( (prev_y_pos==0) && (y_pos==0) ){
1642 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1643 x_pos, draw_height-1+top_y_border,
1653 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1654 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1655 user_data->dlg.dialog_graph.pixmap,
1658 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1661 /* update the scrollbar */
1662 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1663 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1664 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1665 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1666 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1668 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1670 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1671 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1672 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1676 /****************************************************************************/
1677 static void dialog_graph_redraw(user_data_t* user_data)
1679 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1680 dialog_graph_draw(user_data);
1683 /****************************************************************************/
1684 static gint quit(GtkWidget *widget, GdkEventExpose *event _U_)
1686 user_data_t *user_data;
1688 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1690 user_data->dlg.dialog_graph.window = NULL;
1694 /****************************************************************************/
1695 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1697 user_data_t *user_data;
1699 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1705 gdk_draw_pixmap(widget->window,
1706 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1707 user_data->dlg.dialog_graph.pixmap,
1708 event->area.x, event->area.y,
1709 event->area.x, event->area.y,
1710 event->area.width, event->area.height);
1715 /****************************************************************************/
1716 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1718 user_data_t *user_data;
1721 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1727 if(user_data->dlg.dialog_graph.pixmap){
1728 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1729 user_data->dlg.dialog_graph.pixmap=NULL;
1732 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1733 widget->allocation.width,
1734 widget->allocation.height,
1736 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1737 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1739 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1740 widget->style->white_gc,
1743 widget->allocation.width,
1744 widget->allocation.height);
1746 /* set up the colors and the GC structs for this pixmap */
1747 for(i=0;i<MAX_GRAPHS;i++){
1748 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1749 #if GTK_MAJOR_VERSION < 2
1750 colormap = gtk_widget_get_colormap (widget);
1751 if (!gdk_color_alloc (colormap, &user_data->dlg.dialog_graph.graph[i].color)){
1752 g_warning ("Couldn't allocate color");
1755 gdk_gc_set_foreground(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1757 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1761 dialog_graph_redraw(user_data);
1765 /****************************************************************************/
1766 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1768 user_data_t *user_data=(user_data_t *)data;
1771 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1772 if(user_data->dlg.dialog_graph.last_interval==mi){
1775 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1776 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1780 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1782 dialog_graph_redraw(user_data);
1786 /****************************************************************************/
1787 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1789 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1790 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "destroy", quit, user_data);
1791 OBJECT_SET_DATA(user_data->dlg.dialog_graph.draw_area, "user_data_t", user_data);
1793 WIDGET_SET_SIZE(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1795 /* signals needed to handle backing pixmap */
1796 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "expose_event", expose_event, NULL);
1797 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "configure_event", configure_event, user_data);
1799 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1800 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1802 /* create the associated scrollbar */
1803 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1804 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1805 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1806 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1807 SIGNAL_CONNECT(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", scrollbar_changed, user_data);
1810 /****************************************************************************/
1811 static void disable_graph(dialog_graph_graph_t *dgg)
1815 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1820 /****************************************************************************/
1821 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1823 /* this graph is not active, just update display and redraw */
1824 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1826 dialog_graph_redraw(dgg->ud);
1831 retap_packets(&cfile);
1832 dialog_graph_redraw(dgg->ud);
1837 /****************************************************************************/
1838 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1844 hbox=gtk_hbox_new(FALSE, 3);
1845 gtk_container_add(GTK_CONTAINER(box), hbox);
1846 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1847 gtk_widget_show(hbox);
1849 g_snprintf(str, 256, "Graph %d", num);
1850 dgg->display_button=gtk_toggle_button_new_with_label(str);
1851 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1852 gtk_widget_show(dgg->display_button);
1853 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1854 SIGNAL_CONNECT(dgg->display_button, "toggled", filter_callback, dgg);
1856 label=gtk_label_new(dgg->title);
1857 gtk_widget_show(label);
1858 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1860 #if GTK_MAJOR_VERSION < 2
1861 /* setting the color of the display button doesn't work */
1862 rc_style = gtk_rc_style_new ();
1863 rc_style->fg[GTK_STATE_NORMAL] = dgg->color;
1864 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG;
1865 rc_style->fg[GTK_STATE_ACTIVE] = dgg->color;
1866 rc_style->color_flags[GTK_STATE_ACTIVE] |= GTK_RC_FG;
1867 rc_style->fg[GTK_STATE_PRELIGHT] = dgg->color;
1868 rc_style->color_flags[GTK_STATE_PRELIGHT] |= GTK_RC_FG;
1869 rc_style->fg[GTK_STATE_SELECTED] = dgg->color;
1870 rc_style->color_flags[GTK_STATE_SELECTED] |= GTK_RC_FG;
1871 rc_style->fg[GTK_STATE_INSENSITIVE] = dgg->color;
1872 rc_style->color_flags[GTK_STATE_INSENSITIVE] |= GTK_RC_FG;
1873 gtk_widget_modify_style (label, rc_style);
1874 gtk_rc_style_unref (rc_style);
1876 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1877 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1878 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1879 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1880 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1886 /****************************************************************************/
1887 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1894 frame=gtk_frame_new("Graphs");
1895 gtk_container_add(GTK_CONTAINER(box), frame);
1896 gtk_widget_show(frame);
1898 vbox=gtk_vbox_new(FALSE, 1);
1899 gtk_container_add(GTK_CONTAINER(frame), vbox);
1900 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
1901 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1902 gtk_widget_show(vbox);
1904 for(i=0;i<MAX_GRAPHS;i++){
1905 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1908 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1909 gtk_widget_show(label);
1910 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1915 /****************************************************************************/
1916 static void yscale_select(GtkWidget *item, gpointer key)
1919 user_data_t *user_data;
1921 user_data=(user_data_t *)key;
1922 val=(int)OBJECT_GET_DATA(item, "yscale_max");
1924 user_data->dlg.dialog_graph.max_y_units=val;
1925 dialog_graph_redraw(user_data);
1928 /****************************************************************************/
1929 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1932 user_data_t *user_data;
1934 user_data=(user_data_t *)key;
1935 val=(int)OBJECT_GET_DATA(item, "pixels_per_tick");
1936 user_data->dlg.dialog_graph.pixels_per_tick=val;
1937 dialog_graph_redraw(user_data);
1940 /****************************************************************************/
1941 static void tick_interval_select(GtkWidget *item, gpointer key)
1944 user_data_t *user_data;
1946 user_data=(user_data_t *)key;
1947 val=(int)OBJECT_GET_DATA(item, "tick_interval");
1949 user_data->dlg.dialog_graph.interval=val;
1950 retap_packets(&cfile);
1951 dialog_graph_redraw(user_data);
1954 /****************************************************************************/
1955 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1958 GtkWidget *menu_item;
1961 for(i=0;i<MAX_YSCALE;i++){
1962 if(yscale_max[i]==AUTO_MAX_YSCALE){
1965 g_snprintf(str, 15, "%d ms", yscale_max[i]/1000);
1967 menu_item=gtk_menu_item_new_with_label(str);
1968 OBJECT_SET_DATA(menu_item, "yscale_max", yscale_max[i]);
1969 SIGNAL_CONNECT(menu_item, "activate", yscale_select, user_data);
1970 gtk_widget_show(menu_item);
1971 gtk_menu_append(GTK_MENU(menu), menu_item);
1976 /****************************************************************************/
1977 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
1980 GtkWidget *menu_item;
1983 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1984 g_snprintf(str, 5, "%d", pixels_per_tick[i]);
1985 menu_item=gtk_menu_item_new_with_label(str);
1987 OBJECT_SET_DATA(menu_item, "pixels_per_tick",
1988 pixels_per_tick[i]);
1989 SIGNAL_CONNECT(menu_item, "activate", pixels_per_tick_select, user_data);
1990 gtk_widget_show(menu_item);
1991 gtk_menu_append(GTK_MENU(menu), menu_item);
1993 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1998 /****************************************************************************/
1999 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
2002 GtkWidget *menu_item;
2005 for(i=0;i<MAX_TICK_VALUES;i++){
2006 if(tick_interval_values[i]>=1000){
2007 g_snprintf(str, 15, "%d sec", tick_interval_values[i]/1000);
2008 } else if(tick_interval_values[i]>=100){
2009 g_snprintf(str, 15, "0.%1d sec", (tick_interval_values[i]/100)%10);
2010 } else if(tick_interval_values[i]>=10){
2011 g_snprintf(str, 15, "0.%02d sec", (tick_interval_values[i]/10)%10);
2013 g_snprintf(str, 15, "0.%03d sec", (tick_interval_values[i])%10);
2016 menu_item=gtk_menu_item_new_with_label(str);
2017 OBJECT_SET_DATA(menu_item, "tick_interval",
2018 tick_interval_values[i]);
2019 SIGNAL_CONNECT(menu_item, "activate", tick_interval_select, (gpointer)user_data);
2020 gtk_widget_show(menu_item);
2021 gtk_menu_append(GTK_MENU(menu), menu_item);
2023 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
2027 /****************************************************************************/
2028 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
2032 GtkWidget *option_menu;
2035 hbox=gtk_hbox_new(FALSE, 0);
2036 gtk_container_add(GTK_CONTAINER(box), hbox);
2037 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2038 gtk_widget_show(hbox);
2040 label=gtk_label_new(name);
2041 gtk_widget_show(label);
2042 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2044 option_menu=gtk_option_menu_new();
2045 menu=gtk_menu_new();
2046 (*func)(user_data, menu);
2047 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
2048 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
2049 gtk_widget_show(option_menu);
2052 /****************************************************************************/
2053 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
2055 GtkWidget *frame_vbox;
2059 frame_vbox=gtk_vbox_new(FALSE, 0);
2060 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
2061 gtk_widget_show(frame_vbox);
2063 frame = gtk_frame_new("X Axis");
2064 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
2065 gtk_widget_show(frame);
2067 vbox=gtk_vbox_new(FALSE, 0);
2068 gtk_container_add(GTK_CONTAINER(frame), vbox);
2069 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
2070 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
2071 gtk_widget_show(vbox);
2073 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
2074 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
2076 frame = gtk_frame_new("Y Axis");
2077 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
2078 gtk_widget_show(frame);
2080 vbox=gtk_vbox_new(FALSE, 0);
2081 gtk_container_add(GTK_CONTAINER(frame), vbox);
2082 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
2083 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
2084 gtk_widget_show(vbox);
2086 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
2091 /****************************************************************************/
2092 static void dialog_graph_init_window(user_data_t* user_data)
2096 GtkWidget *bt_close;
2098 /* create the main window */
2099 user_data->dlg.dialog_graph.window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
2101 vbox=gtk_vbox_new(FALSE, 0);
2102 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
2103 gtk_widget_show(vbox);
2105 create_draw_area(user_data, vbox);
2107 hbox=gtk_hbox_new(FALSE, 3);
2108 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2109 gtk_container_border_width(GTK_CONTAINER(hbox), 3);
2110 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2111 gtk_widget_show(hbox);
2113 create_filter_area(user_data, hbox);
2114 create_ctrl_area(user_data, hbox);
2116 dialog_graph_set_title(user_data);
2118 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
2119 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2120 gtk_widget_show(hbox);
2122 bt_close = OBJECT_GET_DATA(hbox, GTK_STOCK_CLOSE);
2123 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
2125 SIGNAL_CONNECT(user_data->dlg.dialog_graph.window, "delete_event", window_delete_event_cb, NULL);
2127 gtk_widget_show(user_data->dlg.dialog_graph.window);
2128 window_present(user_data->dlg.dialog_graph.window);
2133 /****************************************************************************/
2134 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2136 if (user_data->dlg.dialog_graph.window != NULL) {
2137 /* There's already a graph window; reactivate it. */
2138 reactivate_window(user_data->dlg.dialog_graph.window);
2142 dialog_graph_init_window(user_data);
2146 /****************************************************************************/
2147 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2151 if (user_data->dlg.selected_clist!=NULL) {
2152 fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
2153 GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
2154 goto_frame(&cfile, fnumber);
2159 static void draw_stat(user_data_t *user_data);
2161 /****************************************************************************/
2162 /* re-dissects all packets */
2163 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2165 gchar filter_text[256];
2167 GString *error_string;
2168 gchar ip_version[3];
2170 /* try to compile the filter. */
2171 strcpy(filter_text,"rtp && ip");
2172 if (!dfilter_compile(filter_text, &sfcode)) {
2173 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, dfilter_error_msg);
2177 if (user_data->ip_src_fwd.type==AT_IPv6){
2178 strcpy(ip_version,"v6");
2181 strcpy(ip_version,"");
2185 if (user_data->ip_src_fwd.type!=AT_NONE){
2186 if (user_data->ip_src_rev.type!=AT_NONE){
2187 g_snprintf(filter_text,sizeof(filter_text),
2188 "rtp && (( ip%s.src==%s && udp.srcport==%u && ip%s.dst==%s && udp.dstport==%u ) || ( ip%s.src==%s && udp.srcport==%u && ip%s.dst==%s && udp.dstport==%u ))",
2190 address_to_str_w_none(&(user_data->ip_src_fwd)),
2191 user_data->port_src_fwd,
2193 address_to_str_w_none(&(user_data->ip_dst_fwd)),
2194 user_data->port_dst_fwd,
2196 address_to_str_w_none(&(user_data->ip_src_rev)),
2197 user_data->port_src_rev,
2199 address_to_str_w_none(&(user_data->ip_dst_rev)),
2200 user_data->port_dst_rev
2204 g_snprintf(filter_text,sizeof(filter_text),
2205 "rtp && (ip%s.src==%s && udp.srcport==%u && ip%s.dst==%s && udp.dstport==%u )",
2207 address_to_str_w_none(&(user_data->ip_src_fwd)),
2208 user_data->port_src_fwd,
2210 address_to_str_w_none(&(user_data->ip_dst_fwd)),
2211 user_data->port_dst_fwd
2216 g_snprintf(filter_text,sizeof(filter_text),
2217 "rtp && ( ip%s.src==%s && udp.srcport==%u && ip%s.dst==%s && udp.dstport==%u )",
2219 address_to_str_w_none(&(user_data->ip_src_rev)),
2220 user_data->port_src_rev,
2222 address_to_str_w_none(&(user_data->ip_dst_rev)),
2223 user_data->port_dst_rev
2227 /* remove tap listener */
2228 protect_thread_critical_region();
2229 remove_tap_listener(user_data);
2230 unprotect_thread_critical_region();
2232 /* register tap listener */
2233 error_string = register_tap_listener("rtp", user_data, filter_text,
2234 rtp_reset, rtp_packet, rtp_draw);
2235 if (error_string != NULL) {
2236 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
2237 g_string_free(error_string, TRUE);
2241 /* retap all packets */
2242 retap_packets(&cfile);
2244 /* draw statistics info */
2245 draw_stat(user_data);
2247 gtk_clist_sort(user_data->dlg.clist_fwd);
2248 gtk_clist_sort(user_data->dlg.clist_rev);
2251 /****************************************************************************/
2252 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2257 if (user_data->dlg.selected_clist==NULL)
2260 clist = user_data->dlg.selected_clist;
2261 row = user_data->dlg.selected_row + 1;
2263 while (gtk_clist_get_text(clist,row,6,&text)) {
2264 if (strcmp(text, OK_TEXT) != 0) {
2265 gtk_clist_select_row(clist, row, 0);
2266 gtk_clist_moveto(clist, row, 0, 0.5, 0);
2274 while (gtk_clist_get_text(clist,row,6,&text) && row<user_data->dlg.selected_row) {
2275 if (strcmp(text, OK_TEXT) != 0) {
2276 gtk_clist_select_row(clist, row, 0);
2277 gtk_clist_moveto(clist, row, 0, 0.5, 0);
2284 /****************************************************************************/
2285 /* when we want to save the information */
2286 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
2289 GtkWidget *rev, *forw, *both;
2290 user_data_t *user_data;
2296 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
2298 /* Perhaps the user specified a directory instead of a file.
2299 Check whether they did. */
2300 if (test_for_directory(g_dest) == EISDIR) {
2301 /* It's a directory - set the file selection box to display it. */
2302 set_last_open_dir(g_dest);
2304 file_selection_set_current_folder(fs, get_last_open_dir());
2308 rev = (GtkWidget*)OBJECT_GET_DATA(bt, "reversed_rb");
2309 forw = (GtkWidget*)OBJECT_GET_DATA(bt, "forward_rb");
2310 both = (GtkWidget*)OBJECT_GET_DATA(bt, "both_rb");
2311 user_data = (user_data_t*)OBJECT_GET_DATA(bt, "user_data");
2313 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
2314 fp = fopen(g_dest, "w");
2316 open_failure_alert_box(g_dest, errno, TRUE);
2320 if (GTK_TOGGLE_BUTTON(both)->active) {
2321 fprintf(fp, "Forward\n");
2323 write_failure_alert_box(g_dest, errno);
2329 for(j = 0; j < NUM_COLS; j++) {
2331 fprintf(fp,"%s",titles[j]);
2333 fprintf(fp,",%s",titles[j]);
2338 write_failure_alert_box(g_dest, errno);
2342 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
2343 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
2344 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
2346 fprintf(fp,"%s",columnText);
2348 fprintf(fp,",%s",columnText);
2353 write_failure_alert_box(g_dest, errno);
2359 if (fclose(fp) == EOF) {
2360 write_failure_alert_box(g_dest, errno);
2365 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
2367 if (GTK_TOGGLE_BUTTON(both)->active) {
2368 fp = fopen(g_dest, "a");
2370 open_failure_alert_box(g_dest, errno, TRUE);
2373 fprintf(fp, "\nReverse\n");
2375 write_failure_alert_box(g_dest, errno);
2380 fp = fopen(g_dest, "w");
2382 open_failure_alert_box(g_dest, errno, TRUE);
2386 for(j = 0; j < NUM_COLS; j++) {
2388 fprintf(fp,"%s",titles[j]);
2390 fprintf(fp,",%s",titles[j]);
2395 write_failure_alert_box(g_dest, errno);
2399 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
2400 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
2401 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
2403 fprintf(fp,"%s",columnText);
2405 fprintf(fp,",%s",columnText);
2410 write_failure_alert_box(g_dest, errno);
2415 if (fclose(fp) == EOF) {
2416 write_failure_alert_box(g_dest, errno);
2421 window_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
2424 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2426 user_data->dlg.save_csv_as_w = NULL;
2429 /* when the user wants to save the csv information in a file */
2430 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
2434 GtkWidget *label_format;
2435 GtkWidget *channels_label;
2436 GSList *channels_group = NULL;
2437 GtkWidget *forward_rb;
2438 GtkWidget *reversed_rb;
2442 if (user_data->dlg.save_csv_as_w != NULL) {
2443 /* There's already a Save CSV info dialog box; reactivate it. */
2444 reactivate_window(user_data->dlg.save_csv_as_w);
2448 user_data->dlg.save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV");
2450 /* Container for each row of widgets */
2451 vertb = gtk_vbox_new(FALSE, 0);
2452 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
2453 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
2454 vertb, FALSE, FALSE, 0);
2455 gtk_widget_show (vertb);
2457 table1 = gtk_table_new (2, 4, FALSE);
2458 gtk_widget_show (table1);
2459 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2460 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2461 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2463 label_format = gtk_label_new ("Format: Comma Separated Values");
2464 gtk_widget_show (label_format);
2465 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2466 (GtkAttachOptions) (GTK_FILL),
2467 (GtkAttachOptions) (0), 0, 0);
2470 channels_label = gtk_label_new ("Channels:");
2471 gtk_widget_show (channels_label);
2472 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2473 (GtkAttachOptions) (GTK_FILL),
2474 (GtkAttachOptions) (0), 0, 0);
2475 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2477 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2478 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
2479 gtk_widget_show (forward_rb);
2480 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2481 (GtkAttachOptions) (GTK_FILL),
2482 (GtkAttachOptions) (0), 0, 0);
2484 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2485 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
2486 gtk_widget_show (reversed_rb);
2487 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2488 (GtkAttachOptions) (GTK_FILL),
2489 (GtkAttachOptions) (0), 0, 0);
2491 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2492 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
2493 gtk_widget_show (both_rb);
2494 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2495 (GtkAttachOptions) (GTK_FILL),
2496 (GtkAttachOptions) (0), 0, 0);
2498 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2500 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
2501 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
2502 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
2503 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
2504 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
2505 SIGNAL_CONNECT(ok_bt, "clicked", save_csv_as_ok_cb,
2506 user_data->dlg.save_csv_as_w);
2508 window_set_cancel_button(user_data->dlg.save_csv_as_w,
2509 GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button, NULL);
2511 SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "delete_event", window_delete_event_cb, NULL);
2512 SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "destroy",
2513 save_csv_as_destroy_cb, user_data);
2515 gtk_widget_show(user_data->dlg.save_csv_as_w);
2516 window_present(user_data->dlg.save_csv_as_w);
2520 /****************************************************************************/
2521 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2523 /* Note that we no longer have a Save voice info dialog box. */
2524 user_data->dlg.save_voice_as_w = NULL;
2527 /****************************************************************************/
2528 /* here we save it into a file that user specified */
2529 /* XXX what about endians here? could go something wrong? */
2530 static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ user_data_t *user_data)
2532 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
2536 guint32 f_write_silence = 0;
2537 guint32 r_write_silence = 0;
2539 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2540 gboolean stop_flag = FALSE;
2542 forw_fd = open(user_data->f_tempname, O_RDONLY | O_BINARY);
2545 rev_fd = open(user_data->r_tempname, O_RDONLY | O_BINARY);
2551 /* open file for saving */
2552 to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2559 progbar = create_progress_dlg("Saving voice in a file", dest, &stop_flag);
2561 /* First we write the .au header. XXX Hope this is endian independant */
2562 /* the magic word 0x2e736e64 == .snd */
2563 *pd = (unsigned char)0x2e; write(to_fd, pd, 1);
2564 *pd = (unsigned char)0x73; write(to_fd, pd, 1);
2565 *pd = (unsigned char)0x6e; write(to_fd, pd, 1);
2566 *pd = (unsigned char)0x64; write(to_fd, pd, 1);
2567 /* header offset == 24 bytes */
2568 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2569 write(to_fd, pd, 1);
2570 write(to_fd, pd, 1);
2571 *pd = (unsigned char)0x18; write(to_fd, pd, 1);
2572 /* total length, it is permited to set this to 0xffffffff */
2573 *pd = (unsigned char)0xff; write(to_fd, pd, 1);
2574 write(to_fd, pd, 1);
2575 write(to_fd, pd, 1);
2576 write(to_fd, pd, 1);
2577 /* encoding format == 8 bit ulaw */
2578 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2579 write(to_fd, pd, 1);
2580 write(to_fd, pd, 1);
2581 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
2582 /* sample rate == 8000 Hz */
2583 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2584 write(to_fd, pd, 1);
2585 *pd = (unsigned char)0x1f; write(to_fd, pd, 1);
2586 *pd = (unsigned char)0x40; write(to_fd, pd, 1);
2588 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2589 write(to_fd, pd, 1);
2590 write(to_fd, pd, 1);
2591 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
2594 /* only forward direction */
2596 progbar_count = user_data->forward.saveinfo.count;
2597 progbar_quantum = user_data->forward.saveinfo.count/100;
2598 while ((fread = read(forw_fd, &f_pd, 2)) > 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;
2607 *pd = (unsigned char)linear2ulaw(f_pd);
2608 fwritten = write(to_fd, pd, 1);
2609 if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) {
2613 destroy_progress_dlg(progbar);
2619 /* only reversed direction */
2621 progbar_count = user_data->reversed.saveinfo.count;
2622 progbar_quantum = user_data->reversed.saveinfo.count/100;
2623 while ((rread = read(rev_fd, &r_pd, 2)) > 0) {
2626 if((count > progbar_nextstep) && (count <= progbar_count)) {
2627 update_progress_dlg(progbar,
2628 (gfloat) count/progbar_count, "Saving");
2629 progbar_nextstep = progbar_nextstep + progbar_quantum;
2632 *pd = (unsigned char)linear2ulaw(r_pd);
2633 rwritten = write(to_fd, pd, 1);
2634 if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) {
2638 destroy_progress_dlg(progbar);
2644 /* both directions */
2646 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2647 (progbar_count = user_data->forward.saveinfo.count) :
2648 (progbar_count = user_data->reversed.saveinfo.count);
2649 progbar_quantum = progbar_count/100;
2650 /* since conversation in one way can start later than in the other one,
2651 * we have to write some silence information for one channel */
2652 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2653 f_write_silence = (guint32)
2654 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2656 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2657 r_write_silence = (guint32)
2658 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2663 if((count > progbar_nextstep) && (count <= progbar_count)) {
2664 update_progress_dlg(progbar,
2665 (gfloat) count/progbar_count, "Saving");
2666 progbar_nextstep = progbar_nextstep + progbar_quantum;
2669 if(f_write_silence > 0) {
2670 rread = read(rev_fd, &r_pd, 2);
2675 else if(r_write_silence > 0) {
2676 fread = read(forw_fd, &f_pd, 2);
2682 fread = read(forw_fd, &f_pd, 2);
2683 rread = read(rev_fd, &r_pd, 2);
2685 if ((rread == 0) && (fread == 0))
2687 *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 );
2688 rwritten = write(to_fd, pd, 1);
2689 if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
2693 destroy_progress_dlg(progbar);
2699 destroy_progress_dlg(progbar);
2707 /****************************************************************************/
2708 /* the user wants to save in a file */
2709 /* XXX support for different formats is currently commented out */
2710 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
2713 /*GtkWidget *wav, *au, *sw;*/
2714 GtkWidget *rev, *forw, *both;
2715 user_data_t *user_data;
2716 gint channels /*, format*/;
2718 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
2720 /* Perhaps the user specified a directory instead of a file.
2721 Check whether they did. */
2722 if (test_for_directory(g_dest) == EISDIR) {
2723 /* It's a directory - set the file selection box to display it. */
2724 set_last_open_dir(g_dest);
2726 file_selection_set_current_folder(fs, get_last_open_dir());
2730 /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
2731 au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
2732 sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
2733 rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
2734 forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
2735 both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
2736 user_data = (user_data_t *)OBJECT_GET_DATA(ok_bt, "user_data");
2738 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2739 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2740 * disable the ok button or disable the buttons for direction if only one is not ok. The
2741 * problem is if we open the save voice dialog and then click the refresh button and maybe
2742 * the state changes, so we can't save anymore. In this case we should be able to update
2743 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2746 /* we can not save in both dirctions */
2747 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2748 /* there are many combinations here, we just exit when first matches */
2749 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2750 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2751 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2752 "Can't save in a file: Unsupported codec!");
2753 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2754 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2755 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2756 "Can't save in a file: Wrong length of captured packets!");
2757 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2758 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2759 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2760 "Can't save in a file: RTP data with padding!");
2761 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2762 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2763 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2764 "Can't save in a file: Not all data in all packets was captured!");
2766 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2767 "Can't save in a file: File I/O problem!");
2770 /* we can not save forward direction */
2771 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2772 (GTK_TOGGLE_BUTTON (both)->active))) {
2773 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2774 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2775 "Can't save forward direction in a file: Unsupported codec!");
2776 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2777 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2778 "Can't save forward direction in a file: Wrong length of captured packets!");
2779 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2780 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2781 "Can't save forward direction in a file: RTP data with padding!");
2782 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2783 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2784 "Can't save forward direction in a file: Not all data in all packets was captured!");
2786 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2787 "Can't save forward direction in a file: File I/O problem!");
2790 /* we can not save reversed direction */
2791 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2792 (GTK_TOGGLE_BUTTON (both)->active))) {
2793 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2794 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2795 "Can't save reversed direction in a file: Unsupported codec!");
2796 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2797 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2798 "Can't save reversed direction in a file: Wrong length of captured packets!");
2799 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2800 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2801 "Can't save reversed direction in a file: RTP data with padding!");
2802 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2803 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2804 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2805 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2806 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2807 "Can't save reversed direction in a file: No RTP data!");
2809 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2810 "Can't save reversed direction in a file: File I/O problem!");
2814 /*if (GTK_TOGGLE_BUTTON (wav)->active)
2816 else if (GTK_TOGGLE_BUTTON (au)->active)
2818 else if (GTK_TOGGLE_BUTTON (sw)->active)
2821 if (GTK_TOGGLE_BUTTON (rev)->active)
2823 else if (GTK_TOGGLE_BUTTON (both)->active)
2828 if(!copy_file(g_dest, channels/*, format*/, user_data)) {
2829 /* XXX - report the error type! */
2830 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2831 "An error occured while saving voice in a file!");
2835 window_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
2838 /****************************************************************************/
2839 /* when the user wants to save the voice information in a file */
2840 /* XXX support for different formats is currently commented out */
2841 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2845 GtkWidget *label_format;
2846 GtkWidget *channels_label;
2847 /*GSList *format_group = NULL;*/
2848 GSList *channels_group = NULL;
2849 GtkWidget *forward_rb;
2850 GtkWidget *reversed_rb;
2852 /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/
2855 /* if we can't save in a file: wrong codec, cut packets or other errors */
2856 /* shold the error arise here or later when you click ok button ?
2857 * if we do it here, then we must disable the refresh button, so we don't do it here */
2859 if (user_data->dlg.save_voice_as_w != NULL) {
2860 /* There's already a Save voice info dialog box; reactivate it. */
2861 reactivate_window(user_data->dlg.save_voice_as_w);
2865 /* XXX - use file_selection from dlg_utils instead! */
2866 user_data->dlg.save_voice_as_w = gtk_file_selection_new("Ethereal: Save Payload As ...");
2868 /* Container for each row of widgets */
2869 vertb = gtk_vbox_new(FALSE, 0);
2870 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
2871 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
2872 vertb, FALSE, FALSE, 0);
2873 gtk_widget_show (vertb);
2875 table1 = gtk_table_new (2, 4, FALSE);
2876 gtk_widget_show (table1);
2877 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2878 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2879 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2881 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2882 gtk_widget_show (label_format);
2883 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2884 (GtkAttachOptions) (GTK_FILL),
2885 (GtkAttachOptions) (0), 0, 0);
2887 /* we support .au - ulaw*/
2888 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2889 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
2890 gtk_widget_show (wav_rb);
2891 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2892 (GtkAttachOptions) (GTK_FILL),
2893 (GtkAttachOptions) (0), 0, 0);
2895 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2896 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
2897 gtk_widget_show (sw_rb);
2898 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2899 (GtkAttachOptions) (GTK_FILL),
2900 (GtkAttachOptions) (0), 0, 0);
2901 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2902 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
2903 gtk_widget_show (au_rb);
2904 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2905 (GtkAttachOptions) (GTK_FILL),
2906 (GtkAttachOptions) (0), 0, 0);
2909 channels_label = gtk_label_new ("Channels:");
2910 gtk_widget_show (channels_label);
2911 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2912 (GtkAttachOptions) (GTK_FILL),
2913 (GtkAttachOptions) (0), 0, 0);
2914 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2916 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2917 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
2918 gtk_widget_show (forward_rb);
2919 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2920 (GtkAttachOptions) (GTK_FILL),
2921 (GtkAttachOptions) (0), 0, 0);
2923 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2924 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
2925 gtk_widget_show (reversed_rb);
2926 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2927 (GtkAttachOptions) (GTK_FILL),
2928 (GtkAttachOptions) (0), 0, 0);
2930 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2931 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
2932 gtk_widget_show (both_rb);
2933 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2934 (GtkAttachOptions) (GTK_FILL),
2935 (GtkAttachOptions) (0), 0, 0);
2937 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2939 /* if one direction is nok we don't allow saving
2940 XXX this is not ok since the user can click the refresh button and cause changes
2941 but we can not update this window. So we move all the decision on the time the ok
2943 if (user_data->forward.saved == FALSE) {
2944 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2945 gtk_widget_set_sensitive(forward_rb, FALSE);
2946 gtk_widget_set_sensitive(both_rb, FALSE);
2948 else if (user_data->reversed.saved == FALSE) {
2949 gtk_widget_set_sensitive(reversed_rb, FALSE);
2950 gtk_widget_set_sensitive(both_rb, FALSE);
2954 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
2955 /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);
2956 OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
2957 OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
2958 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
2959 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
2960 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
2961 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
2962 SIGNAL_CONNECT(ok_bt, "clicked", save_voice_as_ok_cb,
2963 user_data->dlg.save_voice_as_w);
2965 window_set_cancel_button(user_data->dlg.save_voice_as_w,
2966 GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button, window_cancel_button_cb);
2968 SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "delete_event",
2969 window_delete_event_cb, NULL);
2970 SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "destroy",
2971 save_voice_as_destroy_cb, user_data);
2973 gtk_widget_show(user_data->dlg.save_voice_as_w);
2974 window_present(user_data->dlg.save_voice_as_w);
2978 /****************************************************************************/
2979 /* when we are finished with redisection, we add the label for the statistic */
2980 static void draw_stat(user_data_t *user_data)
2982 gchar label_max[200];
2983 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
2984 - user_data->forward.statinfo.start_seq_nr + 1;
2985 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
2986 - user_data->reversed.statinfo.start_seq_nr + 1;
2987 gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
2988 gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
2989 double f_perc, r_perc;
2991 f_perc = (double)(f_lost*100)/(double)f_expected;
2996 r_perc = (double)(r_lost*100)/(double)r_expected;
3001 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
3002 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
3003 " Sequence errors = %u",
3004 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
3005 user_data->forward.statinfo.total_nr,
3006 f_expected, f_lost, f_perc, user_data->forward.statinfo.sequence);
3008 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
3010 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
3011 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
3012 " Sequence errors = %u",
3013 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
3014 user_data->reversed.statinfo.total_nr,
3015 r_expected, r_lost, r_perc, user_data->reversed.statinfo.sequence);
3017 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
3024 /****************************************************************************/
3025 /* append a line to clist */
3026 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
3027 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
3028 gchar *timeStr, guint32 pkt_len, GdkColor *color)
3034 data[0]=&field[0][0];
3035 data[1]=&field[1][0];
3036 data[2]=&field[2][0];
3037 data[3]=&field[3][0];
3038 data[4]=&field[4][0];
3039 data[5]=&field[5][0];
3040 data[6]=&field[6][0];
3041 data[7]=&field[7][0];
3042 data[8]=&field[8][0];
3044 g_snprintf(field[0], 20, "%u", number);
3045 g_snprintf(field[1], 20, "%u", seq_num);
3046 g_snprintf(field[2], 20, "%.2f", delta);
3047 g_snprintf(field[3], 20, "%.2f", jitter);
3048 g_snprintf(field[4], 20, "%.2f", bandwidth);
3049 g_snprintf(field[5], 20, "%s", marker? "SET" : "");
3050 g_snprintf(field[6], 40, "%s", status);
3051 g_snprintf(field[7], 32, "%s", timeStr);
3052 g_snprintf(field[8], 20, "%u", pkt_len);
3053 added_row = gtk_clist_append(GTK_CLIST(clist), data);
3054 gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
3055 gtk_clist_set_background(GTK_CLIST(clist), added_row, color);
3059 /****************************************************************************/
3060 /* callback for sorting columns of clist */
3061 static gint rtp_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
3068 const GtkCListRow *row1 = (GtkCListRow *) ptr1;
3069 const GtkCListRow *row2 = (GtkCListRow *) ptr2;
3071 text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
3072 text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
3074 switch(clist->sort_column){
3075 /* columns representing strings */
3079 return strcmp (text1, text2);
3080 /* columns representing ints */
3087 /* columns representing floats */
3093 if (fabs(f1-f2)<0.0000005)
3099 g_assert_not_reached();
3104 /****************************************************************************/
3106 click_column_cb(GtkCList *clist, gint column, gpointer data)
3108 column_arrows *col_arrows = (column_arrows *) data;
3111 gtk_clist_freeze(clist);
3113 for (i = 0; i < NUM_COLS; i++) {
3114 gtk_widget_hide(col_arrows[i].ascend_pm);
3115 gtk_widget_hide(col_arrows[i].descend_pm);
3118 if (column == clist->sort_column) {
3119 if (clist->sort_type == GTK_SORT_ASCENDING) {
3120 clist->sort_type = GTK_SORT_DESCENDING;
3121 gtk_widget_show(col_arrows[column].descend_pm);
3123 clist->sort_type = GTK_SORT_ASCENDING;
3124 gtk_widget_show(col_arrows[column].ascend_pm);
3127 clist->sort_type = GTK_SORT_ASCENDING;
3128 gtk_widget_show(col_arrows[column].ascend_pm);
3129 gtk_clist_set_sort_column(clist, column);
3131 gtk_clist_thaw(clist);
3133 gtk_clist_sort(clist);
3137 /****************************************************************************/
3138 /* Add the packet list */
3140 GtkWidget* create_clist(user_data_t* user_data)
3142 GtkWidget* clist_fwd;
3144 /* clist for the information */
3145 clist_fwd = gtk_clist_new(NUM_COLS);
3146 gtk_widget_show(clist_fwd);
3147 SIGNAL_CONNECT(clist_fwd, "select_row", on_clist_select_row, user_data);
3149 gtk_clist_column_titles_show(GTK_CLIST(clist_fwd));
3150 gtk_clist_set_compare_func(GTK_CLIST(clist_fwd), rtp_sort_column);
3151 gtk_clist_set_sort_column(GTK_CLIST(clist_fwd), 0);
3152 gtk_clist_set_sort_type(GTK_CLIST(clist_fwd), GTK_SORT_ASCENDING);
3154 /* hide date and length column */
3155 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
3156 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 8, FALSE);
3158 /* column widths and justification */
3159 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 60);
3160 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 75);
3161 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 75);
3162 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 75);
3163 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 50);
3164 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 5, 75);
3165 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_RIGHT);
3166 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_RIGHT);
3167 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
3168 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
3169 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
3170 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
3171 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 6, GTK_JUSTIFY_CENTER);
3176 /****************************************************************************/
3177 /* Add the sort by column feature for a packet clist */
3179 column_arrows* add_sort_by_column(GtkWidget* window, GtkWidget* clist,
3180 user_data_t* user_data _U_)
3182 column_arrows *col_arrows;
3183 GdkBitmap *ascend_bm, *descend_bm;
3184 GdkPixmap *ascend_pm, *descend_pm;
3185 GtkStyle *win_style;
3186 GtkWidget *column_lb;
3189 col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
3190 win_style = gtk_widget_get_style(window);
3191 ascend_pm = gdk_pixmap_create_from_xpm_d(window->window,
3193 &win_style->bg[GTK_STATE_NORMAL],
3194 (gchar **)clist_ascend_xpm);
3195 descend_pm = gdk_pixmap_create_from_xpm_d(window->window,
3197 &win_style->bg[GTK_STATE_NORMAL],
3198 (gchar **)clist_descend_xpm);
3200 for (i=0; i<NUM_COLS; i++) {
3201 col_arrows[i].table = gtk_table_new(2, 2, FALSE);
3202 gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
3203 column_lb = gtk_label_new(titles[i]);
3204 gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
3205 gtk_widget_show(column_lb);
3207 col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
3208 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
3209 col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
3210 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
3211 /* make packet-nr be the default sort order */
3213 gtk_widget_show(col_arrows[i].ascend_pm);
3215 gtk_clist_set_column_widget(GTK_CLIST(clist), i, col_arrows[i].table);
3216 gtk_widget_show(col_arrows[i].table);
3219 SIGNAL_CONNECT(clist, "click-column", click_column_cb, col_arrows);
3224 /****************************************************************************/
3225 /* Create the dialog box with all widgets */
3226 void create_rtp_dialog(user_data_t* user_data)
3228 GtkWidget *window = NULL;
3229 GtkWidget *clist_fwd;
3230 GtkWidget *clist_rev;
3231 GtkWidget *label_stats_fwd;
3232 GtkWidget *label_stats_rev;
3233 GtkWidget *notebook;
3235 GtkWidget *main_vb, *page, *page_r;
3237 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3238 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3239 #ifdef USE_CONVERSATION_GRAPH
3240 GtkWidget *graph_bt;
3242 GtkWidget *graph_bt;
3243 gchar label_forward[150];
3244 gchar label_reverse[150];
3246 gchar str_ip_src[16];
3247 gchar str_ip_dst[16];
3248 column_arrows *col_arrows_fwd;
3249 column_arrows *col_arrows_rev;
3251 window = window_new(GTK_WINDOW_TOPLEVEL, "Ethereal: RTP Stream Analysis");
3252 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3254 /* Container for each row of widgets */
3255 main_vb = gtk_vbox_new(FALSE, 2);
3256 gtk_container_border_width(GTK_CONTAINER(main_vb), 2);
3257 gtk_container_add(GTK_CONTAINER(window), main_vb);
3258 gtk_widget_show(main_vb);
3261 strcpy(str_ip_src, address_to_str_w_none(&(user_data->ip_src_fwd)));
3262 strcpy(str_ip_dst, address_to_str_w_none(&(user_data->ip_dst_fwd)));
3264 g_snprintf(label_forward, 149,
3265 "Analysing stream from %s port %u to %s port %u SSRC = %u",
3266 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3269 strcpy(str_ip_src, address_to_str_w_none(&(user_data->ip_src_rev)));
3270 strcpy(str_ip_dst, address_to_str_w_none(&(user_data->ip_dst_rev)));
3272 g_snprintf(label_reverse, 149,
3273 "Analysing stream from %s port %u to %s port %u SSRC = %u",
3274 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3276 /* Start a notebook for flipping between sets of changes */
3277 notebook = gtk_notebook_new();
3278 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3279 OBJECT_SET_DATA(window, "notebook", notebook);
3281 user_data->dlg.notebook_signal_id = SIGNAL_CONNECT(notebook, "switch_page", on_notebook_switch_page,
3284 /* page for forward connection */
3285 page = gtk_vbox_new(FALSE, 8);
3286 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3288 /* direction label */
3289 label = gtk_label_new(label_forward);
3290 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3292 /* place for some statistics */
3293 label_stats_fwd = gtk_label_new("\n");
3294 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3296 /* scrolled window */
3297 scrolled_window = scrolled_window_new(NULL, NULL);
3300 clist_fwd = create_clist(user_data);
3301 gtk_widget_show(clist_fwd);
3302 gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
3303 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3304 gtk_widget_show(scrolled_window);
3307 label = gtk_label_new(" Forward Direction ");
3308 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3310 /* same page for reversed connection */
3311 page_r = gtk_vbox_new(FALSE, 8);
3312 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3313 label = gtk_label_new(label_reverse);
3314 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3315 label_stats_rev = gtk_label_new("\n");
3316 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3318 scrolled_window_r = scrolled_window_new(NULL, NULL);
3320 clist_rev = create_clist(user_data);
3321 gtk_widget_show(clist_rev);
3322 gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
3323 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3324 gtk_widget_show(scrolled_window_r);
3326 label = gtk_label_new(" Reversed Direction ");
3327 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3329 /* page for help&about or future
3330 page_help = gtk_hbox_new(FALSE, 5);
3331 label = gtk_label_new(" Future ");
3332 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3333 frame = gtk_frame_new("");
3334 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3335 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3336 gtk_container_add(GTK_CONTAINER(frame), text);
3337 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3338 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3341 /* show all notebooks */
3342 gtk_widget_show_all(notebook);
3345 box4 = gtk_hbutton_box_new();
3346 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3347 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3348 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3349 gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0);
3350 gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
3351 gtk_widget_show(box4);
3353 voice_bt = gtk_button_new_with_label("Save payload...");
3354 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3355 gtk_widget_show(voice_bt);
3356 SIGNAL_CONNECT(voice_bt, "clicked", on_save_bt_clicked, user_data);
3358 csv_bt = gtk_button_new_with_label("Save as CSV...");
3359 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3360 gtk_widget_show(csv_bt);
3361 SIGNAL_CONNECT(csv_bt, "clicked", save_csv_as_cb, user_data);
3363 refresh_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_REFRESH);
3364 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3365 gtk_widget_show(refresh_bt);
3366 SIGNAL_CONNECT(refresh_bt, "clicked", on_refresh_bt_clicked, user_data);
3368 goto_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_JUMP_TO);
3369 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3370 gtk_widget_show(goto_bt);
3371 SIGNAL_CONNECT(goto_bt, "clicked", on_goto_bt_clicked, user_data);
3373 graph_bt = gtk_button_new_with_label("Graph");
3374 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3375 gtk_widget_show(graph_bt);
3376 SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
3379 #ifdef USE_CONVERSATION_GRAPH
3380 graph_bt = gtk_button_new_with_label("Graph");
3381 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3382 gtk_widget_show(graph_bt);
3383 SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
3386 next_bt = gtk_button_new_with_label("Next non-Ok");
3387 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3388 gtk_widget_show(next_bt);
3389 SIGNAL_CONNECT(next_bt, "clicked", on_next_bt_clicked, user_data);
3391 close_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
3392 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3393 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3394 gtk_widget_show(close_bt);
3395 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3397 SIGNAL_CONNECT(window, "delete_event", window_delete_event_cb, NULL);
3398 SIGNAL_CONNECT(window, "destroy", on_destroy, user_data);
3400 gtk_widget_show(window);
3401 window_present(window);
3403 /* sort by column feature */
3404 col_arrows_fwd = add_sort_by_column(window, clist_fwd, user_data);
3405 col_arrows_rev = add_sort_by_column(window, clist_rev, user_data);
3407 /* some widget references need to be saved for outside use */
3408 user_data->dlg.window = window;
3409 user_data->dlg.clist_fwd = GTK_CLIST(clist_fwd);
3410 user_data->dlg.clist_rev = GTK_CLIST(clist_rev);
3411 user_data->dlg.label_stats_fwd = label_stats_fwd;
3412 user_data->dlg.label_stats_rev = label_stats_rev;
3413 user_data->dlg.notebook = notebook;
3414 user_data->dlg.selected_clist = GTK_CLIST(clist_fwd);
3415 user_data->dlg.selected_row = 0;
3416 user_data->dlg.col_arrows_fwd = col_arrows_fwd;
3417 user_data->dlg.col_arrows_rev = col_arrows_rev;
3421 /****************************************************************************/
3422 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3423 gchar* proto_field, guint32* p_result)
3426 proto_node *proto_sibling_node;
3427 header_field_info *hfssrc;
3430 finfo = PITEM_FINFO(ptree_node);
3432 if (hfinformation==(finfo->hfinfo)) {
3433 hfssrc = proto_registrar_get_byname(proto_field);
3436 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3437 ptree_node=ptree_node->next) {
3438 finfo=PITEM_FINFO(ptree_node);
3439 if (hfssrc==finfo->hfinfo) {
3440 if (hfinformation->type==FT_IPv4) {
3441 ipv4 = fvalue_get(&finfo->value);
3442 *p_result = ipv4_get_net_order_addr(ipv4);
3445 *p_result = fvalue_get_integer(&finfo->value);
3452 proto_sibling_node = ptree_node->next;
3454 if (proto_sibling_node) {
3455 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3461 /****************************************************************************/
3462 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3467 proto_node *ptree_node;
3468 header_field_info *hfinformation;
3470 hfinformation = proto_registrar_get_byname(proto_name);
3471 if (hfinformation == NULL)
3474 ptree_node = ((proto_node *)protocol_tree)->first_child;
3478 return process_node(ptree_node, hfinformation, proto_field, p_result);
3482 /* XXX just copied from gtk/rpc_stat.c */
3483 void protect_thread_critical_region(void);
3484 void unprotect_thread_critical_region(void);
3486 /****************************************************************************/
3487 /* XXX only handles RTP over IPv4, should add IPv6 support */
3489 address *ip_src_fwd,
3490 guint16 port_src_fwd,
3491 address *ip_dst_fwd,
3492 guint16 port_dst_fwd,
3494 address *ip_src_rev,
3495 guint16 port_src_rev,
3496 address *ip_dst_rev,
3497 guint16 port_dst_rev,
3501 user_data_t *user_data;
3504 static color_t col[MAX_GRAPHS] = {
3505 {0, 0x0000, 0x0000, 0x0000},
3506 {0, 0xffff, 0x0000, 0x0000},
3507 {0, 0x0000, 0xffff, 0x0000},
3508 {0, 0x0000, 0x0000, 0xffff}
3512 user_data = g_malloc(sizeof(user_data_t));
3514 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3515 user_data->port_src_fwd = port_src_fwd;
3516 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3517 user_data->port_dst_fwd = port_dst_fwd;
3518 user_data->ssrc_fwd = ssrc_fwd;
3519 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3520 user_data->port_src_rev = port_src_rev;
3521 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3522 user_data->port_dst_rev = port_dst_rev;
3523 user_data->ssrc_rev = ssrc_rev;
3526 /* file names for storing sound data */
3527 /*XXX: check for errors*/
3528 fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
3531 fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
3534 user_data->forward.saveinfo.fp = NULL;
3535 user_data->reversed.saveinfo.fp = NULL;
3536 user_data->dlg.save_voice_as_w = NULL;
3537 user_data->dlg.save_csv_as_w = NULL;
3538 user_data->dlg.dialog_graph.window = NULL;
3540 #ifdef USE_CONVERSATION_GRAPH
3541 user_data->dlg.graph_window = NULL;
3542 user_data->series_fwd.value_pairs = NULL;
3543 user_data->series_rev.value_pairs = NULL;
3546 /* init dialog_graph */
3547 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3548 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3549 user_data->dlg.dialog_graph.draw_area=NULL;
3550 user_data->dlg.dialog_graph.pixmap=NULL;
3551 user_data->dlg.dialog_graph.scrollbar=NULL;
3552 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3553 user_data->dlg.dialog_graph.pixmap_width=500;
3554 user_data->dlg.dialog_graph.pixmap_height=200;
3555 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3556 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3557 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3558 user_data->dlg.dialog_graph.max_interval=0;
3559 user_data->dlg.dialog_graph.num_items=0;
3560 user_data->dlg.dialog_graph.start_time = -1;
3562 for(i=0;i<MAX_GRAPHS;i++){
3563 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3564 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3565 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3566 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3567 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3568 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3569 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3570 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3573 /* create the dialog box */
3574 create_rtp_dialog(user_data);
3576 /* proceed as if the Refresh button would have been pressed */
3577 on_refresh_bt_clicked(NULL, user_data);
3580 /****************************************************************************/
3581 /* entry point from main menu */
3582 void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3585 guint16 port_src_fwd;
3587 guint16 port_dst_fwd;
3588 guint32 ssrc_fwd = 0;
3590 guint16 port_src_rev;
3592 guint16 port_dst_rev;
3593 guint32 ssrc_rev = 0;
3594 unsigned int version_fwd;
3596 gchar filter_text[256];
3599 epan_dissect_t *edt;
3602 gboolean frame_matched;
3604 GList *strinfo_list;
3605 GList *filtered_list = NULL;
3606 rtp_stream_info_t *strinfo;
3609 /* Try to compile the filter. */
3610 strcpy(filter_text,"rtp && (ip || ipv6)");
3611 if (!dfilter_compile(filter_text, &sfcode)) {
3612 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, dfilter_error_msg);
3615 /* we load the current file into cf variable */
3617 fdata = cf->current_frame;
3619 /* we are on the selected frame now */
3621 return; /* if we exit here it's an error */
3623 /* dissect the current frame */
3624 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3625 cf->pd, fdata->cap_len, &err, &err_info)) {
3626 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3627 cf_read_error_message(err, err_info), cf->filename);
3630 edt = epan_dissect_new(TRUE, FALSE);
3631 epan_dissect_prime_dfilter(edt, sfcode);
3632 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
3633 frame_matched = dfilter_apply_edt(sfcode, edt);
3635 /* if it is not an rtp frame, show the rtpstream dialog */
3636 frame_matched = dfilter_apply_edt(sfcode, edt);
3637 if (frame_matched != 1) {
3638 epan_dissect_free(edt);
3639 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3640 "You didn't choose a RTP packet!");
3644 /* ok, it is a RTP frame, so let's get the ip and port values */
3645 COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
3646 COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
3647 port_src_fwd = edt->pi.srcport;
3648 port_dst_fwd = edt->pi.destport;
3650 /* assume the inverse ip/port combination for the reverse direction */
3651 COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
3652 COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
3653 port_src_rev = edt->pi.destport;
3654 port_dst_rev = edt->pi.srcport;
3656 /* check if it is RTP Version 2 */
3657 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3658 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3659 "RTP Version != 2 isn't supported!");
3663 /* now we need the SSRC value of the current frame */
3664 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3665 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3666 "SSRC value couldn't be found!");
3670 /* Scan for rtpstream */
3672 /* search for reversed direction in the global rtp streams list */
3674 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3675 while (strinfo_list)
3677 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3678 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3679 && strinfo->src_port==port_src_fwd
3680 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3681 && strinfo->dest_port==port_dst_fwd)
3683 filtered_list = g_list_prepend(filtered_list, strinfo);
3686 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3687 && strinfo->src_port==port_src_rev
3688 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3689 && strinfo->dest_port==port_dst_rev)
3692 filtered_list = g_list_append(filtered_list, strinfo);
3694 ssrc_rev = strinfo->ssrc;
3697 strinfo_list = g_list_next(strinfo_list);
3700 /* if more than one reverse streams found, we let the user choose the right one */
3702 rtpstream_dlg_show(filtered_list);
3721 /****************************************************************************/
3723 rtp_analysis_init(char *dummy _U_)
3725 rtp_analysis_cb(NULL, NULL);
3728 /****************************************************************************/
3730 register_tap_listener_rtp_analysis(void)
3732 register_ethereal_tap("rtp", rtp_analysis_init);
3734 register_tap_menu_item("RTP/Stream Analysis...", REGISTER_TAP_GROUP_NONE,
3735 rtp_analysis_cb, NULL, NULL, NULL);