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>
60 #include <epan/addr_resolv.h>
64 #include "gtkglobals.h"
66 #include <epan/stat_cmd_args.h>
67 #include "dlg_utils.h"
68 #include "gui_utils.h"
69 #include "alert_box.h"
70 #include "simple_dialog.h"
71 #include "stat_menu.h"
73 #include "progress_dlg.h"
74 #include "compat_macros.h"
77 #include "image/clist_ascend.xpm"
78 #include "image/clist_descend.xpm"
94 #include <io.h> /* open/close on win32 */
97 /* Win32 needs the O_BINARY flag for open() */
102 /****************************************************************************/
104 typedef struct column_arrows {
106 GtkWidget *ascend_pm;
107 GtkWidget *descend_pm;
111 #define NUM_GRAPH_ITEMS 100000
112 #define MAX_YSCALE 16
113 #define AUTO_MAX_YSCALE 0
115 #define GRAPH_FWD_JITTER 0
116 #define GRAPH_FWD_DIFF 1
117 #define GRAPH_REV_JITTER 2
118 #define GRAPH_REV_DIFF 3
119 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
121 #define MAX_PIXELS_PER_TICK 4
122 #define DEFAULT_PIXELS_PER_TICK 1
123 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
124 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
126 #define MAX_TICK_VALUES 5
127 #define DEFAULT_TICK_VALUE 1
128 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
129 typedef struct _dialog_graph_graph_item_t {
132 } dialog_graph_graph_item_t;
134 typedef struct _dialog_graph_graph_t {
135 struct _user_data_t *ud;
136 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
139 GtkWidget *display_button;
144 } dialog_graph_graph_t;
147 typedef struct _dialog_graph_t {
148 gboolean needs_redraw;
149 gint32 interval; /* measurement interval in ms */
150 guint32 last_interval;
151 guint32 max_interval; /* XXX max_interval and num_items are redundant */
153 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
155 GtkWidget *draw_area;
157 GtkAdjustment *scrollbar_adjustment;
158 GtkWidget *scrollbar;
166 typedef struct _dialog_data_t {
170 GtkWidget *label_stats_fwd;
171 GtkWidget *label_stats_rev;
172 column_arrows *col_arrows_fwd;
173 column_arrows *col_arrows_rev;
175 GtkCList *selected_clist;
176 GtkWidget *save_voice_as_w;
177 GtkWidget *save_csv_as_w;
178 gint notebook_signal_id;
180 dialog_graph_t dialog_graph;
181 #ifdef USE_CONVERSATION_GRAPH
182 GtkWidget *graph_window;
186 #define OK_TEXT "[ Ok ]"
188 typedef struct _key_value {
194 /* RTP sampling clock rates for fixed payload types as defined in
195 http://www.iana.org/assignments/rtp-parameters */
196 static const key_value clock_map[] = {
202 {PT_DVI4_8000, 8000},
203 {PT_DVI4_16000, 16000},
207 {PT_L16_STEREO, 44100},
208 {PT_L16_MONO, 44100},
214 {PT_DVI4_11025, 11025},
215 {PT_DVI4_22050, 22050},
227 #define NUM_CLOCK_VALUES (sizeof clock_map / sizeof clock_map[0])
230 get_clock_rate(guint32 key)
234 for (i = 0; i < NUM_CLOCK_VALUES; i++) {
235 if (clock_map[i].key == key)
236 return clock_map[i].value;
242 /* type of error when saving voice in a file didn't succeed */
245 TAP_RTP_WRONG_LENGTH,
246 TAP_RTP_PADDING_ERROR,
248 TAP_RTP_FILE_OPEN_ERROR,
252 #if GTK_MAJOR_VERSION < 2
253 GtkRcStyle *rc_style;
254 GdkColormap *colormap;
257 typedef struct _tap_rtp_save_info_t {
260 error_type_t error_type;
262 } tap_rtp_save_info_t;
265 /* structure that holds the information about the forward and reversed direction */
266 struct _info_direction {
267 tap_rtp_stat_t statinfo;
268 tap_rtp_save_info_t saveinfo;
271 #define TMPNAMSIZE 100
273 /* structure that holds general information about the connection
274 * and structures for both directions */
275 typedef struct _user_data_t {
276 /* tap associated data*/
278 guint16 port_src_fwd;
280 guint16 port_dst_fwd;
283 guint16 port_src_rev;
285 guint16 port_dst_rev;
288 struct _info_direction forward;
289 struct _info_direction reversed;
291 char f_tempname[TMPNAMSIZE];
292 char r_tempname[TMPNAMSIZE];
294 /* dialog associated data */
297 #ifdef USE_CONVERSATION_GRAPH
298 time_series_t series_fwd;
299 time_series_t series_rev;
305 static const gchar *titles[9] = {
318 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_);
319 /****************************************************************************/
320 static void enable_graph(dialog_graph_graph_t *dgg)
327 static void dialog_graph_reset(user_data_t* user_data);
331 /****************************************************************************/
334 /****************************************************************************/
335 /* when there is a [re]reading of packet's */
337 rtp_reset(void *user_data_arg)
339 user_data_t *user_data = user_data_arg;
340 user_data->forward.statinfo.first_packet = TRUE;
341 user_data->reversed.statinfo.first_packet = TRUE;
342 user_data->forward.statinfo.max_delta = 0;
343 user_data->reversed.statinfo.max_delta = 0;
344 user_data->forward.statinfo.max_jitter = 0;
345 user_data->reversed.statinfo.max_jitter = 0;
346 user_data->forward.statinfo.mean_jitter = 0;
347 user_data->reversed.statinfo.mean_jitter = 0;
348 user_data->forward.statinfo.delta = 0;
349 user_data->reversed.statinfo.delta = 0;
350 user_data->forward.statinfo.diff = 0;
351 user_data->reversed.statinfo.diff = 0;
352 user_data->forward.statinfo.jitter = 0;
353 user_data->reversed.statinfo.jitter = 0;
354 user_data->forward.statinfo.bandwidth = 0;
355 user_data->reversed.statinfo.bandwidth = 0;
356 user_data->forward.statinfo.total_bytes = 0;
357 user_data->reversed.statinfo.total_bytes = 0;
358 user_data->forward.statinfo.bw_start_index = 0;
359 user_data->reversed.statinfo.bw_start_index = 0;
360 user_data->forward.statinfo.bw_index = 0;
361 user_data->reversed.statinfo.bw_index = 0;
362 user_data->forward.statinfo.timestamp = 0;
363 user_data->reversed.statinfo.timestamp = 0;
364 user_data->forward.statinfo.max_nr = 0;
365 user_data->reversed.statinfo.max_nr = 0;
366 user_data->forward.statinfo.total_nr = 0;
367 user_data->reversed.statinfo.total_nr = 0;
368 user_data->forward.statinfo.sequence = 0;
369 user_data->reversed.statinfo.sequence = 0;
370 user_data->forward.statinfo.start_seq_nr = 0;
371 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
372 user_data->forward.statinfo.stop_seq_nr = 0;
373 user_data->reversed.statinfo.stop_seq_nr = 0;
374 user_data->forward.statinfo.cycles = 0;
375 user_data->reversed.statinfo.cycles = 0;
376 user_data->forward.statinfo.under = FALSE;
377 user_data->reversed.statinfo.under = FALSE;
378 user_data->forward.statinfo.start_time = 0;
379 user_data->reversed.statinfo.start_time = 0;
380 user_data->forward.statinfo.time = 0;
381 user_data->reversed.statinfo.time = 0;
382 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
383 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
385 user_data->forward.saveinfo.count = 0;
386 user_data->reversed.saveinfo.count = 0;
387 user_data->forward.saveinfo.saved = FALSE;
388 user_data->reversed.saveinfo.saved = FALSE;
390 /* clear the dialog box clists */
391 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
392 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
394 /* reset graph info */
395 dialog_graph_reset(user_data);
397 #ifdef USE_CONVERSATION_GRAPH
398 if (user_data->dlg.graph_window != NULL)
399 window_destroy(user_data->dlg.graph_window);
401 g_array_free(user_data->series_fwd.value_pairs, TRUE);
402 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
404 g_array_free(user_data->series_rev.value_pairs, TRUE);
405 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
408 /* XXX check for error at fclose? */
409 if (user_data->forward.saveinfo.fp != NULL)
410 fclose(user_data->forward.saveinfo.fp);
411 if (user_data->reversed.saveinfo.fp != NULL)
412 fclose(user_data->reversed.saveinfo.fp);
413 user_data->forward.saveinfo.fp = fopen(user_data->f_tempname, "wb");
414 if (user_data->forward.saveinfo.fp == NULL)
415 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
416 user_data->reversed.saveinfo.fp = fopen(user_data->r_tempname, "wb");
417 if (user_data->reversed.saveinfo.fp == NULL)
418 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
422 /****************************************************************************/
423 static int rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
425 dialog_graph_graph_item_t *it;
429 /* we sometimes get called when dgg is disabled.
430 this is a bug since the tap listener should be removed first */
435 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
438 * Find which interval this is supposed to to in and store the
439 * interval index as idx
441 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
442 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
444 rtp_time = ((double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000) - dgg->ud->dlg.dialog_graph.start_time;
448 idx = (guint32)(rtp_time*1000)/dgg->ud->dlg.dialog_graph.interval;
450 /* some sanity checks */
451 if((idx<0)||(idx>=NUM_GRAPH_ITEMS)){
455 /* update num_items */
456 if((guint32)idx > dgg->ud->dlg.dialog_graph.num_items){
457 dgg->ud->dlg.dialog_graph.num_items=idx;
458 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
462 * Find the appropriate dialog_graph_graph_item_t structure
467 * Use the max value to highlight RTP problems
469 if (value > it->value) {
472 it->flags = it->flags | statinfo->flags;
477 /****************************************************************************/
478 /* here we can redraw the output */
480 static void rtp_draw(void *prs _U_)
485 /* forward declarations */
486 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
487 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
488 gchar *timeStr, guint32 pkt_len, GdkColor *color);
490 static int rtp_packet_add_info(GtkCList *clist,
491 tap_rtp_stat_t *statinfo, packet_info *pinfo,
492 const struct _rtp_info *rtpinfo);
494 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
495 tap_rtp_stat_t *statinfo,
497 const struct _rtp_info *rtpinfo);
500 /****************************************************************************/
501 /* whenever a RTP packet is seen by the tap listener */
502 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
504 user_data_t *user_data = user_data_arg;
505 const struct _rtp_info *rtpinfo = rtpinfo_arg;
506 #ifdef USE_CONVERSATION_GRAPH
509 /* we ignore packets that are not displayed */
510 if (pinfo->fd->flags.passed_dfilter == 0)
512 /* also ignore RTP Version != 2 */
513 else if (rtpinfo->info_version !=2)
515 /* is it the forward direction? */
516 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
517 && CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
518 && user_data->port_src_fwd == pinfo->srcport
519 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
520 && user_data->port_dst_fwd == pinfo->destport) {
521 #ifdef USE_CONVERSATION_GRAPH
522 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
523 vp.fnumber = pinfo->fd->num;
524 g_array_append_val(user_data->series_fwd.value_pairs, vp);
526 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
527 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));
528 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));
529 rtp_packet_add_info(user_data->dlg.clist_fwd,
530 &(user_data->forward.statinfo), pinfo, rtpinfo);
531 rtp_packet_save_payload(&(user_data->forward.saveinfo),
532 &(user_data->forward.statinfo), pinfo, rtpinfo);
534 /* is it the reversed direction? */
535 else if (user_data->ssrc_rev == rtpinfo->info_sync_src
536 && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
537 && user_data->port_src_rev == pinfo->srcport
538 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
539 && user_data->port_dst_rev == pinfo->destport) {
540 #ifdef USE_CONVERSATION_GRAPH
541 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
542 vp.fnumber = pinfo->fd->num;
543 g_array_append_val(user_data->series_rev.value_pairs, vp);
545 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
546 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));
547 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));
548 rtp_packet_add_info(user_data->dlg.clist_rev,
549 &(user_data->reversed.statinfo), pinfo, rtpinfo);
550 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
551 &(user_data->reversed.statinfo), pinfo, rtpinfo);
558 /****************************************************************************/
559 int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
561 const struct _rtp_info *rtpinfo)
564 double current_jitter;
569 /* check payload type */
570 if (rtpinfo->info_payload_type == PT_CN
571 || rtpinfo->info_payload_type == PT_CN_OLD)
572 statinfo->flags |= STAT_FLAG_PT_CN;
573 if (statinfo->pt == PT_CN
574 || statinfo->pt == PT_CN_OLD)
575 statinfo->flags |= STAT_FLAG_FOLLOW_PT_CN;
576 if (rtpinfo->info_payload_type != statinfo->pt)
577 statinfo->flags |= STAT_FLAG_PT_CHANGE;
578 statinfo->pt = rtpinfo->info_payload_type;
580 * XXX - should "get_clock_rate()" return 0 for unknown
581 * payload types, presumably meaning that we should
582 * just ignore this packet?
584 clock_rate = get_clock_rate(statinfo->pt);
586 /* store the current time and calculate the current jitter */
587 current_time = (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
588 current_diff = fabs (current_time - (statinfo->time) - ((double)(rtpinfo->info_timestamp)-(double)(statinfo->timestamp))/clock_rate);
589 current_jitter = statinfo->jitter + ( current_diff - statinfo->jitter)/16;
590 statinfo->delta = current_time-(statinfo->time);
591 statinfo->jitter = current_jitter;
592 statinfo->diff = current_diff;
594 /* calculate the BW in Kbps adding the IP+UDP header to the RTP -> 20bytes(IP)+8bytes(UDP) = 28bytes */
595 statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + 28;
596 statinfo->bw_history[statinfo->bw_index].time = current_time;
597 /* check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
598 while ((statinfo->bw_history[statinfo->bw_start_index].time+1)<current_time){
599 statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;
600 statinfo->bw_start_index++;
601 if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
603 statinfo->total_bytes += rtpinfo->info_data_len + 28;
604 statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
605 statinfo->bw_index++;
606 if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
609 /* is this the first packet we got in this direction? */
610 if (statinfo->first_packet) {
611 statinfo->start_seq_nr = rtpinfo->info_seq_num;
612 statinfo->start_time = current_time;
614 statinfo->jitter = 0;
616 statinfo->flags |= STAT_FLAG_FIRST;
617 statinfo->first_packet = FALSE;
618 statinfo->timestamp = rtpinfo->info_timestamp;
620 /* is it a packet with the mark bit set? */
621 if (rtpinfo->info_marker_set) {
622 if (rtpinfo->info_timestamp > statinfo->timestamp){
623 statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
624 statinfo->flags |= STAT_FLAG_MARKER;
626 else if (!(statinfo->flags & STAT_FLAG_FIRST)) {
627 statinfo->flags |= STAT_FLAG_WRONG_TIMESTAMP;
630 /* is it a regular packet? */
631 if (!(statinfo->flags & STAT_FLAG_FIRST)
632 && !(statinfo->flags & STAT_FLAG_MARKER)
633 && !(statinfo->flags & STAT_FLAG_PT_CN)
634 && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
635 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
636 /* include it in maximum delta calculation */
637 if (statinfo->delta > statinfo->max_delta) {
638 statinfo->max_delta = statinfo->delta;
639 statinfo->max_nr = pinfo->fd->num;
641 /* maximum and mean jitter calculation */
642 if (statinfo->jitter > statinfo->max_jitter) {
643 statinfo->max_jitter = statinfo->jitter;
645 statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1);
647 /* regular payload change? (CN ignored) */
648 if (!(statinfo->flags & STAT_FLAG_FIRST)
649 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
650 if ((statinfo->pt != statinfo->reg_pt)
651 && (statinfo->reg_pt != PT_UNDEFINED)) {
652 statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
656 /* set regular payload*/
657 if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
658 statinfo->reg_pt = statinfo->pt;
662 /* When calculating expected rtp packets the seq number can wrap around
663 * so we have to count the number of cycles
664 * Variable cycles counts the wraps around in forwarding connection and
665 * under is flag that indicates where we are
667 * XXX how to determine number of cycles with all possible lost, late
668 * and duplicated packets without any doubt? It seems to me, that
669 * because of all possible combination of late, duplicated or lost
670 * packets, this can only be more or less good approximation
672 * There are some combinations (rare but theoretically possible),
673 * where below code won't work correctly - statistic may be wrong then.
676 /* so if the current sequence number is less than the start one
677 * we assume, that there is another cycle running */
678 if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
680 statinfo->under = TRUE;
682 /* what if the start seq nr was 0? Then the above condition will never
683 * be true, so we add another condition. XXX The problem would arise
684 * if one of the packets with seq nr 0 or 65535 would be lost or late */
685 else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
686 (statinfo->under == FALSE)){
688 statinfo->under = TRUE;
690 /* the whole round is over, so reset the flag */
691 else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
692 statinfo->under = FALSE;
695 /* Since it is difficult to count lost, duplicate or late packets separately,
696 * we would like to know at least how many times the sequence number was not ok */
698 /* if the current seq number equals the last one or if we are here for
699 * the first time, then it is ok, we just store the current one as the last one */
700 if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
701 statinfo->seq_num = rtpinfo->info_seq_num;
702 /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
703 else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
704 statinfo->seq_num = rtpinfo->info_seq_num;
706 else if (statinfo->seq_num+1 < rtpinfo->info_seq_num) {
707 statinfo->seq_num = rtpinfo->info_seq_num;
708 statinfo->sequence++;
709 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
711 /* late or duplicated */
712 else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
713 statinfo->sequence++;
714 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
716 statinfo->time = current_time;
717 statinfo->timestamp = rtpinfo->info_timestamp;
718 statinfo->stop_seq_nr = rtpinfo->info_seq_num;
719 statinfo->total_nr++;
725 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
726 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
727 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
728 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
730 /****************************************************************************/
731 /* adds statistics information from the packet to the clist */
732 static int rtp_packet_add_info(GtkCList *clist,
733 tap_rtp_stat_t *statinfo, packet_info *pinfo,
734 const struct _rtp_info *rtpinfo)
741 GdkColor color = COLOR_DEFAULT;
742 then = pinfo->fd->abs_secs;
743 msecs = (guint16)(pinfo->fd->abs_usecs/1000);
744 tm_tmp = localtime(&then);
745 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
748 tm_tmp->tm_year + 1900,
754 if (statinfo->pt == PT_CN) {
755 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
758 else if (statinfo->pt == PT_CN_OLD) {
759 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
762 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
763 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
766 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
767 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
768 color = COLOR_WARNING;
770 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
771 g_snprintf(status,sizeof(status),"Incorrect timestamp");
772 color = COLOR_WARNING;
774 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
775 && !(statinfo->flags & STAT_FLAG_FIRST)
776 && !(statinfo->flags & STAT_FLAG_PT_CN)
777 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
778 && !(statinfo->flags & STAT_FLAG_MARKER)) {
779 g_snprintf(status,sizeof(status),"Marker missing?");
780 color = COLOR_WARNING;
783 if (statinfo->flags & STAT_FLAG_MARKER) {
784 color = COLOR_WARNING;
786 g_snprintf(status,sizeof(status),OK_TEXT);
788 /* is this the first packet we got in this direction? */
789 if (statinfo->flags & STAT_FLAG_FIRST) {
791 pinfo->fd->num, rtpinfo->info_seq_num,
796 rtpinfo->info_marker_set,
797 timeStr, pinfo->fd->pkt_len,
802 pinfo->fd->num, rtpinfo->info_seq_num,
803 statinfo->delta*1000,
804 statinfo->jitter*1000,
807 rtpinfo->info_marker_set,
808 timeStr, pinfo->fd->pkt_len,
815 /****************************************************************************/
816 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
817 tap_rtp_stat_t *statinfo,
819 const struct _rtp_info *rtpinfo)
825 /* is this the first packet we got in this direction? */
826 if (statinfo->flags & STAT_FLAG_FIRST) {
827 if (saveinfo->fp == NULL) {
828 saveinfo->saved = FALSE;
829 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
832 saveinfo->saved = TRUE;
835 /* save the voice information */
836 /* if there was already an error, we quit */
837 if (saveinfo->saved == FALSE)
840 /* if the captured length and packet length aren't equal, we quit
841 * because there is some information missing */
842 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
843 saveinfo->saved = FALSE;
844 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
848 /* if padding bit is set, but the padding count is bigger
849 * then the whole RTP data - error with padding count */
850 if ( (rtpinfo->info_padding_set != FALSE) &&
851 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
852 saveinfo->saved = FALSE;
853 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
857 /* do we need to insert some silence? */
858 if ((rtpinfo->info_marker_set) &&
859 !(statinfo->flags & STAT_FLAG_FIRST) &&
860 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
861 /* the amount of silence should be the difference between
862 * the last timestamp and the current one minus x
863 * x should equal the amount of information in the last frame
864 * XXX not done yet */
865 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
866 rtpinfo->info_padding_count); i++) {
867 tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
868 fwrite(&tmp, 2, 1, saveinfo->fp);
871 fflush(saveinfo->fp);
875 if (rtpinfo->info_payload_type == PT_PCMU) {
876 if (!rtpinfo->info_all_data_present) {
877 /* Not all the data was captured. */
878 saveinfo->saved = FALSE;
879 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
883 /* we put the pointer at the beginning of the RTP
884 * payload, that is, at the beginning of the RTP data
885 * plus the offset of the payload from the beginning
887 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
888 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
889 tmp = (gint16 )ulaw2linear((unsigned char)*data);
890 fwrite(&tmp, 2, 1, saveinfo->fp);
893 fflush(saveinfo->fp);
894 saveinfo->saved = TRUE;
899 else if (rtpinfo->info_payload_type == PT_PCMA) {
900 if (!rtpinfo->info_all_data_present) {
901 /* Not all the data was captured. */
902 saveinfo->saved = FALSE;
903 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
907 /* we put the pointer at the beginning of the RTP
908 * payload, that is, at the beginning of the RTP data
909 * plus the offset of the payload from the beginning
911 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
912 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
913 tmp = (gint16 )alaw2linear((unsigned char)*data);
914 fwrite(&tmp, 2, 1, saveinfo->fp);
917 fflush(saveinfo->fp);
918 saveinfo->saved = TRUE;
921 /* comfort noise? - do nothing */
922 else if (rtpinfo->info_payload_type == PT_CN
923 || rtpinfo->info_payload_type == PT_CN_OLD) {
925 /* unsupported codec or XXX other error */
927 saveinfo->saved = FALSE;
928 saveinfo->error_type = TAP_RTP_WRONG_CODEC;
936 /****************************************************************************/
939 /****************************************************************************/
940 /* XXX just copied from gtk/rpc_stat.c */
941 void protect_thread_critical_region(void);
942 void unprotect_thread_critical_region(void);
945 /****************************************************************************/
946 /* close the dialog window and remove the tap listener */
947 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
949 /* remove tap listener */
950 protect_thread_critical_region();
951 remove_tap_listener(user_data);
952 unprotect_thread_critical_region();
954 /* close and remove temporary files */
955 if (user_data->forward.saveinfo.fp != NULL)
956 fclose(user_data->forward.saveinfo.fp);
957 if (user_data->reversed.saveinfo.fp != NULL)
958 fclose(user_data->reversed.saveinfo.fp);
959 /*XXX: test for error **/
960 remove(user_data->f_tempname);
961 remove(user_data->r_tempname);
963 /* destroy save_voice_as window if open */
964 if (user_data->dlg.save_voice_as_w != NULL)
965 window_destroy(user_data->dlg.save_voice_as_w);
967 /* destroy graph window if open */
968 if (user_data->dlg.dialog_graph.window != NULL)
969 window_destroy(user_data->dlg.dialog_graph.window);
971 #ifdef USE_CONVERSATION_GRAPH
972 /* destroy graph window if open */
973 if (user_data->dlg.graph_window != NULL)
974 window_destroy(user_data->dlg.graph_window);
977 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exeption using GTK1*/
978 gtk_signal_disconnect(GTK_OBJECT(user_data->dlg.notebook), user_data->dlg.notebook_signal_id);
980 g_free(user_data->dlg.col_arrows_fwd);
981 g_free(user_data->dlg.col_arrows_rev);
986 /****************************************************************************/
987 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
988 GtkNotebookPage *page _U_,
990 user_data_t *user_data _U_)
992 user_data->dlg.selected_clist =
993 (page_num==0) ? user_data->dlg.clist_fwd : user_data->dlg.clist_rev ;
994 user_data->dlg.selected_row = 0;
997 /****************************************************************************/
998 static void on_clist_select_row(GtkCList *clist _U_,
1001 GdkEvent *event _U_,
1002 user_data_t *user_data _U_)
1004 user_data->dlg.selected_clist = clist;
1005 user_data->dlg.selected_row = row;
1009 #ifdef USE_CONVERSATION_GRAPH
1010 /****************************************************************************/
1011 /* when the graph window gets destroyed */
1012 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
1014 /* note that graph window has been destroyed */
1015 user_data->dlg.graph_window = NULL;
1018 /****************************************************************************/
1019 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
1022 GtkCList *clist = NULL;
1023 if (vp.fnumber != 0) {
1024 clist = GTK_CLIST(user_data->dlg.clist_fwd);
1025 row = gtk_clist_find_row_from_data(clist,
1026 GUINT_TO_POINTER(vp.fnumber));
1028 clist = GTK_CLIST(user_data->dlg.clist_rev);
1029 row = gtk_clist_find_row_from_data(clist,
1030 GUINT_TO_POINTER(vp.fnumber));
1033 gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook),
1034 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
1035 gtk_clist_select_row(clist, row, 0);
1036 gtk_clist_moveto(clist, row, 0, 0.5, 0);
1042 /****************************************************************************/
1043 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1049 if (user_data->dlg.graph_window != NULL) {
1050 /* There's already a graph window; reactivate it. */
1051 reactivate_window(user_data->dlg.graph_window);
1054 list = g_list_append(list, &(user_data->series_fwd));
1055 list = g_list_append(list, &(user_data->series_rev));
1057 user_data->series_fwd.color.pixel = 0;
1058 user_data->series_fwd.color.red = 0x80ff;
1059 user_data->series_fwd.color.green = 0xe0ff;
1060 user_data->series_fwd.color.blue = 0xffff;
1061 user_data->series_fwd.yvalue = 0.5;
1063 user_data->series_rev.color.pixel = 0;
1064 user_data->series_rev.color.red = 0x60ff;
1065 user_data->series_rev.color.green = 0xc0ff;
1066 user_data->series_rev.color.blue = 0xffff;
1067 user_data->series_rev.yvalue = -0.5;
1069 g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=%u)",
1070 get_addr_name(&(user_data->ip_src_fwd)),
1071 user_data->port_src_fwd,
1072 get_addr_name(&(user_data->ip_dst_fwd)),
1073 user_data->port_dst_fwd,
1074 user_data->ssrc_fwd);
1076 g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=%u)",
1077 get_addr_name(&(user_data->ip_src_rev)),
1078 user_data->port_src_rev,
1079 get_addr_name(&(user_data->ip_dst_rev)),
1080 user_data->port_dst_rev,
1081 user_data->ssrc_rev);
1083 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
1084 &graph_selection_callback, user_data);
1085 SIGNAL_CONNECT(user_data->dlg.graph_window, "destroy",
1086 on_destroy_graph, user_data);
1088 #endif /*USE_CONVERSATION_GRAPH*/
1090 /****************************************************************************/
1091 static void dialog_graph_set_title(user_data_t* user_data)
1094 if (!user_data->dlg.dialog_graph.window){
1097 title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
1098 get_addr_name(&(user_data->ip_src_fwd)),
1099 user_data->port_src_fwd,
1100 get_addr_name(&(user_data->ip_dst_fwd)),
1101 user_data->port_dst_fwd,
1102 get_addr_name(&(user_data->ip_src_rev)),
1103 user_data->port_src_rev,
1104 get_addr_name(&(user_data->ip_dst_rev)),
1105 user_data->port_dst_rev);
1107 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
1113 /****************************************************************************/
1114 static void dialog_graph_reset(user_data_t* user_data)
1118 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1119 for(i=0;i<MAX_GRAPHS;i++){
1120 for(j=0;j<NUM_GRAPH_ITEMS;j++){
1121 dialog_graph_graph_item_t *dggi;
1122 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
1127 user_data->dlg.dialog_graph.last_interval=0xffffffff;
1128 user_data->dlg.dialog_graph.max_interval=0;
1129 user_data->dlg.dialog_graph.num_items=0;
1131 /* create the color titles near the filter buttons */
1132 for(i=0;i<MAX_GRAPHS;i++){
1135 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=%u)",
1137 get_addr_name(&(user_data->ip_src_fwd)),
1138 user_data->port_src_fwd,
1139 get_addr_name(&(user_data->ip_dst_fwd)),
1140 user_data->port_dst_fwd,
1141 user_data->ssrc_fwd);
1144 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=%u)",
1146 get_addr_name(&(user_data->ip_src_rev)),
1147 user_data->port_src_rev,
1148 get_addr_name(&(user_data->ip_dst_rev)),
1149 user_data->port_dst_rev,
1150 user_data->ssrc_rev);
1154 dialog_graph_set_title(user_data);
1157 /****************************************************************************/
1158 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
1160 dialog_graph_graph_item_t *it;
1162 it=&dgg->items[idx];
1167 /****************************************************************************/
1168 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
1171 g_snprintf(buf, buf_len, "%ds",t/1000000);
1172 } else if(t>=1000000){
1173 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
1174 } else if(t>=10000){
1175 g_snprintf(buf, buf_len, "%dms",t/1000);
1177 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
1179 g_snprintf(buf, buf_len, "%dus",t);
1183 /****************************************************************************/
1184 static void dialog_graph_draw(user_data_t* user_data)
1187 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
1188 gint32 current_interval;
1189 guint32 left_x_border;
1190 guint32 right_x_border;
1191 guint32 top_y_border;
1192 guint32 bottom_y_border;
1193 #if GTK_MAJOR_VERSION < 2
1196 PangoLayout *layout;
1198 guint32 label_width, label_height;
1199 guint32 draw_width, draw_height;
1200 char label_string[15];
1203 guint32 num_time_intervals;
1204 guint32 max_value; /* max value of seen data */
1205 guint32 max_y; /* max value of the Y scale */
1207 #if GTK_MAJOR_VERSION <2
1208 font = user_data->dlg.dialog_graph.draw_area->style->font;
1210 if(!user_data->dlg.dialog_graph.needs_redraw){
1213 user_data->dlg.dialog_graph.needs_redraw=FALSE;
1216 * Find the length of the intervals we have data for
1217 * so we know how large arrays we need to malloc()
1219 num_time_intervals=user_data->dlg.dialog_graph.num_items;
1220 /* if there isnt anything to do, just return */
1221 if(num_time_intervals==0){
1224 num_time_intervals+=1;
1225 /* XXX move this check to _packet() */
1226 if(num_time_intervals>NUM_GRAPH_ITEMS){
1227 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
1232 * find the max value so we can autoscale the y axis
1235 for(i=0;i<MAX_GRAPHS;i++){
1238 if(!user_data->dlg.dialog_graph.graph[i].display){
1241 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
1244 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1246 /* keep track of the max value we have encountered */
1254 * Clear out old plot
1256 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1257 user_data->dlg.dialog_graph.draw_area->style->white_gc,
1260 user_data->dlg.dialog_graph.draw_area->allocation.width,
1261 user_data->dlg.dialog_graph.draw_area->allocation.height);
1265 * Calculate the y scale we should use
1267 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1268 max_y=yscale_max[MAX_YSCALE-1];
1269 for(i=MAX_YSCALE-1;i>0;i--){
1270 if(max_value<yscale_max[i]){
1271 max_y=yscale_max[i];
1275 /* the user had specified an explicit y scale to use */
1276 max_y=user_data->dlg.dialog_graph.max_y_units;
1280 * Calculate size of borders surrounding the plot
1281 * The border on the right side needs to be adjusted depending
1282 * on the width of the text labels. For simplicity we assume that the
1283 * top y scale label will be the widest one
1285 print_time_scale_string(label_string, 15, max_y);
1286 #if GTK_MAJOR_VERSION < 2
1287 label_width=gdk_string_width(font, label_string);
1288 label_height=gdk_string_height(font, label_string);
1290 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1291 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1294 right_x_border=label_width+20;
1296 bottom_y_border=label_height+20;
1300 * Calculate the size of the drawing area for the actual plot
1302 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1303 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1307 * Draw the y axis and labels
1308 * (we always draw the y scale with 11 ticks along the axis)
1310 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1311 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1313 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1314 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1320 /* first, middle and last tick are slightly longer */
1324 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1325 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1326 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1327 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1328 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1329 /* draw the labels */
1331 print_time_scale_string(label_string, 15, (max_y*i/10));
1332 #if GTK_MAJOR_VERSION < 2
1333 lwidth=gdk_string_width(font, label_string);
1334 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1336 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1337 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1338 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1341 pango_layout_set_text(layout, label_string, -1);
1342 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1343 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1344 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1345 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1346 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1351 print_time_scale_string(label_string, 15, (max_y*i/10));
1352 #if GTK_MAJOR_VERSION < 2
1353 lwidth=gdk_string_width(font, label_string);
1354 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1356 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1357 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1358 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1361 pango_layout_set_text(layout, label_string, -1);
1362 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1363 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1364 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1365 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1366 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1371 print_time_scale_string(label_string, 15, (max_y*i/10));
1372 #if GTK_MAJOR_VERSION < 2
1373 lwidth=gdk_string_width(font, label_string);
1374 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1376 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1377 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1378 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1381 pango_layout_set_text(layout, label_string, -1);
1382 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1383 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1384 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1385 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1386 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1395 * if we have not specified the last_interval via the gui,
1396 * then just pick the current end of the capture so that is scrolls
1397 * nicely when doing live captures
1399 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1400 last_interval=user_data->dlg.dialog_graph.max_interval;
1402 last_interval=user_data->dlg.dialog_graph.last_interval;
1409 /* plot the x-scale */
1410 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);
1412 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1413 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1414 first_interval*=user_data->dlg.dialog_graph.interval;
1421 while(interval_delta<((last_interval-first_interval)/10)){
1422 interval_delta*=delta_multiplier;
1423 if(delta_multiplier==5){
1430 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1433 /* if pixels_per_tick is <5, only draw every 10 ticks */
1434 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1438 if(current_interval%interval_delta){
1444 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1445 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1446 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1447 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1448 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1449 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1453 if(user_data->dlg.dialog_graph.interval>=1000){
1454 g_snprintf(label_string, 15, "%ds", current_interval/1000);
1455 } else if(user_data->dlg.dialog_graph.interval>=100){
1456 g_snprintf(label_string, 15, "%d.%1ds", current_interval/1000,(current_interval/100)%10)
1458 } else if(user_data->dlg.dialog_graph.interval>=10){
1459 g_snprintf(label_string, 15, "%d.%2ds", current_interval/1000,(current_interval/10)%100)
1462 g_snprintf(label_string, 15, "%d.%3ds", current_interval/1000,current_interval%1000);
1464 #if GTK_MAJOR_VERSION < 2
1465 lwidth=gdk_string_width(font, label_string);
1466 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1468 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1469 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1470 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20+label_height,
1473 pango_layout_set_text(layout, label_string, -1);
1474 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1475 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1476 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1477 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1478 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1491 * Draw "x" for Sequence Errors and "m" for Marks
1493 /* Draw the labels Fwd and Rev */
1494 strcpy(label_string,"<-Fwd");
1495 #if GTK_MAJOR_VERSION < 2
1496 lwidth=gdk_string_width(font, label_string);
1497 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1499 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1500 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1501 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+label_height,
1504 pango_layout_set_text(layout, label_string, -1);
1505 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1506 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1507 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1508 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1509 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1512 strcpy(label_string,"<-Rev");
1513 #if GTK_MAJOR_VERSION < 2
1514 lwidth=gdk_string_width(font, label_string);
1515 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1517 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1518 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1519 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9+label_height,
1522 pango_layout_set_text(layout, label_string, -1);
1523 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1524 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1525 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1526 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1527 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1531 /* Draw the marks */
1532 for(i=MAX_GRAPHS-1;i>=0;i--){
1534 guint32 x_pos, prev_x_pos;
1536 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1537 if (!user_data->dlg.dialog_graph.graph[i].display){
1540 /* initialize prev x/y to the low left corner of the graph */
1541 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;
1543 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1544 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;
1546 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1548 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1549 strcpy(label_string,"x");
1551 strcpy(label_string,"m");
1554 #if GTK_MAJOR_VERSION < 2
1555 lwidth=gdk_string_width(font, label_string);
1556 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1558 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1560 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2)+label_height,
1563 pango_layout_set_text(layout, label_string, -1);
1564 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1565 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1566 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1568 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1577 #if GTK_MAJOR_VERSION >= 2
1578 g_object_unref(G_OBJECT(layout));
1583 * Loop over all graphs and draw them
1585 for(i=MAX_GRAPHS-1;i>=0;i--){
1587 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1588 if (!user_data->dlg.dialog_graph.graph[i].display){
1591 /* initialize prev x/y to the low left corner of the graph */
1592 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;
1593 prev_y_pos=draw_height-1+top_y_border;
1595 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1597 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;
1598 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1602 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1605 /* dont need to draw anything if the segment
1606 * is entirely above the top of the graph
1608 if( (prev_y_pos==0) && (y_pos==0) ){
1615 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1616 x_pos, draw_height-1+top_y_border,
1626 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1627 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1628 user_data->dlg.dialog_graph.pixmap,
1631 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1634 /* update the scrollbar */
1635 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1636 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1637 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1638 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1639 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1641 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1643 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1644 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1645 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1649 /****************************************************************************/
1650 static void dialog_graph_redraw(user_data_t* user_data)
1652 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1653 dialog_graph_draw(user_data);
1656 /****************************************************************************/
1657 static gint quit(GtkWidget *widget, GdkEventExpose *event _U_)
1659 user_data_t *user_data;
1661 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1663 user_data->dlg.dialog_graph.window = NULL;
1667 /****************************************************************************/
1668 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1670 user_data_t *user_data;
1672 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1678 gdk_draw_pixmap(widget->window,
1679 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1680 user_data->dlg.dialog_graph.pixmap,
1681 event->area.x, event->area.y,
1682 event->area.x, event->area.y,
1683 event->area.width, event->area.height);
1688 /****************************************************************************/
1689 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1691 user_data_t *user_data;
1694 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1700 if(user_data->dlg.dialog_graph.pixmap){
1701 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1702 user_data->dlg.dialog_graph.pixmap=NULL;
1705 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1706 widget->allocation.width,
1707 widget->allocation.height,
1709 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1710 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1712 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1713 widget->style->white_gc,
1716 widget->allocation.width,
1717 widget->allocation.height);
1719 /* set up the colors and the GC structs for this pixmap */
1720 for(i=0;i<MAX_GRAPHS;i++){
1721 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1722 #if GTK_MAJOR_VERSION < 2
1723 colormap = gtk_widget_get_colormap (widget);
1724 if (!gdk_color_alloc (colormap, &user_data->dlg.dialog_graph.graph[i].color)){
1725 g_warning ("Couldn't allocate color");
1728 gdk_gc_set_foreground(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1730 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1734 dialog_graph_redraw(user_data);
1738 /****************************************************************************/
1739 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1741 user_data_t *user_data=(user_data_t *)data;
1744 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1745 if(user_data->dlg.dialog_graph.last_interval==mi){
1748 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1749 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1753 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1755 dialog_graph_redraw(user_data);
1759 /****************************************************************************/
1760 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1762 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1763 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "destroy", quit, user_data);
1764 OBJECT_SET_DATA(user_data->dlg.dialog_graph.draw_area, "user_data_t", user_data);
1766 WIDGET_SET_SIZE(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1768 /* signals needed to handle backing pixmap */
1769 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "expose_event", expose_event, NULL);
1770 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "configure_event", configure_event, user_data);
1772 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1773 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1775 /* create the associated scrollbar */
1776 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1777 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1778 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1779 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1780 SIGNAL_CONNECT(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", scrollbar_changed, user_data);
1783 /****************************************************************************/
1784 static void disable_graph(dialog_graph_graph_t *dgg)
1788 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1793 /****************************************************************************/
1794 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1796 /* this graph is not active, just update display and redraw */
1797 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1799 dialog_graph_redraw(dgg->ud);
1804 cf_retap_packets(&cfile);
1805 dialog_graph_redraw(dgg->ud);
1810 /****************************************************************************/
1811 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1817 hbox=gtk_hbox_new(FALSE, 3);
1818 gtk_container_add(GTK_CONTAINER(box), hbox);
1819 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1820 gtk_widget_show(hbox);
1822 g_snprintf(str, 256, "Graph %d", num);
1823 dgg->display_button=gtk_toggle_button_new_with_label(str);
1824 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1825 gtk_widget_show(dgg->display_button);
1826 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1827 SIGNAL_CONNECT(dgg->display_button, "toggled", filter_callback, dgg);
1829 label=gtk_label_new(dgg->title);
1830 gtk_widget_show(label);
1831 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1833 #if GTK_MAJOR_VERSION < 2
1834 /* setting the color of the display button doesn't work */
1835 rc_style = gtk_rc_style_new ();
1836 rc_style->fg[GTK_STATE_NORMAL] = dgg->color;
1837 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG;
1838 rc_style->fg[GTK_STATE_ACTIVE] = dgg->color;
1839 rc_style->color_flags[GTK_STATE_ACTIVE] |= GTK_RC_FG;
1840 rc_style->fg[GTK_STATE_PRELIGHT] = dgg->color;
1841 rc_style->color_flags[GTK_STATE_PRELIGHT] |= GTK_RC_FG;
1842 rc_style->fg[GTK_STATE_SELECTED] = dgg->color;
1843 rc_style->color_flags[GTK_STATE_SELECTED] |= GTK_RC_FG;
1844 rc_style->fg[GTK_STATE_INSENSITIVE] = dgg->color;
1845 rc_style->color_flags[GTK_STATE_INSENSITIVE] |= GTK_RC_FG;
1846 gtk_widget_modify_style (label, rc_style);
1847 gtk_rc_style_unref (rc_style);
1849 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1850 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1851 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1852 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1853 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1859 /****************************************************************************/
1860 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1867 frame=gtk_frame_new("Graphs");
1868 gtk_container_add(GTK_CONTAINER(box), frame);
1869 gtk_widget_show(frame);
1871 vbox=gtk_vbox_new(FALSE, 1);
1872 gtk_container_add(GTK_CONTAINER(frame), vbox);
1873 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
1874 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1875 gtk_widget_show(vbox);
1877 for(i=0;i<MAX_GRAPHS;i++){
1878 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1881 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1882 gtk_widget_show(label);
1883 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1888 /****************************************************************************/
1889 static void yscale_select(GtkWidget *item, gpointer key)
1892 user_data_t *user_data;
1894 user_data=(user_data_t *)key;
1895 val=(int)OBJECT_GET_DATA(item, "yscale_max");
1897 user_data->dlg.dialog_graph.max_y_units=val;
1898 dialog_graph_redraw(user_data);
1901 /****************************************************************************/
1902 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1905 user_data_t *user_data;
1907 user_data=(user_data_t *)key;
1908 val=(int)OBJECT_GET_DATA(item, "pixels_per_tick");
1909 user_data->dlg.dialog_graph.pixels_per_tick=val;
1910 dialog_graph_redraw(user_data);
1913 /****************************************************************************/
1914 static void tick_interval_select(GtkWidget *item, gpointer key)
1917 user_data_t *user_data;
1919 user_data=(user_data_t *)key;
1920 val=(int)OBJECT_GET_DATA(item, "tick_interval");
1922 user_data->dlg.dialog_graph.interval=val;
1923 cf_retap_packets(&cfile);
1924 dialog_graph_redraw(user_data);
1927 /****************************************************************************/
1928 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1931 GtkWidget *menu_item;
1934 for(i=0;i<MAX_YSCALE;i++){
1935 if(yscale_max[i]==AUTO_MAX_YSCALE){
1938 g_snprintf(str, 15, "%u ms", yscale_max[i]/1000);
1940 menu_item=gtk_menu_item_new_with_label(str);
1941 OBJECT_SET_DATA(menu_item, "yscale_max",
1942 GUINT_TO_POINTER(yscale_max[i]));
1943 SIGNAL_CONNECT(menu_item, "activate", yscale_select, user_data);
1944 gtk_widget_show(menu_item);
1945 gtk_menu_append(GTK_MENU(menu), menu_item);
1950 /****************************************************************************/
1951 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
1954 GtkWidget *menu_item;
1957 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1958 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1959 menu_item=gtk_menu_item_new_with_label(str);
1961 OBJECT_SET_DATA(menu_item, "pixels_per_tick",
1962 GUINT_TO_POINTER(pixels_per_tick[i]));
1963 SIGNAL_CONNECT(menu_item, "activate", pixels_per_tick_select, user_data);
1964 gtk_widget_show(menu_item);
1965 gtk_menu_append(GTK_MENU(menu), menu_item);
1967 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1972 /****************************************************************************/
1973 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
1976 GtkWidget *menu_item;
1979 for(i=0;i<MAX_TICK_VALUES;i++){
1980 if(tick_interval_values[i]>=1000){
1981 g_snprintf(str, 15, "%u sec", tick_interval_values[i]/1000);
1982 } else if(tick_interval_values[i]>=100){
1983 g_snprintf(str, 15, "0.%1u sec", (tick_interval_values[i]/100)%10);
1984 } else if(tick_interval_values[i]>=10){
1985 g_snprintf(str, 15, "0.%02u sec", (tick_interval_values[i]/10)%10);
1987 g_snprintf(str, 15, "0.%03u sec", (tick_interval_values[i])%10);
1990 menu_item=gtk_menu_item_new_with_label(str);
1991 OBJECT_SET_DATA(menu_item, "tick_interval",
1992 GUINT_TO_POINTER(tick_interval_values[i]));
1993 SIGNAL_CONNECT(menu_item, "activate", tick_interval_select, (gpointer)user_data);
1994 gtk_widget_show(menu_item);
1995 gtk_menu_append(GTK_MENU(menu), menu_item);
1997 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
2001 /****************************************************************************/
2002 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
2006 GtkWidget *option_menu;
2009 hbox=gtk_hbox_new(FALSE, 0);
2010 gtk_container_add(GTK_CONTAINER(box), hbox);
2011 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2012 gtk_widget_show(hbox);
2014 label=gtk_label_new(name);
2015 gtk_widget_show(label);
2016 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2018 option_menu=gtk_option_menu_new();
2019 menu=gtk_menu_new();
2020 (*func)(user_data, menu);
2021 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
2022 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
2023 gtk_widget_show(option_menu);
2026 /****************************************************************************/
2027 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
2029 GtkWidget *frame_vbox;
2033 frame_vbox=gtk_vbox_new(FALSE, 0);
2034 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
2035 gtk_widget_show(frame_vbox);
2037 frame = gtk_frame_new("X Axis");
2038 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
2039 gtk_widget_show(frame);
2041 vbox=gtk_vbox_new(FALSE, 0);
2042 gtk_container_add(GTK_CONTAINER(frame), vbox);
2043 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
2044 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
2045 gtk_widget_show(vbox);
2047 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
2048 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
2050 frame = gtk_frame_new("Y Axis");
2051 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
2052 gtk_widget_show(frame);
2054 vbox=gtk_vbox_new(FALSE, 0);
2055 gtk_container_add(GTK_CONTAINER(frame), vbox);
2056 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
2057 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
2058 gtk_widget_show(vbox);
2060 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
2065 /****************************************************************************/
2066 static void dialog_graph_init_window(user_data_t* user_data)
2070 GtkWidget *bt_close;
2072 /* create the main window */
2073 user_data->dlg.dialog_graph.window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
2075 vbox=gtk_vbox_new(FALSE, 0);
2076 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
2077 gtk_widget_show(vbox);
2079 create_draw_area(user_data, vbox);
2081 hbox=gtk_hbox_new(FALSE, 3);
2082 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2083 gtk_container_border_width(GTK_CONTAINER(hbox), 3);
2084 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2085 gtk_widget_show(hbox);
2087 create_filter_area(user_data, hbox);
2088 create_ctrl_area(user_data, hbox);
2090 dialog_graph_set_title(user_data);
2092 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
2093 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2094 gtk_widget_show(hbox);
2096 bt_close = OBJECT_GET_DATA(hbox, GTK_STOCK_CLOSE);
2097 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
2099 SIGNAL_CONNECT(user_data->dlg.dialog_graph.window, "delete_event", window_delete_event_cb, NULL);
2101 gtk_widget_show(user_data->dlg.dialog_graph.window);
2102 window_present(user_data->dlg.dialog_graph.window);
2107 /****************************************************************************/
2108 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2110 if (user_data->dlg.dialog_graph.window != NULL) {
2111 /* There's already a graph window; reactivate it. */
2112 reactivate_window(user_data->dlg.dialog_graph.window);
2116 dialog_graph_init_window(user_data);
2120 /****************************************************************************/
2121 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2125 if (user_data->dlg.selected_clist!=NULL) {
2126 fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
2127 GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
2128 cf_goto_frame(&cfile, fnumber);
2133 static void draw_stat(user_data_t *user_data);
2135 /****************************************************************************/
2136 /* re-dissects all packets */
2137 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2139 GString *error_string;
2141 /* remove tap listener */
2142 protect_thread_critical_region();
2143 remove_tap_listener(user_data);
2144 unprotect_thread_critical_region();
2146 /* register tap listener */
2147 error_string = register_tap_listener("rtp", user_data, NULL,
2148 rtp_reset, rtp_packet, rtp_draw);
2149 if (error_string != NULL) {
2150 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
2151 g_string_free(error_string, TRUE);
2155 /* retap all packets */
2156 cf_retap_packets(&cfile);
2158 /* draw statistics info */
2159 draw_stat(user_data);
2161 gtk_clist_sort(user_data->dlg.clist_fwd);
2162 gtk_clist_sort(user_data->dlg.clist_rev);
2165 /****************************************************************************/
2166 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2171 if (user_data->dlg.selected_clist==NULL)
2174 clist = user_data->dlg.selected_clist;
2175 row = user_data->dlg.selected_row + 1;
2177 while (gtk_clist_get_text(clist,row,6,&text)) {
2178 if (strcmp(text, OK_TEXT) != 0) {
2179 gtk_clist_select_row(clist, row, 0);
2180 gtk_clist_moveto(clist, row, 0, 0.5, 0);
2188 while (gtk_clist_get_text(clist,row,6,&text) && row<user_data->dlg.selected_row) {
2189 if (strcmp(text, OK_TEXT) != 0) {
2190 gtk_clist_select_row(clist, row, 0);
2191 gtk_clist_moveto(clist, row, 0, 0.5, 0);
2198 /****************************************************************************/
2199 /* when we want to save the information */
2200 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
2203 GtkWidget *rev, *forw, *both;
2204 user_data_t *user_data;
2210 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
2212 /* Perhaps the user specified a directory instead of a file.
2213 Check whether they did. */
2214 if (test_for_directory(g_dest) == EISDIR) {
2215 /* It's a directory - set the file selection box to display it. */
2216 set_last_open_dir(g_dest);
2218 file_selection_set_current_folder(fs, get_last_open_dir());
2222 rev = (GtkWidget*)OBJECT_GET_DATA(bt, "reversed_rb");
2223 forw = (GtkWidget*)OBJECT_GET_DATA(bt, "forward_rb");
2224 both = (GtkWidget*)OBJECT_GET_DATA(bt, "both_rb");
2225 user_data = (user_data_t*)OBJECT_GET_DATA(bt, "user_data");
2227 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
2228 fp = fopen(g_dest, "w");
2230 open_failure_alert_box(g_dest, errno, TRUE);
2234 if (GTK_TOGGLE_BUTTON(both)->active) {
2235 fprintf(fp, "Forward\n");
2237 write_failure_alert_box(g_dest, errno);
2243 for(j = 0; j < NUM_COLS; j++) {
2245 fprintf(fp,"%s",titles[j]);
2247 fprintf(fp,",%s",titles[j]);
2252 write_failure_alert_box(g_dest, errno);
2256 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
2257 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
2258 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
2260 fprintf(fp,"%s",columnText);
2262 fprintf(fp,",%s",columnText);
2267 write_failure_alert_box(g_dest, errno);
2273 if (fclose(fp) == EOF) {
2274 write_failure_alert_box(g_dest, errno);
2279 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
2281 if (GTK_TOGGLE_BUTTON(both)->active) {
2282 fp = fopen(g_dest, "a");
2284 open_failure_alert_box(g_dest, errno, TRUE);
2287 fprintf(fp, "\nReverse\n");
2289 write_failure_alert_box(g_dest, errno);
2294 fp = fopen(g_dest, "w");
2296 open_failure_alert_box(g_dest, errno, TRUE);
2300 for(j = 0; j < NUM_COLS; j++) {
2302 fprintf(fp,"%s",titles[j]);
2304 fprintf(fp,",%s",titles[j]);
2309 write_failure_alert_box(g_dest, errno);
2313 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
2314 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
2315 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
2317 fprintf(fp,"%s",columnText);
2319 fprintf(fp,",%s",columnText);
2324 write_failure_alert_box(g_dest, errno);
2329 if (fclose(fp) == EOF) {
2330 write_failure_alert_box(g_dest, errno);
2335 window_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
2338 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2340 user_data->dlg.save_csv_as_w = NULL;
2343 /* when the user wants to save the csv information in a file */
2344 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
2348 GtkWidget *label_format;
2349 GtkWidget *channels_label;
2350 GSList *channels_group = NULL;
2351 GtkWidget *forward_rb;
2352 GtkWidget *reversed_rb;
2356 if (user_data->dlg.save_csv_as_w != NULL) {
2357 /* There's already a Save CSV info dialog box; reactivate it. */
2358 reactivate_window(user_data->dlg.save_csv_as_w);
2362 user_data->dlg.save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV");
2364 /* Container for each row of widgets */
2365 vertb = gtk_vbox_new(FALSE, 0);
2366 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
2367 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
2368 vertb, FALSE, FALSE, 0);
2369 gtk_widget_show (vertb);
2371 table1 = gtk_table_new (2, 4, FALSE);
2372 gtk_widget_show (table1);
2373 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2374 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2375 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2377 label_format = gtk_label_new ("Format: Comma Separated Values");
2378 gtk_widget_show (label_format);
2379 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2380 (GtkAttachOptions) (GTK_FILL),
2381 (GtkAttachOptions) (0), 0, 0);
2384 channels_label = gtk_label_new ("Channels:");
2385 gtk_widget_show (channels_label);
2386 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2387 (GtkAttachOptions) (GTK_FILL),
2388 (GtkAttachOptions) (0), 0, 0);
2389 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2391 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2392 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
2393 gtk_widget_show (forward_rb);
2394 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2395 (GtkAttachOptions) (GTK_FILL),
2396 (GtkAttachOptions) (0), 0, 0);
2398 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2399 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
2400 gtk_widget_show (reversed_rb);
2401 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2402 (GtkAttachOptions) (GTK_FILL),
2403 (GtkAttachOptions) (0), 0, 0);
2405 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2406 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
2407 gtk_widget_show (both_rb);
2408 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2409 (GtkAttachOptions) (GTK_FILL),
2410 (GtkAttachOptions) (0), 0, 0);
2412 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2414 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
2415 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
2416 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
2417 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
2418 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
2419 SIGNAL_CONNECT(ok_bt, "clicked", save_csv_as_ok_cb,
2420 user_data->dlg.save_csv_as_w);
2422 window_set_cancel_button(user_data->dlg.save_csv_as_w,
2423 GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button, NULL);
2425 SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "delete_event", window_delete_event_cb, NULL);
2426 SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "destroy",
2427 save_csv_as_destroy_cb, user_data);
2429 gtk_widget_show(user_data->dlg.save_csv_as_w);
2430 window_present(user_data->dlg.save_csv_as_w);
2434 /****************************************************************************/
2435 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2437 /* Note that we no longer have a Save voice info dialog box. */
2438 user_data->dlg.save_voice_as_w = NULL;
2441 /****************************************************************************/
2442 /* here we save it into a file that user specified */
2443 /* XXX what about endians here? could go something wrong? */
2444 static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ user_data_t *user_data)
2446 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
2450 guint32 f_write_silence = 0;
2451 guint32 r_write_silence = 0;
2453 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2454 gboolean stop_flag = FALSE;
2456 forw_fd = open(user_data->f_tempname, O_RDONLY | O_BINARY);
2459 rev_fd = open(user_data->r_tempname, O_RDONLY | O_BINARY);
2465 /* open file for saving */
2466 to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2473 progbar = create_progress_dlg("Saving voice in a file", dest, &stop_flag);
2475 /* First we write the .au header. XXX Hope this is endian independant */
2476 /* the magic word 0x2e736e64 == .snd */
2477 *pd = (unsigned char)0x2e; write(to_fd, pd, 1);
2478 *pd = (unsigned char)0x73; write(to_fd, pd, 1);
2479 *pd = (unsigned char)0x6e; write(to_fd, pd, 1);
2480 *pd = (unsigned char)0x64; write(to_fd, pd, 1);
2481 /* header offset == 24 bytes */
2482 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2483 write(to_fd, pd, 1);
2484 write(to_fd, pd, 1);
2485 *pd = (unsigned char)0x18; write(to_fd, pd, 1);
2486 /* total length, it is permited to set this to 0xffffffff */
2487 *pd = (unsigned char)0xff; write(to_fd, pd, 1);
2488 write(to_fd, pd, 1);
2489 write(to_fd, pd, 1);
2490 write(to_fd, pd, 1);
2491 /* encoding format == 8 bit ulaw */
2492 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2493 write(to_fd, pd, 1);
2494 write(to_fd, pd, 1);
2495 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
2496 /* sample rate == 8000 Hz */
2497 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2498 write(to_fd, pd, 1);
2499 *pd = (unsigned char)0x1f; write(to_fd, pd, 1);
2500 *pd = (unsigned char)0x40; write(to_fd, pd, 1);
2502 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2503 write(to_fd, pd, 1);
2504 write(to_fd, pd, 1);
2505 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
2508 /* only forward direction */
2510 progbar_count = user_data->forward.saveinfo.count;
2511 progbar_quantum = user_data->forward.saveinfo.count/100;
2512 while ((fread = read(forw_fd, &f_pd, 2)) > 0) {
2515 if((count > progbar_nextstep) && (count <= progbar_count)) {
2516 update_progress_dlg(progbar,
2517 (gfloat) count/progbar_count, "Saving");
2518 progbar_nextstep = progbar_nextstep + progbar_quantum;
2521 *pd = (unsigned char)linear2ulaw(f_pd);
2522 fwritten = write(to_fd, pd, 1);
2523 if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) {
2527 destroy_progress_dlg(progbar);
2533 /* only reversed direction */
2535 progbar_count = user_data->reversed.saveinfo.count;
2536 progbar_quantum = user_data->reversed.saveinfo.count/100;
2537 while ((rread = read(rev_fd, &r_pd, 2)) > 0) {
2540 if((count > progbar_nextstep) && (count <= progbar_count)) {
2541 update_progress_dlg(progbar,
2542 (gfloat) count/progbar_count, "Saving");
2543 progbar_nextstep = progbar_nextstep + progbar_quantum;
2546 *pd = (unsigned char)linear2ulaw(r_pd);
2547 rwritten = write(to_fd, pd, 1);
2548 if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) {
2552 destroy_progress_dlg(progbar);
2558 /* both directions */
2560 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2561 (progbar_count = user_data->forward.saveinfo.count) :
2562 (progbar_count = user_data->reversed.saveinfo.count);
2563 progbar_quantum = progbar_count/100;
2564 /* since conversation in one way can start later than in the other one,
2565 * we have to write some silence information for one channel */
2566 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2567 f_write_silence = (guint32)
2568 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2570 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2571 r_write_silence = (guint32)
2572 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2577 if((count > progbar_nextstep) && (count <= progbar_count)) {
2578 update_progress_dlg(progbar,
2579 (gfloat) count/progbar_count, "Saving");
2580 progbar_nextstep = progbar_nextstep + progbar_quantum;
2583 if(f_write_silence > 0) {
2584 rread = read(rev_fd, &r_pd, 2);
2589 else if(r_write_silence > 0) {
2590 fread = read(forw_fd, &f_pd, 2);
2596 fread = read(forw_fd, &f_pd, 2);
2597 rread = read(rev_fd, &r_pd, 2);
2599 if ((rread == 0) && (fread == 0))
2601 *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 );
2602 rwritten = write(to_fd, pd, 1);
2603 if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
2607 destroy_progress_dlg(progbar);
2613 destroy_progress_dlg(progbar);
2621 /****************************************************************************/
2622 /* the user wants to save in a file */
2623 /* XXX support for different formats is currently commented out */
2624 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
2627 /*GtkWidget *wav, *au, *sw;*/
2628 GtkWidget *rev, *forw, *both;
2629 user_data_t *user_data;
2630 gint channels /*, format*/;
2632 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
2634 /* Perhaps the user specified a directory instead of a file.
2635 Check whether they did. */
2636 if (test_for_directory(g_dest) == EISDIR) {
2637 /* It's a directory - set the file selection box to display it. */
2638 set_last_open_dir(g_dest);
2640 file_selection_set_current_folder(fs, get_last_open_dir());
2644 /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
2645 au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
2646 sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
2647 rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
2648 forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
2649 both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
2650 user_data = (user_data_t *)OBJECT_GET_DATA(ok_bt, "user_data");
2652 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2653 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2654 * disable the ok button or disable the buttons for direction if only one is not ok. The
2655 * problem is if we open the save voice dialog and then click the refresh button and maybe
2656 * the state changes, so we can't save anymore. In this case we should be able to update
2657 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2660 /* we can not save in both dirctions */
2661 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2662 /* there are many combinations here, we just exit when first matches */
2663 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2664 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2665 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2666 "Can't save in a file: Unsupported codec!");
2667 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2668 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2669 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2670 "Can't save in a file: Wrong length of captured packets!");
2671 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2672 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2673 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2674 "Can't save in a file: RTP data with padding!");
2675 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2676 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2677 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2678 "Can't save in a file: Not all data in all packets was captured!");
2680 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2681 "Can't save in a file: File I/O problem!");
2684 /* we can not save forward direction */
2685 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2686 (GTK_TOGGLE_BUTTON (both)->active))) {
2687 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2688 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2689 "Can't save forward direction in a file: Unsupported codec!");
2690 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2691 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2692 "Can't save forward direction in a file: Wrong length of captured packets!");
2693 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2694 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2695 "Can't save forward direction in a file: RTP data with padding!");
2696 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2697 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2698 "Can't save forward direction in a file: Not all data in all packets was captured!");
2700 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2701 "Can't save forward direction in a file: File I/O problem!");
2704 /* we can not save reversed direction */
2705 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2706 (GTK_TOGGLE_BUTTON (both)->active))) {
2707 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2708 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2709 "Can't save reversed direction in a file: Unsupported codec!");
2710 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2711 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2712 "Can't save reversed direction in a file: Wrong length of captured packets!");
2713 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2714 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2715 "Can't save reversed direction in a file: RTP data with padding!");
2716 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2717 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2718 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2719 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2720 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2721 "Can't save reversed direction in a file: No RTP data!");
2723 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2724 "Can't save reversed direction in a file: File I/O problem!");
2728 /*if (GTK_TOGGLE_BUTTON (wav)->active)
2730 else if (GTK_TOGGLE_BUTTON (au)->active)
2732 else if (GTK_TOGGLE_BUTTON (sw)->active)
2735 if (GTK_TOGGLE_BUTTON (rev)->active)
2737 else if (GTK_TOGGLE_BUTTON (both)->active)
2742 if(!copy_file(g_dest, channels/*, format*/, user_data)) {
2743 /* XXX - report the error type! */
2744 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2745 "An error occured while saving voice in a file!");
2749 window_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
2752 /****************************************************************************/
2753 /* when the user wants to save the voice information in a file */
2754 /* XXX support for different formats is currently commented out */
2755 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2759 GtkWidget *label_format;
2760 GtkWidget *channels_label;
2761 /*GSList *format_group = NULL;*/
2762 GSList *channels_group = NULL;
2763 GtkWidget *forward_rb;
2764 GtkWidget *reversed_rb;
2766 /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/
2769 /* if we can't save in a file: wrong codec, cut packets or other errors */
2770 /* shold the error arise here or later when you click ok button ?
2771 * if we do it here, then we must disable the refresh button, so we don't do it here */
2773 if (user_data->dlg.save_voice_as_w != NULL) {
2774 /* There's already a Save voice info dialog box; reactivate it. */
2775 reactivate_window(user_data->dlg.save_voice_as_w);
2779 /* XXX - use file_selection from dlg_utils instead! */
2780 user_data->dlg.save_voice_as_w = gtk_file_selection_new("Ethereal: Save Payload As ...");
2782 /* Container for each row of widgets */
2783 vertb = gtk_vbox_new(FALSE, 0);
2784 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
2785 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
2786 vertb, FALSE, FALSE, 0);
2787 gtk_widget_show (vertb);
2789 table1 = gtk_table_new (2, 4, FALSE);
2790 gtk_widget_show (table1);
2791 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2792 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2793 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2795 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2796 gtk_widget_show (label_format);
2797 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2798 (GtkAttachOptions) (GTK_FILL),
2799 (GtkAttachOptions) (0), 0, 0);
2801 /* we support .au - ulaw*/
2802 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2803 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
2804 gtk_widget_show (wav_rb);
2805 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2806 (GtkAttachOptions) (GTK_FILL),
2807 (GtkAttachOptions) (0), 0, 0);
2809 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2810 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
2811 gtk_widget_show (sw_rb);
2812 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2813 (GtkAttachOptions) (GTK_FILL),
2814 (GtkAttachOptions) (0), 0, 0);
2815 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2816 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
2817 gtk_widget_show (au_rb);
2818 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2819 (GtkAttachOptions) (GTK_FILL),
2820 (GtkAttachOptions) (0), 0, 0);
2823 channels_label = gtk_label_new ("Channels:");
2824 gtk_widget_show (channels_label);
2825 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2826 (GtkAttachOptions) (GTK_FILL),
2827 (GtkAttachOptions) (0), 0, 0);
2828 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2830 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2831 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
2832 gtk_widget_show (forward_rb);
2833 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2834 (GtkAttachOptions) (GTK_FILL),
2835 (GtkAttachOptions) (0), 0, 0);
2837 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2838 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
2839 gtk_widget_show (reversed_rb);
2840 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2841 (GtkAttachOptions) (GTK_FILL),
2842 (GtkAttachOptions) (0), 0, 0);
2844 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2845 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
2846 gtk_widget_show (both_rb);
2847 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2848 (GtkAttachOptions) (GTK_FILL),
2849 (GtkAttachOptions) (0), 0, 0);
2851 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2853 /* if one direction is nok we don't allow saving
2854 XXX this is not ok since the user can click the refresh button and cause changes
2855 but we can not update this window. So we move all the decision on the time the ok
2857 if (user_data->forward.saved == FALSE) {
2858 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2859 gtk_widget_set_sensitive(forward_rb, FALSE);
2860 gtk_widget_set_sensitive(both_rb, FALSE);
2862 else if (user_data->reversed.saved == FALSE) {
2863 gtk_widget_set_sensitive(reversed_rb, FALSE);
2864 gtk_widget_set_sensitive(both_rb, FALSE);
2868 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
2869 /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);
2870 OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
2871 OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
2872 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
2873 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
2874 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
2875 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
2876 SIGNAL_CONNECT(ok_bt, "clicked", save_voice_as_ok_cb,
2877 user_data->dlg.save_voice_as_w);
2879 window_set_cancel_button(user_data->dlg.save_voice_as_w,
2880 GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button, window_cancel_button_cb);
2882 SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "delete_event",
2883 window_delete_event_cb, NULL);
2884 SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "destroy",
2885 save_voice_as_destroy_cb, user_data);
2887 gtk_widget_show(user_data->dlg.save_voice_as_w);
2888 window_present(user_data->dlg.save_voice_as_w);
2892 /****************************************************************************/
2893 /* when we are finished with redisection, we add the label for the statistic */
2894 static void draw_stat(user_data_t *user_data)
2896 gchar label_max[200];
2897 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
2898 - user_data->forward.statinfo.start_seq_nr + 1;
2899 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
2900 - user_data->reversed.statinfo.start_seq_nr + 1;
2901 gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
2902 gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
2903 double f_perc, r_perc;
2905 f_perc = (double)(f_lost*100)/(double)f_expected;
2910 r_perc = (double)(r_lost*100)/(double)r_expected;
2915 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
2916 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2917 " Sequence errors = %u",
2918 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
2919 user_data->forward.statinfo.total_nr,
2920 f_expected, f_lost, f_perc, user_data->forward.statinfo.sequence);
2922 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2924 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
2925 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2926 " Sequence errors = %u",
2927 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
2928 user_data->reversed.statinfo.total_nr,
2929 r_expected, r_lost, r_perc, user_data->reversed.statinfo.sequence);
2931 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2938 /****************************************************************************/
2939 /* append a line to clist */
2940 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
2941 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
2942 gchar *timeStr, guint32 pkt_len, GdkColor *color)
2949 data[0]=&field[0][0];
2950 data[1]=&field[1][0];
2951 data[2]=&field[2][0];
2952 data[3]=&field[3][0];
2953 data[4]=&field[4][0];
2954 data[5]=&field[5][0];
2955 data[6]=&field[6][0];
2956 data[7]=&field[7][0];
2957 data[8]=&field[8][0];
2959 /* save the current locale */
2960 savelocale = setlocale(LC_NUMERIC, NULL);
2961 /* switch to "C" locale to avoid problems with localized decimal separators
2962 in g_snprintf("%f") functions */
2963 setlocale(LC_NUMERIC, "C");
2964 g_snprintf(field[0], 20, "%u", number);
2965 g_snprintf(field[1], 20, "%u", seq_num);
2966 g_snprintf(field[2], 20, "%.2f", delta);
2967 g_snprintf(field[3], 20, "%.2f", jitter);
2968 g_snprintf(field[4], 20, "%.2f", bandwidth);
2969 g_snprintf(field[5], 20, "%s", marker? "SET" : "");
2970 g_snprintf(field[6], 40, "%s", status);
2971 g_snprintf(field[7], 32, "%s", timeStr);
2972 g_snprintf(field[8], 20, "%u", pkt_len);
2973 /* restore previous locale setting */
2974 setlocale(LC_NUMERIC, savelocale);
2976 added_row = gtk_clist_append(GTK_CLIST(clist), data);
2977 gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
2978 gtk_clist_set_background(GTK_CLIST(clist), added_row, color);
2982 /****************************************************************************/
2983 /* callback for sorting columns of clist */
2984 static gint rtp_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
2991 const GtkCListRow *row1 = ptr1;
2992 const GtkCListRow *row2 = ptr2;
2994 text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
2995 text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
2997 switch(clist->sort_column){
2998 /* columns representing strings */
3002 return strcmp (text1, text2);
3003 /* columns representing ints */
3010 /* columns representing floats */
3016 if (fabs(f1-f2)<0.0000005)
3022 g_assert_not_reached();
3027 /****************************************************************************/
3029 click_column_cb(GtkCList *clist, gint column, gpointer data)
3031 column_arrows *col_arrows = (column_arrows *) data;
3034 gtk_clist_freeze(clist);
3036 for (i = 0; i < NUM_COLS; i++) {
3037 gtk_widget_hide(col_arrows[i].ascend_pm);
3038 gtk_widget_hide(col_arrows[i].descend_pm);
3041 if (column == clist->sort_column) {
3042 if (clist->sort_type == GTK_SORT_ASCENDING) {
3043 clist->sort_type = GTK_SORT_DESCENDING;
3044 gtk_widget_show(col_arrows[column].descend_pm);
3046 clist->sort_type = GTK_SORT_ASCENDING;
3047 gtk_widget_show(col_arrows[column].ascend_pm);
3050 clist->sort_type = GTK_SORT_ASCENDING;
3051 gtk_widget_show(col_arrows[column].ascend_pm);
3052 gtk_clist_set_sort_column(clist, column);
3054 gtk_clist_thaw(clist);
3056 gtk_clist_sort(clist);
3060 /****************************************************************************/
3061 /* Add the packet list */
3063 GtkWidget* create_clist(user_data_t* user_data)
3065 GtkWidget* clist_fwd;
3067 /* clist for the information */
3068 clist_fwd = gtk_clist_new(NUM_COLS);
3069 gtk_widget_show(clist_fwd);
3070 SIGNAL_CONNECT(clist_fwd, "select_row", on_clist_select_row, user_data);
3072 gtk_clist_column_titles_show(GTK_CLIST(clist_fwd));
3073 gtk_clist_set_compare_func(GTK_CLIST(clist_fwd), rtp_sort_column);
3074 gtk_clist_set_sort_column(GTK_CLIST(clist_fwd), 0);
3075 gtk_clist_set_sort_type(GTK_CLIST(clist_fwd), GTK_SORT_ASCENDING);
3077 /* hide date and length column */
3078 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
3079 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 8, FALSE);
3081 /* column widths and justification */
3082 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 60);
3083 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 75);
3084 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 75);
3085 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 75);
3086 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 50);
3087 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 5, 75);
3088 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_RIGHT);
3089 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_RIGHT);
3090 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
3091 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
3092 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
3093 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
3094 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 6, GTK_JUSTIFY_CENTER);
3099 /****************************************************************************/
3100 /* Add the sort by column feature for a packet clist */
3102 column_arrows* add_sort_by_column(GtkWidget* window, GtkWidget* clist,
3103 user_data_t* user_data _U_)
3105 column_arrows *col_arrows;
3106 GdkBitmap *ascend_bm, *descend_bm;
3107 GdkPixmap *ascend_pm, *descend_pm;
3108 GtkStyle *win_style;
3109 GtkWidget *column_lb;
3112 col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
3113 win_style = gtk_widget_get_style(window);
3114 ascend_pm = gdk_pixmap_create_from_xpm_d(window->window,
3116 &win_style->bg[GTK_STATE_NORMAL],
3117 (gchar **)clist_ascend_xpm);
3118 descend_pm = gdk_pixmap_create_from_xpm_d(window->window,
3120 &win_style->bg[GTK_STATE_NORMAL],
3121 (gchar **)clist_descend_xpm);
3123 for (i=0; i<NUM_COLS; i++) {
3124 col_arrows[i].table = gtk_table_new(2, 2, FALSE);
3125 gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
3126 column_lb = gtk_label_new(titles[i]);
3127 gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
3128 gtk_widget_show(column_lb);
3130 col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
3131 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
3132 col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
3133 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
3134 /* make packet-nr be the default sort order */
3136 gtk_widget_show(col_arrows[i].ascend_pm);
3138 gtk_clist_set_column_widget(GTK_CLIST(clist), i, col_arrows[i].table);
3139 gtk_widget_show(col_arrows[i].table);
3142 SIGNAL_CONNECT(clist, "click-column", click_column_cb, col_arrows);
3147 /****************************************************************************/
3148 /* Create the dialog box with all widgets */
3149 static void create_rtp_dialog(user_data_t* user_data)
3151 GtkWidget *window = NULL;
3152 GtkWidget *clist_fwd;
3153 GtkWidget *clist_rev;
3154 GtkWidget *label_stats_fwd;
3155 GtkWidget *label_stats_rev;
3156 GtkWidget *notebook;
3158 GtkWidget *main_vb, *page, *page_r;
3160 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3161 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3162 #ifdef USE_CONVERSATION_GRAPH
3163 GtkWidget *graph_bt;
3165 GtkWidget *graph_bt;
3166 gchar label_forward[150];
3167 gchar label_reverse[150];
3169 gchar str_ip_src[16];
3170 gchar str_ip_dst[16];
3171 column_arrows *col_arrows_fwd;
3172 column_arrows *col_arrows_rev;
3174 window = window_new(GTK_WINDOW_TOPLEVEL, "Ethereal: RTP Stream Analysis");
3175 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3177 /* Container for each row of widgets */
3178 main_vb = gtk_vbox_new(FALSE, 2);
3179 gtk_container_border_width(GTK_CONTAINER(main_vb), 2);
3180 gtk_container_add(GTK_CONTAINER(window), main_vb);
3181 gtk_widget_show(main_vb);
3184 strcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)));
3185 strcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)));
3187 g_snprintf(label_forward, 149,
3188 "Analysing stream from %s port %u to %s port %u SSRC = %u",
3189 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3192 strcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)));
3193 strcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)));
3195 g_snprintf(label_reverse, 149,
3196 "Analysing stream from %s port %u to %s port %u SSRC = %u",
3197 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3199 /* Start a notebook for flipping between sets of changes */
3200 notebook = gtk_notebook_new();
3201 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3202 OBJECT_SET_DATA(window, "notebook", notebook);
3204 user_data->dlg.notebook_signal_id = SIGNAL_CONNECT(notebook, "switch_page", on_notebook_switch_page,
3207 /* page for forward connection */
3208 page = gtk_vbox_new(FALSE, 8);
3209 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3211 /* direction label */
3212 label = gtk_label_new(label_forward);
3213 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3215 /* place for some statistics */
3216 label_stats_fwd = gtk_label_new("\n");
3217 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3219 /* scrolled window */
3220 scrolled_window = scrolled_window_new(NULL, NULL);
3223 clist_fwd = create_clist(user_data);
3224 gtk_widget_show(clist_fwd);
3225 gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
3226 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3227 gtk_widget_show(scrolled_window);
3230 label = gtk_label_new(" Forward Direction ");
3231 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3233 /* same page for reversed connection */
3234 page_r = gtk_vbox_new(FALSE, 8);
3235 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3236 label = gtk_label_new(label_reverse);
3237 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3238 label_stats_rev = gtk_label_new("\n");
3239 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3241 scrolled_window_r = scrolled_window_new(NULL, NULL);
3243 clist_rev = create_clist(user_data);
3244 gtk_widget_show(clist_rev);
3245 gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
3246 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3247 gtk_widget_show(scrolled_window_r);
3249 label = gtk_label_new(" Reversed Direction ");
3250 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3252 /* page for help&about or future
3253 page_help = gtk_hbox_new(FALSE, 5);
3254 label = gtk_label_new(" Future ");
3255 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3256 frame = gtk_frame_new("");
3257 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3258 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3259 gtk_container_add(GTK_CONTAINER(frame), text);
3260 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3261 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3264 /* show all notebooks */
3265 gtk_widget_show_all(notebook);
3268 box4 = gtk_hbutton_box_new();
3269 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3270 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3271 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3272 gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0);
3273 gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
3274 gtk_widget_show(box4);
3276 voice_bt = gtk_button_new_with_label("Save payload...");
3277 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3278 gtk_widget_show(voice_bt);
3279 SIGNAL_CONNECT(voice_bt, "clicked", on_save_bt_clicked, user_data);
3281 csv_bt = gtk_button_new_with_label("Save as CSV...");
3282 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3283 gtk_widget_show(csv_bt);
3284 SIGNAL_CONNECT(csv_bt, "clicked", save_csv_as_cb, user_data);
3286 refresh_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_REFRESH);
3287 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3288 gtk_widget_show(refresh_bt);
3289 SIGNAL_CONNECT(refresh_bt, "clicked", on_refresh_bt_clicked, user_data);
3291 goto_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_JUMP_TO);
3292 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3293 gtk_widget_show(goto_bt);
3294 SIGNAL_CONNECT(goto_bt, "clicked", on_goto_bt_clicked, user_data);
3296 graph_bt = gtk_button_new_with_label("Graph");
3297 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3298 gtk_widget_show(graph_bt);
3299 SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
3302 #ifdef USE_CONVERSATION_GRAPH
3303 graph_bt = gtk_button_new_with_label("Graph");
3304 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3305 gtk_widget_show(graph_bt);
3306 SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
3309 next_bt = gtk_button_new_with_label("Next non-Ok");
3310 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3311 gtk_widget_show(next_bt);
3312 SIGNAL_CONNECT(next_bt, "clicked", on_next_bt_clicked, user_data);
3314 close_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
3315 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3316 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3317 gtk_widget_show(close_bt);
3318 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3320 SIGNAL_CONNECT(window, "delete_event", window_delete_event_cb, NULL);
3321 SIGNAL_CONNECT(window, "destroy", on_destroy, user_data);
3323 gtk_widget_show(window);
3324 window_present(window);
3326 /* sort by column feature */
3327 col_arrows_fwd = add_sort_by_column(window, clist_fwd, user_data);
3328 col_arrows_rev = add_sort_by_column(window, clist_rev, user_data);
3330 /* some widget references need to be saved for outside use */
3331 user_data->dlg.window = window;
3332 user_data->dlg.clist_fwd = GTK_CLIST(clist_fwd);
3333 user_data->dlg.clist_rev = GTK_CLIST(clist_rev);
3334 user_data->dlg.label_stats_fwd = label_stats_fwd;
3335 user_data->dlg.label_stats_rev = label_stats_rev;
3336 user_data->dlg.notebook = notebook;
3337 user_data->dlg.selected_clist = GTK_CLIST(clist_fwd);
3338 user_data->dlg.selected_row = 0;
3339 user_data->dlg.col_arrows_fwd = col_arrows_fwd;
3340 user_data->dlg.col_arrows_rev = col_arrows_rev;
3344 /****************************************************************************/
3345 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3346 const gchar* proto_field, guint32* p_result)
3349 proto_node *proto_sibling_node;
3350 header_field_info *hfssrc;
3353 finfo = PITEM_FINFO(ptree_node);
3355 if (hfinformation==(finfo->hfinfo)) {
3356 hfssrc = proto_registrar_get_byname(proto_field);
3359 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3360 ptree_node=ptree_node->next) {
3361 finfo=PITEM_FINFO(ptree_node);
3362 if (hfssrc==finfo->hfinfo) {
3363 if (hfinformation->type==FT_IPv4) {
3364 ipv4 = fvalue_get(&finfo->value);
3365 *p_result = ipv4_get_net_order_addr(ipv4);
3368 *p_result = fvalue_get_integer(&finfo->value);
3375 proto_sibling_node = ptree_node->next;
3377 if (proto_sibling_node) {
3378 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3384 /****************************************************************************/
3385 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3386 const gchar* proto_name,
3387 const gchar* proto_field,
3390 proto_node *ptree_node;
3391 header_field_info *hfinformation;
3393 hfinformation = proto_registrar_get_byname(proto_name);
3394 if (hfinformation == NULL)
3397 ptree_node = ((proto_node *)protocol_tree)->first_child;
3401 return process_node(ptree_node, hfinformation, proto_field, p_result);
3405 /* XXX just copied from gtk/rpc_stat.c */
3406 void protect_thread_critical_region(void);
3407 void unprotect_thread_critical_region(void);
3409 /****************************************************************************/
3411 address *ip_src_fwd,
3412 guint16 port_src_fwd,
3413 address *ip_dst_fwd,
3414 guint16 port_dst_fwd,
3416 address *ip_src_rev,
3417 guint16 port_src_rev,
3418 address *ip_dst_rev,
3419 guint16 port_dst_rev,
3423 user_data_t *user_data;
3426 static color_t col[MAX_GRAPHS] = {
3427 {0, 0x0000, 0x0000, 0x0000},
3428 {0, 0xffff, 0x0000, 0x0000},
3429 {0, 0x0000, 0xffff, 0x0000},
3430 {0, 0x0000, 0x0000, 0xffff}
3434 user_data = g_malloc(sizeof(user_data_t));
3436 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3437 user_data->port_src_fwd = port_src_fwd;
3438 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3439 user_data->port_dst_fwd = port_dst_fwd;
3440 user_data->ssrc_fwd = ssrc_fwd;
3441 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3442 user_data->port_src_rev = port_src_rev;
3443 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3444 user_data->port_dst_rev = port_dst_rev;
3445 user_data->ssrc_rev = ssrc_rev;
3448 /* file names for storing sound data */
3449 /*XXX: check for errors*/
3450 fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
3453 fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
3456 user_data->forward.saveinfo.fp = NULL;
3457 user_data->reversed.saveinfo.fp = NULL;
3458 user_data->dlg.save_voice_as_w = NULL;
3459 user_data->dlg.save_csv_as_w = NULL;
3460 user_data->dlg.dialog_graph.window = NULL;
3462 #ifdef USE_CONVERSATION_GRAPH
3463 user_data->dlg.graph_window = NULL;
3464 user_data->series_fwd.value_pairs = NULL;
3465 user_data->series_rev.value_pairs = NULL;
3468 /* init dialog_graph */
3469 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3470 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3471 user_data->dlg.dialog_graph.draw_area=NULL;
3472 user_data->dlg.dialog_graph.pixmap=NULL;
3473 user_data->dlg.dialog_graph.scrollbar=NULL;
3474 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3475 user_data->dlg.dialog_graph.pixmap_width=500;
3476 user_data->dlg.dialog_graph.pixmap_height=200;
3477 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3478 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3479 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3480 user_data->dlg.dialog_graph.max_interval=0;
3481 user_data->dlg.dialog_graph.num_items=0;
3482 user_data->dlg.dialog_graph.start_time = -1;
3484 for(i=0;i<MAX_GRAPHS;i++){
3485 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3486 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3487 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3488 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3489 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3490 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3491 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3492 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3495 /* create the dialog box */
3496 create_rtp_dialog(user_data);
3498 /* proceed as if the Refresh button would have been pressed */
3499 on_refresh_bt_clicked(NULL, user_data);
3502 /****************************************************************************/
3503 /* entry point from main menu */
3504 static void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3507 guint16 port_src_fwd;
3509 guint16 port_dst_fwd;
3510 guint32 ssrc_fwd = 0;
3512 guint16 port_src_rev;
3514 guint16 port_dst_rev;
3515 guint32 ssrc_rev = 0;
3516 unsigned int version_fwd;
3518 gchar filter_text[256];
3521 epan_dissect_t *edt;
3524 gboolean frame_matched;
3526 GList *strinfo_list;
3527 GList *filtered_list = NULL;
3528 rtp_stream_info_t *strinfo;
3531 /* Try to compile the filter. */
3532 strcpy(filter_text,"rtp && rtp.version && rtp.ssrc && (ip || ipv6)");
3533 if (!dfilter_compile(filter_text, &sfcode)) {
3534 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, dfilter_error_msg);
3537 /* we load the current file into cf variable */
3539 fdata = cf->current_frame;
3541 /* we are on the selected frame now */
3543 return; /* if we exit here it's an error */
3545 /* dissect the current frame */
3546 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3547 cf->pd, fdata->cap_len, &err, &err_info)) {
3548 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3549 cf_read_error_message(err, err_info), cf->filename);
3552 edt = epan_dissect_new(TRUE, FALSE);
3553 epan_dissect_prime_dfilter(edt, sfcode);
3554 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3555 frame_matched = dfilter_apply_edt(sfcode, edt);
3557 /* if it is not an rtp frame, show the rtpstream dialog */
3558 frame_matched = dfilter_apply_edt(sfcode, edt);
3559 if (frame_matched != 1) {
3560 epan_dissect_free(edt);
3561 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3562 "You didn't choose a RTP packet!");
3566 /* ok, it is a RTP frame, so let's get the ip and port values */
3567 COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
3568 COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
3569 port_src_fwd = edt->pi.srcport;
3570 port_dst_fwd = edt->pi.destport;
3572 /* assume the inverse ip/port combination for the reverse direction */
3573 COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
3574 COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
3575 port_src_rev = edt->pi.destport;
3576 port_dst_rev = edt->pi.srcport;
3578 /* check if it is RTP Version 2 */
3579 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3580 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3581 "RTP Version != 2 isn't supported!");
3585 /* now we need the SSRC value of the current frame */
3586 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3587 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3588 "SSRC value couldn't be found!");
3592 /* Scan for rtpstream */
3594 /* search for reversed direction in the global rtp streams list */
3596 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3597 while (strinfo_list)
3599 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3600 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3601 && strinfo->src_port==port_src_fwd
3602 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3603 && strinfo->dest_port==port_dst_fwd)
3605 filtered_list = g_list_prepend(filtered_list, strinfo);
3608 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3609 && strinfo->src_port==port_src_rev
3610 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3611 && strinfo->dest_port==port_dst_rev)
3614 filtered_list = g_list_append(filtered_list, strinfo);
3616 ssrc_rev = strinfo->ssrc;
3619 strinfo_list = g_list_next(strinfo_list);
3622 /* if more than one reverse streams found, we let the user choose the right one */
3624 rtpstream_dlg_show(filtered_list);
3643 /****************************************************************************/
3645 rtp_analysis_init(const char *dummy _U_)
3647 rtp_analysis_cb(NULL, NULL);
3650 /****************************************************************************/
3652 register_tap_listener_rtp_analysis(void)
3654 register_stat_cmd_arg("rtp", rtp_analysis_init);
3656 register_stat_menu_item("RTP/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3657 rtp_analysis_cb, NULL, NULL, NULL);