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 "dlg_utils.h"
68 #include "alert_box.h"
69 #include "simple_dialog.h"
72 #include "progress_dlg.h"
73 #include "compat_macros.h"
76 #include "image/clist_ascend.xpm"
77 #include "image/clist_descend.xpm"
93 #include <io.h> /* open/close on win32 */
96 /* Win32 needs the O_BINARY flag for open() */
101 /****************************************************************************/
103 typedef struct column_arrows {
105 GtkWidget *ascend_pm;
106 GtkWidget *descend_pm;
110 #define NUM_GRAPH_ITEMS 100000
111 #define MAX_YSCALE 16
112 #define AUTO_MAX_YSCALE 0
114 #define GRAPH_FWD_JITTER 0
115 #define GRAPH_FWD_DIFF 1
116 #define GRAPH_REV_JITTER 2
117 #define GRAPH_REV_DIFF 3
118 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
120 #define MAX_PIXELS_PER_TICK 4
121 #define DEFAULT_PIXELS_PER_TICK 1
122 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
123 static char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
125 #define MAX_TICK_VALUES 5
126 #define DEFAULT_TICK_VALUE 1
127 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
128 typedef struct _dialog_graph_graph_item_t {
131 } dialog_graph_graph_item_t;
133 typedef struct _dialog_graph_graph_t {
134 struct _user_data_t *ud;
135 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
138 GtkWidget *display_button;
143 } dialog_graph_graph_t;
146 typedef struct _dialog_graph_t {
147 gboolean needs_redraw;
148 gint32 interval; /* measurement interval in ms */
149 guint32 last_interval;
150 guint32 max_interval; /* XXX max_interval and num_items are redundant */
152 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
154 GtkWidget *draw_area;
156 GtkAdjustment *scrollbar_adjustment;
157 GtkWidget *scrollbar;
165 typedef struct _dialog_data_t {
169 GtkWidget *label_stats_fwd;
170 GtkWidget *label_stats_rev;
171 column_arrows *col_arrows_fwd;
172 column_arrows *col_arrows_rev;
174 GtkCList *selected_clist;
175 GtkWidget *save_voice_as_w;
176 GtkWidget *save_csv_as_w;
177 gint notebook_signal_id;
179 dialog_graph_t dialog_graph;
180 #ifdef USE_CONVERSATION_GRAPH
181 GtkWidget *graph_window;
185 #define OK_TEXT "[ Ok ]"
187 typedef struct _key_value {
193 /* RTP sampling clock rates for fixed payload types as defined in
194 http://www.iana.org/assignments/rtp-parameters */
195 static const key_value clock_map[] = {
201 {PT_DVI4_8000, 8000},
202 {PT_DVI4_16000, 16000},
206 {PT_L16_STEREO, 44100},
207 {PT_L16_MONO, 44100},
213 {PT_DVI4_11025, 11025},
214 {PT_DVI4_22050, 22050},
226 #define NUM_CLOCK_VALUES (sizeof clock_map / sizeof clock_map[0])
229 get_clock_rate(guint32 key)
233 for (i = 0; i < NUM_CLOCK_VALUES; i++) {
234 if (clock_map[i].key == key)
235 return clock_map[i].value;
241 /* type of error when saving voice in a file didn't succeed */
244 TAP_RTP_WRONG_LENGTH,
245 TAP_RTP_PADDING_ERROR,
247 TAP_RTP_FILE_OPEN_ERROR,
251 #if GTK_MAJOR_VERSION < 2
252 GtkRcStyle *rc_style;
253 GdkColormap *colormap;
256 typedef struct _tap_rtp_save_info_t {
259 error_type_t error_type;
261 } tap_rtp_save_info_t;
264 /* structure that holds the information about the forward and reversed direction */
265 struct _info_direction {
266 tap_rtp_stat_t statinfo;
267 tap_rtp_save_info_t saveinfo;
270 #define TMPNAMSIZE 100
272 /* structure that holds general information about the connection
273 * and structures for both directions */
274 typedef struct _user_data_t {
275 /* tap associated data*/
277 guint16 port_src_fwd;
279 guint16 port_dst_fwd;
282 guint16 port_src_rev;
284 guint16 port_dst_rev;
287 struct _info_direction forward;
288 struct _info_direction reversed;
290 char f_tempname[TMPNAMSIZE];
291 char r_tempname[TMPNAMSIZE];
293 /* dialog associated data */
296 #ifdef USE_CONVERSATION_GRAPH
297 time_series_t series_fwd;
298 time_series_t series_rev;
304 static gchar *titles[9] = {
317 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_);
318 /****************************************************************************/
319 static void enable_graph(dialog_graph_graph_t *dgg)
326 static void dialog_graph_reset(user_data_t* user_data);
330 /****************************************************************************/
333 /****************************************************************************/
334 /* when there is a [re]reading of packet's */
336 rtp_reset(void *user_data_arg)
338 user_data_t *user_data = user_data_arg;
339 user_data->forward.statinfo.first_packet = TRUE;
340 user_data->reversed.statinfo.first_packet = TRUE;
341 user_data->forward.statinfo.max_delta = 0;
342 user_data->reversed.statinfo.max_delta = 0;
343 user_data->forward.statinfo.max_jitter = 0;
344 user_data->reversed.statinfo.max_jitter = 0;
345 user_data->forward.statinfo.mean_jitter = 0;
346 user_data->reversed.statinfo.mean_jitter = 0;
347 user_data->forward.statinfo.delta = 0;
348 user_data->reversed.statinfo.delta = 0;
349 user_data->forward.statinfo.diff = 0;
350 user_data->reversed.statinfo.diff = 0;
351 user_data->forward.statinfo.jitter = 0;
352 user_data->reversed.statinfo.jitter = 0;
353 user_data->forward.statinfo.bandwidth = 0;
354 user_data->reversed.statinfo.bandwidth = 0;
355 user_data->forward.statinfo.total_bytes = 0;
356 user_data->reversed.statinfo.total_bytes = 0;
357 user_data->forward.statinfo.bw_start_index = 0;
358 user_data->reversed.statinfo.bw_start_index = 0;
359 user_data->forward.statinfo.bw_index = 0;
360 user_data->reversed.statinfo.bw_index = 0;
361 user_data->forward.statinfo.timestamp = 0;
362 user_data->reversed.statinfo.timestamp = 0;
363 user_data->forward.statinfo.max_nr = 0;
364 user_data->reversed.statinfo.max_nr = 0;
365 user_data->forward.statinfo.total_nr = 0;
366 user_data->reversed.statinfo.total_nr = 0;
367 user_data->forward.statinfo.sequence = 0;
368 user_data->reversed.statinfo.sequence = 0;
369 user_data->forward.statinfo.start_seq_nr = 0;
370 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
371 user_data->forward.statinfo.stop_seq_nr = 0;
372 user_data->reversed.statinfo.stop_seq_nr = 0;
373 user_data->forward.statinfo.cycles = 0;
374 user_data->reversed.statinfo.cycles = 0;
375 user_data->forward.statinfo.under = FALSE;
376 user_data->reversed.statinfo.under = FALSE;
377 user_data->forward.statinfo.start_time = 0;
378 user_data->reversed.statinfo.start_time = 0;
379 user_data->forward.statinfo.time = 0;
380 user_data->reversed.statinfo.time = 0;
381 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
382 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
384 user_data->forward.saveinfo.count = 0;
385 user_data->reversed.saveinfo.count = 0;
386 user_data->forward.saveinfo.saved = FALSE;
387 user_data->reversed.saveinfo.saved = FALSE;
389 /* clear the dialog box clists */
390 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
391 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
393 /* reset graph info */
394 dialog_graph_reset(user_data);
396 #ifdef USE_CONVERSATION_GRAPH
397 if (user_data->dlg.graph_window != NULL)
398 window_destroy(user_data->dlg.graph_window);
400 g_array_free(user_data->series_fwd.value_pairs, TRUE);
401 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
403 g_array_free(user_data->series_rev.value_pairs, TRUE);
404 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
407 /* XXX check for error at fclose? */
408 if (user_data->forward.saveinfo.fp != NULL)
409 fclose(user_data->forward.saveinfo.fp);
410 if (user_data->reversed.saveinfo.fp != NULL)
411 fclose(user_data->reversed.saveinfo.fp);
412 user_data->forward.saveinfo.fp = fopen(user_data->f_tempname, "wb");
413 if (user_data->forward.saveinfo.fp == NULL)
414 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
415 user_data->reversed.saveinfo.fp = fopen(user_data->r_tempname, "wb");
416 if (user_data->reversed.saveinfo.fp == NULL)
417 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
421 /****************************************************************************/
422 static int rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
424 dialog_graph_graph_item_t *it;
428 /* we sometimes get called when dgg is disabled.
429 this is a bug since the tap listener should be removed first */
434 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
437 * Find which interval this is supposed to to in and store the
438 * interval index as idx
440 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
441 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
443 rtp_time = ((double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000) - dgg->ud->dlg.dialog_graph.start_time;
447 idx = (guint32)(rtp_time*1000)/dgg->ud->dlg.dialog_graph.interval;
449 /* some sanity checks */
450 if((idx<0)||(idx>=NUM_GRAPH_ITEMS)){
454 /* update num_items */
455 if((guint32)idx > dgg->ud->dlg.dialog_graph.num_items){
456 dgg->ud->dlg.dialog_graph.num_items=idx;
457 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
461 * Find the appropriate dialog_graph_graph_item_t structure
466 * Use the max value to highlight RTP problems
468 if (value > it->value) {
471 it->flags = it->flags | statinfo->flags;
476 /****************************************************************************/
477 /* here we can redraw the output */
479 static void rtp_draw(void *prs _U_)
484 /* forward declarations */
485 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
486 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
487 gchar *timeStr, guint32 pkt_len, GdkColor *color);
489 static int rtp_packet_add_info(GtkCList *clist,
490 tap_rtp_stat_t *statinfo, packet_info *pinfo,
491 const struct _rtp_info *rtpinfo);
493 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
494 tap_rtp_stat_t *statinfo,
496 const struct _rtp_info *rtpinfo);
499 /****************************************************************************/
500 /* whenever a RTP packet is seen by the tap listener */
501 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
503 user_data_t *user_data = user_data_arg;
504 const struct _rtp_info *rtpinfo = rtpinfo_arg;
505 #ifdef USE_CONVERSATION_GRAPH
508 /* we ignore packets that are not displayed */
509 if (pinfo->fd->flags.passed_dfilter == 0)
511 /* also ignore RTP Version != 2 */
512 else if (rtpinfo->info_version !=2)
514 /* is it the forward direction? */
515 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
516 && CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
517 && user_data->port_src_fwd == pinfo->srcport
518 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
519 && user_data->port_dst_fwd == pinfo->destport) {
520 #ifdef USE_CONVERSATION_GRAPH
521 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
522 vp.fnumber = pinfo->fd->num;
523 g_array_append_val(user_data->series_fwd.value_pairs, vp);
525 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
526 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));
527 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));
528 rtp_packet_add_info(user_data->dlg.clist_fwd,
529 &(user_data->forward.statinfo), pinfo, rtpinfo);
530 rtp_packet_save_payload(&(user_data->forward.saveinfo),
531 &(user_data->forward.statinfo), pinfo, rtpinfo);
533 /* is it the reversed direction? */
534 else if (user_data->ssrc_rev == rtpinfo->info_sync_src
535 && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
536 && user_data->port_src_rev == pinfo->srcport
537 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
538 && user_data->port_dst_rev == pinfo->destport) {
539 #ifdef USE_CONVERSATION_GRAPH
540 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
541 vp.fnumber = pinfo->fd->num;
542 g_array_append_val(user_data->series_rev.value_pairs, vp);
544 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
545 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));
546 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));
547 rtp_packet_add_info(user_data->dlg.clist_rev,
548 &(user_data->reversed.statinfo), pinfo, rtpinfo);
549 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
550 &(user_data->reversed.statinfo), pinfo, rtpinfo);
557 /****************************************************************************/
558 int rtp_packet_analyse(tap_rtp_stat_t *statinfo,
560 const struct _rtp_info *rtpinfo)
563 double current_jitter;
568 /* check payload type */
569 if (rtpinfo->info_payload_type == PT_CN
570 || rtpinfo->info_payload_type == PT_CN_OLD)
571 statinfo->flags |= STAT_FLAG_PT_CN;
572 if (statinfo->pt == PT_CN
573 || statinfo->pt == PT_CN_OLD)
574 statinfo->flags |= STAT_FLAG_FOLLOW_PT_CN;
575 if (rtpinfo->info_payload_type != statinfo->pt)
576 statinfo->flags |= STAT_FLAG_PT_CHANGE;
577 statinfo->pt = rtpinfo->info_payload_type;
579 * XXX - should "get_clock_rate()" return 0 for unknown
580 * payload types, presumably meaning that we should
581 * just ignore this packet?
583 clock_rate = get_clock_rate(statinfo->pt);
585 /* store the current time and calculate the current jitter */
586 current_time = (double)pinfo->fd->rel_secs + (double) pinfo->fd->rel_usecs/1000000;
587 current_diff = fabs (current_time - (statinfo->time) - ((double)(rtpinfo->info_timestamp)-(double)(statinfo->timestamp))/clock_rate);
588 current_jitter = statinfo->jitter + ( current_diff - statinfo->jitter)/16;
589 statinfo->delta = current_time-(statinfo->time);
590 statinfo->jitter = current_jitter;
591 statinfo->diff = current_diff;
593 /* calculate the BW in Kbps adding the IP+UDP header to the RTP -> 20bytes(IP)+8bytes(UDP) = 28bytes */
594 statinfo->bw_history[statinfo->bw_index].bytes = rtpinfo->info_data_len + 28;
595 statinfo->bw_history[statinfo->bw_index].time = current_time;
596 /* check if there are more than 1sec in the history buffer to calculate BW in bps. If so, remove those for the calculation */
597 while ((statinfo->bw_history[statinfo->bw_start_index].time+1)<current_time){
598 statinfo->total_bytes -= statinfo->bw_history[statinfo->bw_start_index].bytes;
599 statinfo->bw_start_index++;
600 if (statinfo->bw_start_index == BUFF_BW) statinfo->bw_start_index=0;
602 statinfo->total_bytes += rtpinfo->info_data_len + 28;
603 statinfo->bandwidth = (double)(statinfo->total_bytes*8)/1000;
604 statinfo->bw_index++;
605 if (statinfo->bw_index == BUFF_BW) statinfo->bw_index = 0;
608 /* is this the first packet we got in this direction? */
609 if (statinfo->first_packet) {
610 statinfo->start_seq_nr = rtpinfo->info_seq_num;
611 statinfo->start_time = current_time;
613 statinfo->jitter = 0;
615 statinfo->flags |= STAT_FLAG_FIRST;
616 statinfo->first_packet = FALSE;
618 /* is it a packet with the mark bit set? */
619 if (rtpinfo->info_marker_set) {
620 if (rtpinfo->info_timestamp > statinfo->timestamp){
621 statinfo->delta_timestamp = rtpinfo->info_timestamp - statinfo->timestamp;
622 statinfo->flags |= STAT_FLAG_MARKER;
625 statinfo->flags |= STAT_FLAG_WRONG_TIMESTAMP;
628 /* is it a regular packet? */
629 if (!(statinfo->flags & STAT_FLAG_FIRST)
630 && !(statinfo->flags & STAT_FLAG_MARKER)
631 && !(statinfo->flags & STAT_FLAG_PT_CN)
632 && !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
633 && !(statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)) {
634 /* include it in maximum delta calculation */
635 if (statinfo->delta > statinfo->max_delta) {
636 statinfo->max_delta = statinfo->delta;
637 statinfo->max_nr = pinfo->fd->num;
639 /* maximum and mean jitter calculation */
640 if (statinfo->jitter > statinfo->max_jitter) {
641 statinfo->max_jitter = statinfo->jitter;
643 statinfo->mean_jitter = (statinfo->mean_jitter*statinfo->total_nr + current_diff) / (statinfo->total_nr+1);
645 /* regular payload change? (CN ignored) */
646 if (!(statinfo->flags & STAT_FLAG_FIRST)
647 && !(statinfo->flags & STAT_FLAG_PT_CN)) {
648 if ((statinfo->pt != statinfo->reg_pt)
649 && (statinfo->reg_pt != PT_UNDEFINED)) {
650 statinfo->flags |= STAT_FLAG_REG_PT_CHANGE;
654 /* set regular payload*/
655 if (!(statinfo->flags & STAT_FLAG_PT_CN)) {
656 statinfo->reg_pt = statinfo->pt;
660 /* When calculating expected rtp packets the seq number can wrap around
661 * so we have to count the number of cycles
662 * Variable cycles counts the wraps around in forwarding connection and
663 * under is flag that indicates where we are
665 * XXX how to determine number of cycles with all possible lost, late
666 * and duplicated packets without any doubt? It seems to me, that
667 * because of all possible combination of late, duplicated or lost
668 * packets, this can only be more or less good approximation
670 * There are some combinations (rare but theoretically possible),
671 * where below code won't work correctly - statistic may be wrong then.
674 /* so if the current sequence number is less than the start one
675 * we assume, that there is another cycle running */
676 if ((rtpinfo->info_seq_num < statinfo->start_seq_nr) && (statinfo->under == FALSE)){
678 statinfo->under = TRUE;
680 /* what if the start seq nr was 0? Then the above condition will never
681 * be true, so we add another condition. XXX The problem would arise
682 * if one of the packets with seq nr 0 or 65535 would be lost or late */
683 else if ((rtpinfo->info_seq_num == 0) && (statinfo->stop_seq_nr == 65535) &&
684 (statinfo->under == FALSE)){
686 statinfo->under = TRUE;
688 /* the whole round is over, so reset the flag */
689 else if ((rtpinfo->info_seq_num > statinfo->start_seq_nr) && (statinfo->under != FALSE)) {
690 statinfo->under = FALSE;
693 /* Since it is difficult to count lost, duplicate or late packets separately,
694 * we would like to know at least how many times the sequence number was not ok */
696 /* if the current seq number equals the last one or if we are here for
697 * the first time, then it is ok, we just store the current one as the last one */
698 if ( (statinfo->seq_num+1 == rtpinfo->info_seq_num) || (statinfo->flags & STAT_FLAG_FIRST) )
699 statinfo->seq_num = rtpinfo->info_seq_num;
700 /* if the first one is 65535. XXX same problem as above: if seq 65535 or 0 is lost... */
701 else if ( (statinfo->seq_num == 65535) && (rtpinfo->info_seq_num == 0) )
702 statinfo->seq_num = rtpinfo->info_seq_num;
704 else if (statinfo->seq_num+1 < rtpinfo->info_seq_num) {
705 statinfo->seq_num = rtpinfo->info_seq_num;
706 statinfo->sequence++;
707 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
709 /* late or duplicated */
710 else if (statinfo->seq_num+1 > rtpinfo->info_seq_num) {
711 statinfo->sequence++;
712 statinfo->flags |= STAT_FLAG_WRONG_SEQ;
714 statinfo->time = current_time;
715 statinfo->timestamp = rtpinfo->info_timestamp;
716 statinfo->stop_seq_nr = rtpinfo->info_seq_num;
717 statinfo->total_nr++;
723 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
724 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
725 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
726 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
728 /****************************************************************************/
729 /* adds statistics information from the packet to the clist */
730 static int rtp_packet_add_info(GtkCList *clist,
731 tap_rtp_stat_t *statinfo, packet_info *pinfo,
732 const struct _rtp_info *rtpinfo)
739 GdkColor color = COLOR_DEFAULT;
740 then = pinfo->fd->abs_secs;
741 msecs = (guint16)(pinfo->fd->abs_usecs/1000);
742 tm_tmp = localtime(&then);
743 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
746 tm_tmp->tm_year + 1900,
752 if (statinfo->pt == PT_CN) {
753 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
756 else if (statinfo->pt == PT_CN_OLD) {
757 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
760 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
761 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
764 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
765 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
766 color = COLOR_WARNING;
768 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
769 g_snprintf(status,sizeof(status),"Incorrect timestamp");
770 color = COLOR_WARNING;
772 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
773 && !(statinfo->flags & STAT_FLAG_FIRST)
774 && !(statinfo->flags & STAT_FLAG_PT_CN)
775 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
776 && !(statinfo->flags & STAT_FLAG_MARKER)) {
777 g_snprintf(status,sizeof(status),"Marker missing?");
778 color = COLOR_WARNING;
781 if (statinfo->flags & STAT_FLAG_MARKER) {
782 color = COLOR_WARNING;
784 g_snprintf(status,sizeof(status),OK_TEXT);
786 /* is this the first packet we got in this direction? */
787 if (statinfo->flags & STAT_FLAG_FIRST) {
789 pinfo->fd->num, rtpinfo->info_seq_num,
794 rtpinfo->info_marker_set,
795 timeStr, pinfo->fd->pkt_len,
800 pinfo->fd->num, rtpinfo->info_seq_num,
801 statinfo->delta*1000,
802 statinfo->jitter*1000,
805 rtpinfo->info_marker_set,
806 timeStr, pinfo->fd->pkt_len,
813 /****************************************************************************/
814 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
815 tap_rtp_stat_t *statinfo,
817 const struct _rtp_info *rtpinfo)
823 /* is this the first packet we got in this direction? */
824 if (statinfo->flags & STAT_FLAG_FIRST) {
825 if (saveinfo->fp == NULL) {
826 saveinfo->saved = FALSE;
827 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
830 saveinfo->saved = TRUE;
833 /* save the voice information */
834 /* if there was already an error, we quit */
835 if (saveinfo->saved == FALSE)
838 /* if the captured length and packet length aren't equal, we quit
839 * because there is some information missing */
840 if (pinfo->fd->pkt_len != pinfo->fd->cap_len) {
841 saveinfo->saved = FALSE;
842 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
846 /* if padding bit is set, but the padding count is bigger
847 * then the whole RTP data - error with padding count */
848 if ( (rtpinfo->info_padding_set != FALSE) &&
849 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
850 saveinfo->saved = FALSE;
851 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
855 /* do we need to insert some silence? */
856 if ((rtpinfo->info_marker_set) &&
857 !(statinfo->flags & STAT_FLAG_FIRST) &&
858 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
859 /* the amount of silence should be the difference between
860 * the last timestamp and the current one minus x
861 * x should equal the amount of information in the last frame
862 * XXX not done yet */
863 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
864 rtpinfo->info_padding_count); i++) {
865 tmp = (gint16 )ulaw2linear((unsigned char)(0x55));
866 fwrite(&tmp, 2, 1, saveinfo->fp);
869 fflush(saveinfo->fp);
873 if (rtpinfo->info_payload_type == PT_PCMU) {
874 if (!rtpinfo->info_all_data_present) {
875 /* Not all the data was captured. */
876 saveinfo->saved = FALSE;
877 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
881 /* we put the pointer at the beginning of the RTP
882 * payload, that is, at the beginning of the RTP data
883 * plus the offset of the payload from the beginning
885 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
886 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
887 tmp = (gint16 )ulaw2linear((unsigned char)*data);
888 fwrite(&tmp, 2, 1, saveinfo->fp);
891 fflush(saveinfo->fp);
892 saveinfo->saved = TRUE;
897 else if (rtpinfo->info_payload_type == PT_PCMA) {
898 if (!rtpinfo->info_all_data_present) {
899 /* Not all the data was captured. */
900 saveinfo->saved = FALSE;
901 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
905 /* we put the pointer at the beginning of the RTP
906 * payload, that is, at the beginning of the RTP data
907 * plus the offset of the payload from the beginning
909 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
910 for(i=0; i < (rtpinfo->info_payload_len - rtpinfo->info_padding_count); i++, data++) {
911 tmp = (gint16 )alaw2linear((unsigned char)*data);
912 fwrite(&tmp, 2, 1, saveinfo->fp);
915 fflush(saveinfo->fp);
916 saveinfo->saved = TRUE;
919 /* comfort noise? - do nothing */
920 else if (rtpinfo->info_payload_type == PT_CN
921 || rtpinfo->info_payload_type == PT_CN_OLD) {
923 /* unsupported codec or XXX other error */
925 saveinfo->saved = FALSE;
926 saveinfo->error_type = TAP_RTP_WRONG_CODEC;
934 /****************************************************************************/
937 /****************************************************************************/
938 /* XXX just copied from gtk/rpc_stat.c */
939 void protect_thread_critical_region(void);
940 void unprotect_thread_critical_region(void);
943 /****************************************************************************/
944 /* close the dialog window and remove the tap listener */
945 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
947 /* remove tap listener */
948 protect_thread_critical_region();
949 remove_tap_listener(user_data);
950 unprotect_thread_critical_region();
952 /* close and remove temporary files */
953 if (user_data->forward.saveinfo.fp != NULL)
954 fclose(user_data->forward.saveinfo.fp);
955 if (user_data->reversed.saveinfo.fp != NULL)
956 fclose(user_data->reversed.saveinfo.fp);
957 /*XXX: test for error **/
958 remove(user_data->f_tempname);
959 remove(user_data->r_tempname);
961 /* destroy save_voice_as window if open */
962 if (user_data->dlg.save_voice_as_w != NULL)
963 window_destroy(user_data->dlg.save_voice_as_w);
965 /* destroy graph window if open */
966 if (user_data->dlg.dialog_graph.window != NULL)
967 window_destroy(user_data->dlg.dialog_graph.window);
969 #ifdef USE_CONVERSATION_GRAPH
970 /* destroy graph window if open */
971 if (user_data->dlg.graph_window != NULL)
972 window_destroy(user_data->dlg.graph_window);
975 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exeption using GTK1*/
976 gtk_signal_disconnect(GTK_OBJECT(user_data->dlg.notebook), user_data->dlg.notebook_signal_id);
978 g_free(user_data->dlg.col_arrows_fwd);
979 g_free(user_data->dlg.col_arrows_rev);
984 /****************************************************************************/
985 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
986 GtkNotebookPage *page _U_,
988 user_data_t *user_data _U_)
990 user_data->dlg.selected_clist =
991 (page_num==0) ? user_data->dlg.clist_fwd : user_data->dlg.clist_rev ;
992 user_data->dlg.selected_row = 0;
995 /****************************************************************************/
996 static void on_clist_select_row(GtkCList *clist _U_,
1000 user_data_t *user_data _U_)
1002 user_data->dlg.selected_clist = clist;
1003 user_data->dlg.selected_row = row;
1007 #ifdef USE_CONVERSATION_GRAPH
1008 /****************************************************************************/
1009 /* when the graph window gets destroyed */
1010 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
1012 /* note that graph window has been destroyed */
1013 user_data->dlg.graph_window = NULL;
1016 /****************************************************************************/
1017 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
1020 GtkCList *clist = NULL;
1021 if (vp.fnumber != 0) {
1022 clist = GTK_CLIST(user_data->dlg.clist_fwd);
1023 row = gtk_clist_find_row_from_data(clist,
1024 GUINT_TO_POINTER(vp.fnumber));
1026 clist = GTK_CLIST(user_data->dlg.clist_rev);
1027 row = gtk_clist_find_row_from_data(clist,
1028 GUINT_TO_POINTER(vp.fnumber));
1031 gtk_notebook_set_page(GTK_NOTEBOOK(user_data->dlg.notebook),
1032 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
1033 gtk_clist_select_row(clist, row, 0);
1034 gtk_clist_moveto(clist, row, 0, 0.5, 0);
1040 /****************************************************************************/
1041 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1047 if (user_data->dlg.graph_window != NULL) {
1048 /* There's already a graph window; reactivate it. */
1049 reactivate_window(user_data->dlg.graph_window);
1052 list = g_list_append(list, &(user_data->series_fwd));
1053 list = g_list_append(list, &(user_data->series_rev));
1055 user_data->series_fwd.color.pixel = 0;
1056 user_data->series_fwd.color.red = 0x80ff;
1057 user_data->series_fwd.color.green = 0xe0ff;
1058 user_data->series_fwd.color.blue = 0xffff;
1059 user_data->series_fwd.yvalue = 0.5;
1061 user_data->series_rev.color.pixel = 0;
1062 user_data->series_rev.color.red = 0x60ff;
1063 user_data->series_rev.color.green = 0xc0ff;
1064 user_data->series_rev.color.blue = 0xffff;
1065 user_data->series_rev.yvalue = -0.5;
1067 g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=%u)",
1068 get_addr_name(&(user_data->ip_src_fwd)),
1069 user_data->port_src_fwd,
1070 get_addr_name(&(user_data->ip_dst_fwd)),
1071 user_data->port_dst_fwd,
1072 user_data->ssrc_fwd);
1074 g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=%u)",
1075 get_addr_name(&(user_data->ip_src_rev)),
1076 user_data->port_src_rev,
1077 get_addr_name(&(user_data->ip_dst_rev)),
1078 user_data->port_dst_rev,
1079 user_data->ssrc_rev);
1081 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
1082 &graph_selection_callback, user_data);
1083 SIGNAL_CONNECT(user_data->dlg.graph_window, "destroy",
1084 on_destroy_graph, user_data);
1086 #endif /*USE_CONVERSATION_GRAPH*/
1088 /****************************************************************************/
1089 static void dialog_graph_set_title(user_data_t* user_data)
1092 if (!user_data->dlg.dialog_graph.window){
1095 title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
1096 get_addr_name(&(user_data->ip_src_fwd)),
1097 user_data->port_src_fwd,
1098 get_addr_name(&(user_data->ip_dst_fwd)),
1099 user_data->port_dst_fwd,
1100 get_addr_name(&(user_data->ip_src_rev)),
1101 user_data->port_src_rev,
1102 get_addr_name(&(user_data->ip_dst_rev)),
1103 user_data->port_dst_rev);
1105 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
1111 /****************************************************************************/
1112 static void dialog_graph_reset(user_data_t* user_data)
1116 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1117 for(i=0;i<MAX_GRAPHS;i++){
1118 for(j=0;j<NUM_GRAPH_ITEMS;j++){
1119 dialog_graph_graph_item_t *dggi;
1120 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
1125 user_data->dlg.dialog_graph.last_interval=0xffffffff;
1126 user_data->dlg.dialog_graph.max_interval=0;
1127 user_data->dlg.dialog_graph.num_items=0;
1129 /* create the color titles near the filter buttons */
1130 for(i=0;i<MAX_GRAPHS;i++){
1133 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=%u)",
1135 get_addr_name(&(user_data->ip_src_fwd)),
1136 user_data->port_src_fwd,
1137 get_addr_name(&(user_data->ip_dst_fwd)),
1138 user_data->port_dst_fwd,
1139 user_data->ssrc_fwd);
1142 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=%u)",
1144 get_addr_name(&(user_data->ip_src_rev)),
1145 user_data->port_src_rev,
1146 get_addr_name(&(user_data->ip_dst_rev)),
1147 user_data->port_dst_rev,
1148 user_data->ssrc_rev);
1152 dialog_graph_set_title(user_data);
1155 /****************************************************************************/
1156 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
1158 dialog_graph_graph_item_t *it;
1160 it=&dgg->items[idx];
1165 /****************************************************************************/
1166 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
1169 g_snprintf(buf, buf_len, "%ds",t/1000000);
1170 } else if(t>=1000000){
1171 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
1172 } else if(t>=10000){
1173 g_snprintf(buf, buf_len, "%dms",t/1000);
1175 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
1177 g_snprintf(buf, buf_len, "%dus",t);
1181 /****************************************************************************/
1182 static void dialog_graph_draw(user_data_t* user_data)
1185 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
1186 gint32 current_interval;
1187 guint32 left_x_border;
1188 guint32 right_x_border;
1189 guint32 top_y_border;
1190 guint32 bottom_y_border;
1191 #if GTK_MAJOR_VERSION < 2
1194 PangoLayout *layout;
1196 guint32 label_width, label_height;
1197 guint32 draw_width, draw_height;
1198 char label_string[15];
1201 guint32 num_time_intervals;
1202 guint32 max_value; /* max value of seen data */
1203 guint32 max_y; /* max value of the Y scale */
1205 #if GTK_MAJOR_VERSION <2
1206 font = user_data->dlg.dialog_graph.draw_area->style->font;
1208 if(!user_data->dlg.dialog_graph.needs_redraw){
1211 user_data->dlg.dialog_graph.needs_redraw=FALSE;
1214 * Find the length of the intervals we have data for
1215 * so we know how large arrays we need to malloc()
1217 num_time_intervals=user_data->dlg.dialog_graph.num_items;
1218 /* if there isnt anything to do, just return */
1219 if(num_time_intervals==0){
1222 num_time_intervals+=1;
1223 /* XXX move this check to _packet() */
1224 if(num_time_intervals>NUM_GRAPH_ITEMS){
1225 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
1230 * find the max value so we can autoscale the y axis
1233 for(i=0;i<MAX_GRAPHS;i++){
1236 if(!user_data->dlg.dialog_graph.graph[i].display){
1239 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
1242 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1244 /* keep track of the max value we have encountered */
1252 * Clear out old plot
1254 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1255 user_data->dlg.dialog_graph.draw_area->style->white_gc,
1258 user_data->dlg.dialog_graph.draw_area->allocation.width,
1259 user_data->dlg.dialog_graph.draw_area->allocation.height);
1263 * Calculate the y scale we should use
1265 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1266 max_y=yscale_max[MAX_YSCALE-1];
1267 for(i=MAX_YSCALE-1;i>0;i--){
1268 if(max_value<yscale_max[i]){
1269 max_y=yscale_max[i];
1273 /* the user had specified an explicit y scale to use */
1274 max_y=user_data->dlg.dialog_graph.max_y_units;
1278 * Calculate size of borders surrounding the plot
1279 * The border on the right side needs to be adjusted depending
1280 * on the width of the text labels. For simplicity we assume that the
1281 * top y scale label will be the widest one
1283 print_time_scale_string(label_string, 15, max_y);
1284 #if GTK_MAJOR_VERSION < 2
1285 label_width=gdk_string_width(font, label_string);
1286 label_height=gdk_string_height(font, label_string);
1288 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1289 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1292 right_x_border=label_width+20;
1294 bottom_y_border=label_height+20;
1298 * Calculate the size of the drawing area for the actual plot
1300 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1301 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1305 * Draw the y axis and labels
1306 * (we always draw the y scale with 11 ticks along the axis)
1308 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1309 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1311 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1312 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1318 /* first, middle and last tick are slightly longer */
1322 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1323 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1324 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1325 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1326 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1327 /* draw the labels */
1329 print_time_scale_string(label_string, 15, (max_y*i/10));
1330 #if GTK_MAJOR_VERSION < 2
1331 lwidth=gdk_string_width(font, label_string);
1332 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1334 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1335 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1336 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1339 pango_layout_set_text(layout, label_string, -1);
1340 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1341 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1342 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1343 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1344 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1349 print_time_scale_string(label_string, 15, (max_y*i/10));
1350 #if GTK_MAJOR_VERSION < 2
1351 lwidth=gdk_string_width(font, label_string);
1352 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1354 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1355 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1356 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1359 pango_layout_set_text(layout, label_string, -1);
1360 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1361 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1362 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1363 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1364 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1369 print_time_scale_string(label_string, 15, (max_y*i/10));
1370 #if GTK_MAJOR_VERSION < 2
1371 lwidth=gdk_string_width(font, label_string);
1372 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1374 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1375 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1376 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10+label_height/2,
1379 pango_layout_set_text(layout, label_string, -1);
1380 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1381 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1382 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1383 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1384 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1393 * if we have not specified the last_interval via the gui,
1394 * then just pick the current end of the capture so that is scrolls
1395 * nicely when doing live captures
1397 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1398 last_interval=user_data->dlg.dialog_graph.max_interval;
1400 last_interval=user_data->dlg.dialog_graph.last_interval;
1407 /* plot the x-scale */
1408 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);
1410 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1411 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1412 first_interval*=user_data->dlg.dialog_graph.interval;
1419 while(interval_delta<((last_interval-first_interval)/10)){
1420 interval_delta*=delta_multiplier;
1421 if(delta_multiplier==5){
1428 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1431 /* if pixels_per_tick is <5, only draw every 10 ticks */
1432 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1436 if(current_interval%interval_delta){
1442 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1443 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1444 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1445 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1446 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1447 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1451 if(user_data->dlg.dialog_graph.interval>=1000){
1452 g_snprintf(label_string, 15, "%ds", current_interval/1000);
1453 } else if(user_data->dlg.dialog_graph.interval>=100){
1454 g_snprintf(label_string, 15, "%d.%1ds", current_interval/1000,(current_interval/100)%10)
1456 } else if(user_data->dlg.dialog_graph.interval>=10){
1457 g_snprintf(label_string, 15, "%d.%2ds", current_interval/1000,(current_interval/10)%100)
1460 g_snprintf(label_string, 15, "%d.%3ds", current_interval/1000,current_interval%1000);
1462 #if GTK_MAJOR_VERSION < 2
1463 lwidth=gdk_string_width(font, label_string);
1464 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1466 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1467 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1468 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20+label_height,
1471 pango_layout_set_text(layout, label_string, -1);
1472 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1473 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1474 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1475 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1476 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1489 * Draw "x" for Sequence Errors and "m" for Marks
1491 /* Draw the labels Fwd and Rev */
1492 strcpy(label_string,"<-Fwd");
1493 #if GTK_MAJOR_VERSION < 2
1494 lwidth=gdk_string_width(font, label_string);
1495 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1497 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1498 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1499 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+label_height,
1502 pango_layout_set_text(layout, label_string, -1);
1503 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1504 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1505 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1506 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1507 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1510 strcpy(label_string,"<-Rev");
1511 #if GTK_MAJOR_VERSION < 2
1512 lwidth=gdk_string_width(font, label_string);
1513 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1515 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1516 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1517 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9+label_height,
1520 pango_layout_set_text(layout, label_string, -1);
1521 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1522 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1523 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1524 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1525 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1529 /* Draw the marks */
1530 for(i=MAX_GRAPHS-1;i>=0;i--){
1532 guint32 x_pos, prev_x_pos;
1534 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1535 if (!user_data->dlg.dialog_graph.graph[i].display){
1538 /* initialize prev x/y to the low left corner of the graph */
1539 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;
1541 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1542 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;
1544 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1546 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1547 strcpy(label_string,"x");
1549 strcpy(label_string,"m");
1552 #if GTK_MAJOR_VERSION < 2
1553 lwidth=gdk_string_width(font, label_string);
1554 gdk_draw_string(user_data->dlg.dialog_graph.pixmap,
1556 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1558 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2)+label_height,
1561 pango_layout_set_text(layout, label_string, -1);
1562 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1563 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1564 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1566 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1575 #if GTK_MAJOR_VERSION >= 2
1576 g_object_unref(G_OBJECT(layout));
1581 * Loop over all graphs and draw them
1583 for(i=MAX_GRAPHS-1;i>=0;i--){
1585 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1586 if (!user_data->dlg.dialog_graph.graph[i].display){
1589 /* initialize prev x/y to the low left corner of the graph */
1590 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;
1591 prev_y_pos=draw_height-1+top_y_border;
1593 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1595 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;
1596 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1600 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1603 /* dont need to draw anything if the segment
1604 * is entirely above the top of the graph
1606 if( (prev_y_pos==0) && (y_pos==0) ){
1613 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1614 x_pos, draw_height-1+top_y_border,
1624 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1625 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1626 user_data->dlg.dialog_graph.pixmap,
1629 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1632 /* update the scrollbar */
1633 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1634 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1635 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1636 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1637 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1639 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1641 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1642 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1643 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1647 /****************************************************************************/
1648 static void dialog_graph_redraw(user_data_t* user_data)
1650 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1651 dialog_graph_draw(user_data);
1654 /****************************************************************************/
1655 static gint quit(GtkWidget *widget, GdkEventExpose *event _U_)
1657 user_data_t *user_data;
1659 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1661 user_data->dlg.dialog_graph.window = NULL;
1665 /****************************************************************************/
1666 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1668 user_data_t *user_data;
1670 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1676 gdk_draw_pixmap(widget->window,
1677 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1678 user_data->dlg.dialog_graph.pixmap,
1679 event->area.x, event->area.y,
1680 event->area.x, event->area.y,
1681 event->area.width, event->area.height);
1686 /****************************************************************************/
1687 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1689 user_data_t *user_data;
1692 user_data=(user_data_t *)OBJECT_GET_DATA(widget, "user_data_t");
1698 if(user_data->dlg.dialog_graph.pixmap){
1699 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1700 user_data->dlg.dialog_graph.pixmap=NULL;
1703 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1704 widget->allocation.width,
1705 widget->allocation.height,
1707 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1708 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1710 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1711 widget->style->white_gc,
1714 widget->allocation.width,
1715 widget->allocation.height);
1717 /* set up the colors and the GC structs for this pixmap */
1718 for(i=0;i<MAX_GRAPHS;i++){
1719 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1720 #if GTK_MAJOR_VERSION < 2
1721 colormap = gtk_widget_get_colormap (widget);
1722 if (!gdk_color_alloc (colormap, &user_data->dlg.dialog_graph.graph[i].color)){
1723 g_warning ("Couldn't allocate color");
1726 gdk_gc_set_foreground(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1728 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1732 dialog_graph_redraw(user_data);
1736 /****************************************************************************/
1737 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1739 user_data_t *user_data=(user_data_t *)data;
1742 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1743 if(user_data->dlg.dialog_graph.last_interval==mi){
1746 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1747 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1751 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1753 dialog_graph_redraw(user_data);
1757 /****************************************************************************/
1758 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1760 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1761 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "destroy", quit, user_data);
1762 OBJECT_SET_DATA(user_data->dlg.dialog_graph.draw_area, "user_data_t", user_data);
1764 WIDGET_SET_SIZE(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1766 /* signals needed to handle backing pixmap */
1767 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "expose_event", expose_event, NULL);
1768 SIGNAL_CONNECT(user_data->dlg.dialog_graph.draw_area, "configure_event", configure_event, user_data);
1770 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1771 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1773 /* create the associated scrollbar */
1774 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1775 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1776 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1777 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1778 SIGNAL_CONNECT(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", scrollbar_changed, user_data);
1781 /****************************************************************************/
1782 static void disable_graph(dialog_graph_graph_t *dgg)
1786 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1791 /****************************************************************************/
1792 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1794 /* this graph is not active, just update display and redraw */
1795 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1797 dialog_graph_redraw(dgg->ud);
1802 cf_retap_packets(&cfile);
1803 dialog_graph_redraw(dgg->ud);
1808 /****************************************************************************/
1809 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1815 hbox=gtk_hbox_new(FALSE, 3);
1816 gtk_container_add(GTK_CONTAINER(box), hbox);
1817 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1818 gtk_widget_show(hbox);
1820 g_snprintf(str, 256, "Graph %d", num);
1821 dgg->display_button=gtk_toggle_button_new_with_label(str);
1822 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1823 gtk_widget_show(dgg->display_button);
1824 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1825 SIGNAL_CONNECT(dgg->display_button, "toggled", filter_callback, dgg);
1827 label=gtk_label_new(dgg->title);
1828 gtk_widget_show(label);
1829 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1831 #if GTK_MAJOR_VERSION < 2
1832 /* setting the color of the display button doesn't work */
1833 rc_style = gtk_rc_style_new ();
1834 rc_style->fg[GTK_STATE_NORMAL] = dgg->color;
1835 rc_style->color_flags[GTK_STATE_NORMAL] |= GTK_RC_FG;
1836 rc_style->fg[GTK_STATE_ACTIVE] = dgg->color;
1837 rc_style->color_flags[GTK_STATE_ACTIVE] |= GTK_RC_FG;
1838 rc_style->fg[GTK_STATE_PRELIGHT] = dgg->color;
1839 rc_style->color_flags[GTK_STATE_PRELIGHT] |= GTK_RC_FG;
1840 rc_style->fg[GTK_STATE_SELECTED] = dgg->color;
1841 rc_style->color_flags[GTK_STATE_SELECTED] |= GTK_RC_FG;
1842 rc_style->fg[GTK_STATE_INSENSITIVE] = dgg->color;
1843 rc_style->color_flags[GTK_STATE_INSENSITIVE] |= GTK_RC_FG;
1844 gtk_widget_modify_style (label, rc_style);
1845 gtk_rc_style_unref (rc_style);
1847 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1848 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1849 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1850 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1851 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1857 /****************************************************************************/
1858 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1865 frame=gtk_frame_new("Graphs");
1866 gtk_container_add(GTK_CONTAINER(box), frame);
1867 gtk_widget_show(frame);
1869 vbox=gtk_vbox_new(FALSE, 1);
1870 gtk_container_add(GTK_CONTAINER(frame), vbox);
1871 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
1872 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1873 gtk_widget_show(vbox);
1875 for(i=0;i<MAX_GRAPHS;i++){
1876 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1879 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1880 gtk_widget_show(label);
1881 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1886 /****************************************************************************/
1887 static void yscale_select(GtkWidget *item, gpointer key)
1890 user_data_t *user_data;
1892 user_data=(user_data_t *)key;
1893 val=(int)OBJECT_GET_DATA(item, "yscale_max");
1895 user_data->dlg.dialog_graph.max_y_units=val;
1896 dialog_graph_redraw(user_data);
1899 /****************************************************************************/
1900 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1903 user_data_t *user_data;
1905 user_data=(user_data_t *)key;
1906 val=(int)OBJECT_GET_DATA(item, "pixels_per_tick");
1907 user_data->dlg.dialog_graph.pixels_per_tick=val;
1908 dialog_graph_redraw(user_data);
1911 /****************************************************************************/
1912 static void tick_interval_select(GtkWidget *item, gpointer key)
1915 user_data_t *user_data;
1917 user_data=(user_data_t *)key;
1918 val=(int)OBJECT_GET_DATA(item, "tick_interval");
1920 user_data->dlg.dialog_graph.interval=val;
1921 cf_retap_packets(&cfile);
1922 dialog_graph_redraw(user_data);
1925 /****************************************************************************/
1926 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1929 GtkWidget *menu_item;
1932 for(i=0;i<MAX_YSCALE;i++){
1933 if(yscale_max[i]==AUTO_MAX_YSCALE){
1936 g_snprintf(str, 15, "%u ms", yscale_max[i]/1000);
1938 menu_item=gtk_menu_item_new_with_label(str);
1939 OBJECT_SET_DATA(menu_item, "yscale_max",
1940 GUINT_TO_POINTER(yscale_max[i]));
1941 SIGNAL_CONNECT(menu_item, "activate", yscale_select, user_data);
1942 gtk_widget_show(menu_item);
1943 gtk_menu_append(GTK_MENU(menu), menu_item);
1948 /****************************************************************************/
1949 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
1952 GtkWidget *menu_item;
1955 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1956 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1957 menu_item=gtk_menu_item_new_with_label(str);
1959 OBJECT_SET_DATA(menu_item, "pixels_per_tick",
1960 GUINT_TO_POINTER(pixels_per_tick[i]));
1961 SIGNAL_CONNECT(menu_item, "activate", pixels_per_tick_select, user_data);
1962 gtk_widget_show(menu_item);
1963 gtk_menu_append(GTK_MENU(menu), menu_item);
1965 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1970 /****************************************************************************/
1971 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
1974 GtkWidget *menu_item;
1977 for(i=0;i<MAX_TICK_VALUES;i++){
1978 if(tick_interval_values[i]>=1000){
1979 g_snprintf(str, 15, "%u sec", tick_interval_values[i]/1000);
1980 } else if(tick_interval_values[i]>=100){
1981 g_snprintf(str, 15, "0.%1u sec", (tick_interval_values[i]/100)%10);
1982 } else if(tick_interval_values[i]>=10){
1983 g_snprintf(str, 15, "0.%02u sec", (tick_interval_values[i]/10)%10);
1985 g_snprintf(str, 15, "0.%03u sec", (tick_interval_values[i])%10);
1988 menu_item=gtk_menu_item_new_with_label(str);
1989 OBJECT_SET_DATA(menu_item, "tick_interval",
1990 GUINT_TO_POINTER(tick_interval_values[i]));
1991 SIGNAL_CONNECT(menu_item, "activate", tick_interval_select, (gpointer)user_data);
1992 gtk_widget_show(menu_item);
1993 gtk_menu_append(GTK_MENU(menu), menu_item);
1995 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1999 /****************************************************************************/
2000 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
2004 GtkWidget *option_menu;
2007 hbox=gtk_hbox_new(FALSE, 0);
2008 gtk_container_add(GTK_CONTAINER(box), hbox);
2009 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2010 gtk_widget_show(hbox);
2012 label=gtk_label_new(name);
2013 gtk_widget_show(label);
2014 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
2016 option_menu=gtk_option_menu_new();
2017 menu=gtk_menu_new();
2018 (*func)(user_data, menu);
2019 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
2020 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
2021 gtk_widget_show(option_menu);
2024 /****************************************************************************/
2025 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
2027 GtkWidget *frame_vbox;
2031 frame_vbox=gtk_vbox_new(FALSE, 0);
2032 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
2033 gtk_widget_show(frame_vbox);
2035 frame = gtk_frame_new("X Axis");
2036 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
2037 gtk_widget_show(frame);
2039 vbox=gtk_vbox_new(FALSE, 0);
2040 gtk_container_add(GTK_CONTAINER(frame), vbox);
2041 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
2042 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
2043 gtk_widget_show(vbox);
2045 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
2046 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
2048 frame = gtk_frame_new("Y Axis");
2049 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
2050 gtk_widget_show(frame);
2052 vbox=gtk_vbox_new(FALSE, 0);
2053 gtk_container_add(GTK_CONTAINER(frame), vbox);
2054 gtk_container_border_width(GTK_CONTAINER(vbox), 3);
2055 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
2056 gtk_widget_show(vbox);
2058 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
2063 /****************************************************************************/
2064 static void dialog_graph_init_window(user_data_t* user_data)
2068 GtkWidget *bt_close;
2070 /* create the main window */
2071 user_data->dlg.dialog_graph.window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
2073 vbox=gtk_vbox_new(FALSE, 0);
2074 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
2075 gtk_widget_show(vbox);
2077 create_draw_area(user_data, vbox);
2079 hbox=gtk_hbox_new(FALSE, 3);
2080 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
2081 gtk_container_border_width(GTK_CONTAINER(hbox), 3);
2082 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
2083 gtk_widget_show(hbox);
2085 create_filter_area(user_data, hbox);
2086 create_ctrl_area(user_data, hbox);
2088 dialog_graph_set_title(user_data);
2090 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
2091 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
2092 gtk_widget_show(hbox);
2094 bt_close = OBJECT_GET_DATA(hbox, GTK_STOCK_CLOSE);
2095 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
2097 SIGNAL_CONNECT(user_data->dlg.dialog_graph.window, "delete_event", window_delete_event_cb, NULL);
2099 gtk_widget_show(user_data->dlg.dialog_graph.window);
2100 window_present(user_data->dlg.dialog_graph.window);
2105 /****************************************************************************/
2106 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2108 if (user_data->dlg.dialog_graph.window != NULL) {
2109 /* There's already a graph window; reactivate it. */
2110 reactivate_window(user_data->dlg.dialog_graph.window);
2114 dialog_graph_init_window(user_data);
2118 /****************************************************************************/
2119 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2123 if (user_data->dlg.selected_clist!=NULL) {
2124 fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
2125 GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
2126 cf_goto_frame(&cfile, fnumber);
2131 static void draw_stat(user_data_t *user_data);
2133 /****************************************************************************/
2134 /* re-dissects all packets */
2135 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2137 GString *error_string;
2139 /* remove tap listener */
2140 protect_thread_critical_region();
2141 remove_tap_listener(user_data);
2142 unprotect_thread_critical_region();
2144 /* register tap listener */
2145 error_string = register_tap_listener("rtp", user_data, NULL,
2146 rtp_reset, rtp_packet, rtp_draw);
2147 if (error_string != NULL) {
2148 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
2149 g_string_free(error_string, TRUE);
2153 /* retap all packets */
2154 cf_retap_packets(&cfile);
2156 /* draw statistics info */
2157 draw_stat(user_data);
2159 gtk_clist_sort(user_data->dlg.clist_fwd);
2160 gtk_clist_sort(user_data->dlg.clist_rev);
2163 /****************************************************************************/
2164 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2169 if (user_data->dlg.selected_clist==NULL)
2172 clist = user_data->dlg.selected_clist;
2173 row = user_data->dlg.selected_row + 1;
2175 while (gtk_clist_get_text(clist,row,6,&text)) {
2176 if (strcmp(text, OK_TEXT) != 0) {
2177 gtk_clist_select_row(clist, row, 0);
2178 gtk_clist_moveto(clist, row, 0, 0.5, 0);
2186 while (gtk_clist_get_text(clist,row,6,&text) && row<user_data->dlg.selected_row) {
2187 if (strcmp(text, OK_TEXT) != 0) {
2188 gtk_clist_select_row(clist, row, 0);
2189 gtk_clist_moveto(clist, row, 0, 0.5, 0);
2196 /****************************************************************************/
2197 /* when we want to save the information */
2198 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
2201 GtkWidget *rev, *forw, *both;
2202 user_data_t *user_data;
2208 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
2210 /* Perhaps the user specified a directory instead of a file.
2211 Check whether they did. */
2212 if (test_for_directory(g_dest) == EISDIR) {
2213 /* It's a directory - set the file selection box to display it. */
2214 set_last_open_dir(g_dest);
2216 file_selection_set_current_folder(fs, get_last_open_dir());
2220 rev = (GtkWidget*)OBJECT_GET_DATA(bt, "reversed_rb");
2221 forw = (GtkWidget*)OBJECT_GET_DATA(bt, "forward_rb");
2222 both = (GtkWidget*)OBJECT_GET_DATA(bt, "both_rb");
2223 user_data = (user_data_t*)OBJECT_GET_DATA(bt, "user_data");
2225 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
2226 fp = fopen(g_dest, "w");
2228 open_failure_alert_box(g_dest, errno, TRUE);
2232 if (GTK_TOGGLE_BUTTON(both)->active) {
2233 fprintf(fp, "Forward\n");
2235 write_failure_alert_box(g_dest, errno);
2241 for(j = 0; j < NUM_COLS; j++) {
2243 fprintf(fp,"%s",titles[j]);
2245 fprintf(fp,",%s",titles[j]);
2250 write_failure_alert_box(g_dest, errno);
2254 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
2255 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
2256 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
2258 fprintf(fp,"%s",columnText);
2260 fprintf(fp,",%s",columnText);
2265 write_failure_alert_box(g_dest, errno);
2271 if (fclose(fp) == EOF) {
2272 write_failure_alert_box(g_dest, errno);
2277 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
2279 if (GTK_TOGGLE_BUTTON(both)->active) {
2280 fp = fopen(g_dest, "a");
2282 open_failure_alert_box(g_dest, errno, TRUE);
2285 fprintf(fp, "\nReverse\n");
2287 write_failure_alert_box(g_dest, errno);
2292 fp = fopen(g_dest, "w");
2294 open_failure_alert_box(g_dest, errno, TRUE);
2298 for(j = 0; j < NUM_COLS; j++) {
2300 fprintf(fp,"%s",titles[j]);
2302 fprintf(fp,",%s",titles[j]);
2307 write_failure_alert_box(g_dest, errno);
2311 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
2312 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
2313 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
2315 fprintf(fp,"%s",columnText);
2317 fprintf(fp,",%s",columnText);
2322 write_failure_alert_box(g_dest, errno);
2327 if (fclose(fp) == EOF) {
2328 write_failure_alert_box(g_dest, errno);
2333 window_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
2336 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2338 user_data->dlg.save_csv_as_w = NULL;
2341 /* when the user wants to save the csv information in a file */
2342 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
2346 GtkWidget *label_format;
2347 GtkWidget *channels_label;
2348 GSList *channels_group = NULL;
2349 GtkWidget *forward_rb;
2350 GtkWidget *reversed_rb;
2354 if (user_data->dlg.save_csv_as_w != NULL) {
2355 /* There's already a Save CSV info dialog box; reactivate it. */
2356 reactivate_window(user_data->dlg.save_csv_as_w);
2360 user_data->dlg.save_csv_as_w = gtk_file_selection_new("Ethereal: Save Data As CSV");
2362 /* Container for each row of widgets */
2363 vertb = gtk_vbox_new(FALSE, 0);
2364 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
2365 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
2366 vertb, FALSE, FALSE, 0);
2367 gtk_widget_show (vertb);
2369 table1 = gtk_table_new (2, 4, FALSE);
2370 gtk_widget_show (table1);
2371 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2372 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2373 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2375 label_format = gtk_label_new ("Format: Comma Separated Values");
2376 gtk_widget_show (label_format);
2377 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2378 (GtkAttachOptions) (GTK_FILL),
2379 (GtkAttachOptions) (0), 0, 0);
2382 channels_label = gtk_label_new ("Channels:");
2383 gtk_widget_show (channels_label);
2384 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2385 (GtkAttachOptions) (GTK_FILL),
2386 (GtkAttachOptions) (0), 0, 0);
2387 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2389 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2390 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
2391 gtk_widget_show (forward_rb);
2392 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2393 (GtkAttachOptions) (GTK_FILL),
2394 (GtkAttachOptions) (0), 0, 0);
2396 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2397 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
2398 gtk_widget_show (reversed_rb);
2399 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2400 (GtkAttachOptions) (GTK_FILL),
2401 (GtkAttachOptions) (0), 0, 0);
2403 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2404 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
2405 gtk_widget_show (both_rb);
2406 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2407 (GtkAttachOptions) (GTK_FILL),
2408 (GtkAttachOptions) (0), 0, 0);
2410 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2412 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
2413 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
2414 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
2415 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
2416 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
2417 SIGNAL_CONNECT(ok_bt, "clicked", save_csv_as_ok_cb,
2418 user_data->dlg.save_csv_as_w);
2420 window_set_cancel_button(user_data->dlg.save_csv_as_w,
2421 GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button, NULL);
2423 SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "delete_event", window_delete_event_cb, NULL);
2424 SIGNAL_CONNECT(user_data->dlg.save_csv_as_w, "destroy",
2425 save_csv_as_destroy_cb, user_data);
2427 gtk_widget_show(user_data->dlg.save_csv_as_w);
2428 window_present(user_data->dlg.save_csv_as_w);
2432 /****************************************************************************/
2433 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2435 /* Note that we no longer have a Save voice info dialog box. */
2436 user_data->dlg.save_voice_as_w = NULL;
2439 /****************************************************************************/
2440 /* here we save it into a file that user specified */
2441 /* XXX what about endians here? could go something wrong? */
2442 static gboolean copy_file(gchar *dest, gint channels, /*gint format,*/ user_data_t *user_data)
2444 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
2448 guint32 f_write_silence = 0;
2449 guint32 r_write_silence = 0;
2451 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2452 gboolean stop_flag = FALSE;
2454 forw_fd = open(user_data->f_tempname, O_RDONLY | O_BINARY);
2457 rev_fd = open(user_data->r_tempname, O_RDONLY | O_BINARY);
2463 /* open file for saving */
2464 to_fd = open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2471 progbar = create_progress_dlg("Saving voice in a file", dest, &stop_flag);
2473 /* First we write the .au header. XXX Hope this is endian independant */
2474 /* the magic word 0x2e736e64 == .snd */
2475 *pd = (unsigned char)0x2e; write(to_fd, pd, 1);
2476 *pd = (unsigned char)0x73; write(to_fd, pd, 1);
2477 *pd = (unsigned char)0x6e; write(to_fd, pd, 1);
2478 *pd = (unsigned char)0x64; write(to_fd, pd, 1);
2479 /* header offset == 24 bytes */
2480 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2481 write(to_fd, pd, 1);
2482 write(to_fd, pd, 1);
2483 *pd = (unsigned char)0x18; write(to_fd, pd, 1);
2484 /* total length, it is permited to set this to 0xffffffff */
2485 *pd = (unsigned char)0xff; write(to_fd, pd, 1);
2486 write(to_fd, pd, 1);
2487 write(to_fd, pd, 1);
2488 write(to_fd, pd, 1);
2489 /* encoding format == 8 bit ulaw */
2490 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2491 write(to_fd, pd, 1);
2492 write(to_fd, pd, 1);
2493 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
2494 /* sample rate == 8000 Hz */
2495 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2496 write(to_fd, pd, 1);
2497 *pd = (unsigned char)0x1f; write(to_fd, pd, 1);
2498 *pd = (unsigned char)0x40; write(to_fd, pd, 1);
2500 *pd = (unsigned char)0x00; write(to_fd, pd, 1);
2501 write(to_fd, pd, 1);
2502 write(to_fd, pd, 1);
2503 *pd = (unsigned char)0x01; write(to_fd, pd, 1);
2506 /* only forward direction */
2508 progbar_count = user_data->forward.saveinfo.count;
2509 progbar_quantum = user_data->forward.saveinfo.count/100;
2510 while ((fread = read(forw_fd, &f_pd, 2)) > 0) {
2513 if((count > progbar_nextstep) && (count <= progbar_count)) {
2514 update_progress_dlg(progbar,
2515 (gfloat) count/progbar_count, "Saving");
2516 progbar_nextstep = progbar_nextstep + progbar_quantum;
2519 *pd = (unsigned char)linear2ulaw(f_pd);
2520 fwritten = write(to_fd, pd, 1);
2521 if ((fwritten*2 < fread) || (fwritten < 0) || (fread < 0)) {
2525 destroy_progress_dlg(progbar);
2531 /* only reversed direction */
2533 progbar_count = user_data->reversed.saveinfo.count;
2534 progbar_quantum = user_data->reversed.saveinfo.count/100;
2535 while ((rread = read(rev_fd, &r_pd, 2)) > 0) {
2538 if((count > progbar_nextstep) && (count <= progbar_count)) {
2539 update_progress_dlg(progbar,
2540 (gfloat) count/progbar_count, "Saving");
2541 progbar_nextstep = progbar_nextstep + progbar_quantum;
2544 *pd = (unsigned char)linear2ulaw(r_pd);
2545 rwritten = write(to_fd, pd, 1);
2546 if ((rwritten*2 < rread) || (rwritten < 0) || (rread < 0)) {
2550 destroy_progress_dlg(progbar);
2556 /* both directions */
2558 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2559 (progbar_count = user_data->forward.saveinfo.count) :
2560 (progbar_count = user_data->reversed.saveinfo.count);
2561 progbar_quantum = progbar_count/100;
2562 /* since conversation in one way can start later than in the other one,
2563 * we have to write some silence information for one channel */
2564 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2565 f_write_silence = (guint32)
2566 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2568 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2569 r_write_silence = (guint32)
2570 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2575 if((count > progbar_nextstep) && (count <= progbar_count)) {
2576 update_progress_dlg(progbar,
2577 (gfloat) count/progbar_count, "Saving");
2578 progbar_nextstep = progbar_nextstep + progbar_quantum;
2581 if(f_write_silence > 0) {
2582 rread = read(rev_fd, &r_pd, 2);
2587 else if(r_write_silence > 0) {
2588 fread = read(forw_fd, &f_pd, 2);
2594 fread = read(forw_fd, &f_pd, 2);
2595 rread = read(rev_fd, &r_pd, 2);
2597 if ((rread == 0) && (fread == 0))
2599 *pd = (unsigned char)linear2ulaw( (f_pd + r_pd)/2 );
2600 rwritten = write(to_fd, pd, 1);
2601 if ((rwritten < 0) || (rread < 0) || (fread < 0)) {
2605 destroy_progress_dlg(progbar);
2611 destroy_progress_dlg(progbar);
2619 /****************************************************************************/
2620 /* the user wants to save in a file */
2621 /* XXX support for different formats is currently commented out */
2622 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
2625 /*GtkWidget *wav, *au, *sw;*/
2626 GtkWidget *rev, *forw, *both;
2627 user_data_t *user_data;
2628 gint channels /*, format*/;
2630 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
2632 /* Perhaps the user specified a directory instead of a file.
2633 Check whether they did. */
2634 if (test_for_directory(g_dest) == EISDIR) {
2635 /* It's a directory - set the file selection box to display it. */
2636 set_last_open_dir(g_dest);
2638 file_selection_set_current_folder(fs, get_last_open_dir());
2642 /*wav = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "wav_rb");
2643 au = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "au_rb");
2644 sw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "sw_rb");*/
2645 rev = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "reversed_rb");
2646 forw = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "forward_rb");
2647 both = (GtkWidget *)OBJECT_GET_DATA(ok_bt, "both_rb");
2648 user_data = (user_data_t *)OBJECT_GET_DATA(ok_bt, "user_data");
2650 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2651 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2652 * disable the ok button or disable the buttons for direction if only one is not ok. The
2653 * problem is if we open the save voice dialog and then click the refresh button and maybe
2654 * the state changes, so we can't save anymore. In this case we should be able to update
2655 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2658 /* we can not save in both dirctions */
2659 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2660 /* there are many combinations here, we just exit when first matches */
2661 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2662 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2663 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2664 "Can't save in a file: Unsupported codec!");
2665 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2666 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2667 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2668 "Can't save in a file: Wrong length of captured packets!");
2669 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2670 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2671 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2672 "Can't save in a file: RTP data with padding!");
2673 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2674 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2675 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2676 "Can't save in a file: Not all data in all packets was captured!");
2678 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2679 "Can't save in a file: File I/O problem!");
2682 /* we can not save forward direction */
2683 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2684 (GTK_TOGGLE_BUTTON (both)->active))) {
2685 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2686 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2687 "Can't save forward direction in a file: Unsupported codec!");
2688 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2689 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2690 "Can't save forward direction in a file: Wrong length of captured packets!");
2691 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2692 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2693 "Can't save forward direction in a file: RTP data with padding!");
2694 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2695 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2696 "Can't save forward direction in a file: Not all data in all packets was captured!");
2698 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2699 "Can't save forward direction in a file: File I/O problem!");
2702 /* we can not save reversed direction */
2703 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2704 (GTK_TOGGLE_BUTTON (both)->active))) {
2705 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2706 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2707 "Can't save reversed direction in a file: Unsupported codec!");
2708 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2709 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2710 "Can't save reversed direction in a file: Wrong length of captured packets!");
2711 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2712 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2713 "Can't save reversed direction in a file: RTP data with padding!");
2714 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2715 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2716 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2717 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2718 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2719 "Can't save reversed direction in a file: No RTP data!");
2721 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2722 "Can't save reversed direction in a file: File I/O problem!");
2726 /*if (GTK_TOGGLE_BUTTON (wav)->active)
2728 else if (GTK_TOGGLE_BUTTON (au)->active)
2730 else if (GTK_TOGGLE_BUTTON (sw)->active)
2733 if (GTK_TOGGLE_BUTTON (rev)->active)
2735 else if (GTK_TOGGLE_BUTTON (both)->active)
2740 if(!copy_file(g_dest, channels/*, format*/, user_data)) {
2741 /* XXX - report the error type! */
2742 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2743 "An error occured while saving voice in a file!");
2747 window_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
2750 /****************************************************************************/
2751 /* when the user wants to save the voice information in a file */
2752 /* XXX support for different formats is currently commented out */
2753 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2757 GtkWidget *label_format;
2758 GtkWidget *channels_label;
2759 /*GSList *format_group = NULL;*/
2760 GSList *channels_group = NULL;
2761 GtkWidget *forward_rb;
2762 GtkWidget *reversed_rb;
2764 /*GtkWidget *wav_rb; GtkWidget *au_rb; GtkWidget *sw_rb;*/
2767 /* if we can't save in a file: wrong codec, cut packets or other errors */
2768 /* shold the error arise here or later when you click ok button ?
2769 * if we do it here, then we must disable the refresh button, so we don't do it here */
2771 if (user_data->dlg.save_voice_as_w != NULL) {
2772 /* There's already a Save voice info dialog box; reactivate it. */
2773 reactivate_window(user_data->dlg.save_voice_as_w);
2777 /* XXX - use file_selection from dlg_utils instead! */
2778 user_data->dlg.save_voice_as_w = gtk_file_selection_new("Ethereal: Save Payload As ...");
2780 /* Container for each row of widgets */
2781 vertb = gtk_vbox_new(FALSE, 0);
2782 gtk_container_border_width(GTK_CONTAINER(vertb), 5);
2783 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
2784 vertb, FALSE, FALSE, 0);
2785 gtk_widget_show (vertb);
2787 table1 = gtk_table_new (2, 4, FALSE);
2788 gtk_widget_show (table1);
2789 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2790 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2791 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2793 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2794 gtk_widget_show (label_format);
2795 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2796 (GtkAttachOptions) (GTK_FILL),
2797 (GtkAttachOptions) (0), 0, 0);
2799 /* we support .au - ulaw*/
2800 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2801 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (wav_rb));
2802 gtk_widget_show (wav_rb);
2803 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2804 (GtkAttachOptions) (GTK_FILL),
2805 (GtkAttachOptions) (0), 0, 0);
2807 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2808 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (sw_rb));
2809 gtk_widget_show (sw_rb);
2810 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2811 (GtkAttachOptions) (GTK_FILL),
2812 (GtkAttachOptions) (0), 0, 0);
2813 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2814 format_group = gtk_radio_button_group (GTK_RADIO_BUTTON (au_rb));
2815 gtk_widget_show (au_rb);
2816 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2817 (GtkAttachOptions) (GTK_FILL),
2818 (GtkAttachOptions) (0), 0, 0);
2821 channels_label = gtk_label_new ("Channels:");
2822 gtk_widget_show (channels_label);
2823 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2824 (GtkAttachOptions) (GTK_FILL),
2825 (GtkAttachOptions) (0), 0, 0);
2826 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2828 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2829 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (forward_rb));
2830 gtk_widget_show (forward_rb);
2831 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2832 (GtkAttachOptions) (GTK_FILL),
2833 (GtkAttachOptions) (0), 0, 0);
2835 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2836 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (reversed_rb));
2837 gtk_widget_show (reversed_rb);
2838 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2839 (GtkAttachOptions) (GTK_FILL),
2840 (GtkAttachOptions) (0), 0, 0);
2842 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2843 channels_group = gtk_radio_button_group (GTK_RADIO_BUTTON (both_rb));
2844 gtk_widget_show (both_rb);
2845 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2846 (GtkAttachOptions) (GTK_FILL),
2847 (GtkAttachOptions) (0), 0, 0);
2849 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2851 /* if one direction is nok we don't allow saving
2852 XXX this is not ok since the user can click the refresh button and cause changes
2853 but we can not update this window. So we move all the decision on the time the ok
2855 if (user_data->forward.saved == FALSE) {
2856 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2857 gtk_widget_set_sensitive(forward_rb, FALSE);
2858 gtk_widget_set_sensitive(both_rb, FALSE);
2860 else if (user_data->reversed.saved == FALSE) {
2861 gtk_widget_set_sensitive(reversed_rb, FALSE);
2862 gtk_widget_set_sensitive(both_rb, FALSE);
2866 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
2867 /*OBJECT_SET_DATA(ok_bt, "wav_rb", wav_rb);
2868 OBJECT_SET_DATA(ok_bt, "au_rb", au_rb);
2869 OBJECT_SET_DATA(ok_bt, "sw_rb", sw_rb);*/
2870 OBJECT_SET_DATA(ok_bt, "forward_rb", forward_rb);
2871 OBJECT_SET_DATA(ok_bt, "reversed_rb", reversed_rb);
2872 OBJECT_SET_DATA(ok_bt, "both_rb", both_rb);
2873 OBJECT_SET_DATA(ok_bt, "user_data", user_data);
2874 SIGNAL_CONNECT(ok_bt, "clicked", save_voice_as_ok_cb,
2875 user_data->dlg.save_voice_as_w);
2877 window_set_cancel_button(user_data->dlg.save_voice_as_w,
2878 GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button, window_cancel_button_cb);
2880 SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "delete_event",
2881 window_delete_event_cb, NULL);
2882 SIGNAL_CONNECT(user_data->dlg.save_voice_as_w, "destroy",
2883 save_voice_as_destroy_cb, user_data);
2885 gtk_widget_show(user_data->dlg.save_voice_as_w);
2886 window_present(user_data->dlg.save_voice_as_w);
2890 /****************************************************************************/
2891 /* when we are finished with redisection, we add the label for the statistic */
2892 static void draw_stat(user_data_t *user_data)
2894 gchar label_max[200];
2895 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
2896 - user_data->forward.statinfo.start_seq_nr + 1;
2897 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
2898 - user_data->reversed.statinfo.start_seq_nr + 1;
2899 gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
2900 gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
2901 double f_perc, r_perc;
2903 f_perc = (double)(f_lost*100)/(double)f_expected;
2908 r_perc = (double)(r_lost*100)/(double)r_expected;
2913 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
2914 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2915 " Sequence errors = %u",
2916 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
2917 user_data->forward.statinfo.total_nr,
2918 f_expected, f_lost, f_perc, user_data->forward.statinfo.sequence);
2920 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2922 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
2923 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2924 " Sequence errors = %u",
2925 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
2926 user_data->reversed.statinfo.total_nr,
2927 r_expected, r_lost, r_perc, user_data->reversed.statinfo.sequence);
2929 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2936 /****************************************************************************/
2937 /* append a line to clist */
2938 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
2939 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
2940 gchar *timeStr, guint32 pkt_len, GdkColor *color)
2947 data[0]=&field[0][0];
2948 data[1]=&field[1][0];
2949 data[2]=&field[2][0];
2950 data[3]=&field[3][0];
2951 data[4]=&field[4][0];
2952 data[5]=&field[5][0];
2953 data[6]=&field[6][0];
2954 data[7]=&field[7][0];
2955 data[8]=&field[8][0];
2957 /* save the current locale */
2958 savelocale = setlocale(LC_NUMERIC, NULL);
2959 /* switch to "C" locale to avoid problems with localized decimal separators
2960 in g_snprintf("%f") functions */
2961 setlocale(LC_NUMERIC, "C");
2962 g_snprintf(field[0], 20, "%u", number);
2963 g_snprintf(field[1], 20, "%u", seq_num);
2964 g_snprintf(field[2], 20, "%.2f", delta);
2965 g_snprintf(field[3], 20, "%.2f", jitter);
2966 g_snprintf(field[4], 20, "%.2f", bandwidth);
2967 g_snprintf(field[5], 20, "%s", marker? "SET" : "");
2968 g_snprintf(field[6], 40, "%s", status);
2969 g_snprintf(field[7], 32, "%s", timeStr);
2970 g_snprintf(field[8], 20, "%u", pkt_len);
2971 /* restore previous locale setting */
2972 setlocale(LC_NUMERIC, savelocale);
2974 added_row = gtk_clist_append(GTK_CLIST(clist), data);
2975 gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
2976 gtk_clist_set_background(GTK_CLIST(clist), added_row, color);
2980 /****************************************************************************/
2981 /* callback for sorting columns of clist */
2982 static gint rtp_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
2989 const GtkCListRow *row1 = ptr1;
2990 const GtkCListRow *row2 = ptr2;
2992 text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
2993 text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
2995 switch(clist->sort_column){
2996 /* columns representing strings */
3000 return strcmp (text1, text2);
3001 /* columns representing ints */
3008 /* columns representing floats */
3014 if (fabs(f1-f2)<0.0000005)
3020 g_assert_not_reached();
3025 /****************************************************************************/
3027 click_column_cb(GtkCList *clist, gint column, gpointer data)
3029 column_arrows *col_arrows = (column_arrows *) data;
3032 gtk_clist_freeze(clist);
3034 for (i = 0; i < NUM_COLS; i++) {
3035 gtk_widget_hide(col_arrows[i].ascend_pm);
3036 gtk_widget_hide(col_arrows[i].descend_pm);
3039 if (column == clist->sort_column) {
3040 if (clist->sort_type == GTK_SORT_ASCENDING) {
3041 clist->sort_type = GTK_SORT_DESCENDING;
3042 gtk_widget_show(col_arrows[column].descend_pm);
3044 clist->sort_type = GTK_SORT_ASCENDING;
3045 gtk_widget_show(col_arrows[column].ascend_pm);
3048 clist->sort_type = GTK_SORT_ASCENDING;
3049 gtk_widget_show(col_arrows[column].ascend_pm);
3050 gtk_clist_set_sort_column(clist, column);
3052 gtk_clist_thaw(clist);
3054 gtk_clist_sort(clist);
3058 /****************************************************************************/
3059 /* Add the packet list */
3061 GtkWidget* create_clist(user_data_t* user_data)
3063 GtkWidget* clist_fwd;
3065 /* clist for the information */
3066 clist_fwd = gtk_clist_new(NUM_COLS);
3067 gtk_widget_show(clist_fwd);
3068 SIGNAL_CONNECT(clist_fwd, "select_row", on_clist_select_row, user_data);
3070 gtk_clist_column_titles_show(GTK_CLIST(clist_fwd));
3071 gtk_clist_set_compare_func(GTK_CLIST(clist_fwd), rtp_sort_column);
3072 gtk_clist_set_sort_column(GTK_CLIST(clist_fwd), 0);
3073 gtk_clist_set_sort_type(GTK_CLIST(clist_fwd), GTK_SORT_ASCENDING);
3075 /* hide date and length column */
3076 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
3077 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 8, FALSE);
3079 /* column widths and justification */
3080 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 60);
3081 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 75);
3082 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 75);
3083 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 75);
3084 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 50);
3085 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 5, 75);
3086 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_RIGHT);
3087 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_RIGHT);
3088 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
3089 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
3090 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
3091 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
3092 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 6, GTK_JUSTIFY_CENTER);
3097 /****************************************************************************/
3098 /* Add the sort by column feature for a packet clist */
3100 column_arrows* add_sort_by_column(GtkWidget* window, GtkWidget* clist,
3101 user_data_t* user_data _U_)
3103 column_arrows *col_arrows;
3104 GdkBitmap *ascend_bm, *descend_bm;
3105 GdkPixmap *ascend_pm, *descend_pm;
3106 GtkStyle *win_style;
3107 GtkWidget *column_lb;
3110 col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
3111 win_style = gtk_widget_get_style(window);
3112 ascend_pm = gdk_pixmap_create_from_xpm_d(window->window,
3114 &win_style->bg[GTK_STATE_NORMAL],
3115 (gchar **)clist_ascend_xpm);
3116 descend_pm = gdk_pixmap_create_from_xpm_d(window->window,
3118 &win_style->bg[GTK_STATE_NORMAL],
3119 (gchar **)clist_descend_xpm);
3121 for (i=0; i<NUM_COLS; i++) {
3122 col_arrows[i].table = gtk_table_new(2, 2, FALSE);
3123 gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
3124 column_lb = gtk_label_new(titles[i]);
3125 gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
3126 gtk_widget_show(column_lb);
3128 col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
3129 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
3130 col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
3131 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
3132 /* make packet-nr be the default sort order */
3134 gtk_widget_show(col_arrows[i].ascend_pm);
3136 gtk_clist_set_column_widget(GTK_CLIST(clist), i, col_arrows[i].table);
3137 gtk_widget_show(col_arrows[i].table);
3140 SIGNAL_CONNECT(clist, "click-column", click_column_cb, col_arrows);
3145 /****************************************************************************/
3146 /* Create the dialog box with all widgets */
3147 void create_rtp_dialog(user_data_t* user_data)
3149 GtkWidget *window = NULL;
3150 GtkWidget *clist_fwd;
3151 GtkWidget *clist_rev;
3152 GtkWidget *label_stats_fwd;
3153 GtkWidget *label_stats_rev;
3154 GtkWidget *notebook;
3156 GtkWidget *main_vb, *page, *page_r;
3158 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3159 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3160 #ifdef USE_CONVERSATION_GRAPH
3161 GtkWidget *graph_bt;
3163 GtkWidget *graph_bt;
3164 gchar label_forward[150];
3165 gchar label_reverse[150];
3167 gchar str_ip_src[16];
3168 gchar str_ip_dst[16];
3169 column_arrows *col_arrows_fwd;
3170 column_arrows *col_arrows_rev;
3172 window = window_new(GTK_WINDOW_TOPLEVEL, "Ethereal: RTP Stream Analysis");
3173 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3175 /* Container for each row of widgets */
3176 main_vb = gtk_vbox_new(FALSE, 2);
3177 gtk_container_border_width(GTK_CONTAINER(main_vb), 2);
3178 gtk_container_add(GTK_CONTAINER(window), main_vb);
3179 gtk_widget_show(main_vb);
3182 strcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)));
3183 strcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)));
3185 g_snprintf(label_forward, 149,
3186 "Analysing stream from %s port %u to %s port %u SSRC = %u",
3187 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3190 strcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)));
3191 strcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)));
3193 g_snprintf(label_reverse, 149,
3194 "Analysing stream from %s port %u to %s port %u SSRC = %u",
3195 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3197 /* Start a notebook for flipping between sets of changes */
3198 notebook = gtk_notebook_new();
3199 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3200 OBJECT_SET_DATA(window, "notebook", notebook);
3202 user_data->dlg.notebook_signal_id = SIGNAL_CONNECT(notebook, "switch_page", on_notebook_switch_page,
3205 /* page for forward connection */
3206 page = gtk_vbox_new(FALSE, 8);
3207 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3209 /* direction label */
3210 label = gtk_label_new(label_forward);
3211 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3213 /* place for some statistics */
3214 label_stats_fwd = gtk_label_new("\n");
3215 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3217 /* scrolled window */
3218 scrolled_window = scrolled_window_new(NULL, NULL);
3221 clist_fwd = create_clist(user_data);
3222 gtk_widget_show(clist_fwd);
3223 gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
3224 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3225 gtk_widget_show(scrolled_window);
3228 label = gtk_label_new(" Forward Direction ");
3229 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3231 /* same page for reversed connection */
3232 page_r = gtk_vbox_new(FALSE, 8);
3233 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3234 label = gtk_label_new(label_reverse);
3235 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3236 label_stats_rev = gtk_label_new("\n");
3237 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3239 scrolled_window_r = scrolled_window_new(NULL, NULL);
3241 clist_rev = create_clist(user_data);
3242 gtk_widget_show(clist_rev);
3243 gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
3244 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3245 gtk_widget_show(scrolled_window_r);
3247 label = gtk_label_new(" Reversed Direction ");
3248 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3250 /* page for help&about or future
3251 page_help = gtk_hbox_new(FALSE, 5);
3252 label = gtk_label_new(" Future ");
3253 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3254 frame = gtk_frame_new("");
3255 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3256 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3257 gtk_container_add(GTK_CONTAINER(frame), text);
3258 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3259 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3262 /* show all notebooks */
3263 gtk_widget_show_all(notebook);
3266 box4 = gtk_hbutton_box_new();
3267 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3268 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3269 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3270 gtk_button_box_set_spacing(GTK_BUTTON_BOX (box4), 0);
3271 gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
3272 gtk_widget_show(box4);
3274 voice_bt = gtk_button_new_with_label("Save payload...");
3275 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3276 gtk_widget_show(voice_bt);
3277 SIGNAL_CONNECT(voice_bt, "clicked", on_save_bt_clicked, user_data);
3279 csv_bt = gtk_button_new_with_label("Save as CSV...");
3280 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3281 gtk_widget_show(csv_bt);
3282 SIGNAL_CONNECT(csv_bt, "clicked", save_csv_as_cb, user_data);
3284 refresh_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_REFRESH);
3285 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3286 gtk_widget_show(refresh_bt);
3287 SIGNAL_CONNECT(refresh_bt, "clicked", on_refresh_bt_clicked, user_data);
3289 goto_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_JUMP_TO);
3290 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3291 gtk_widget_show(goto_bt);
3292 SIGNAL_CONNECT(goto_bt, "clicked", on_goto_bt_clicked, user_data);
3294 graph_bt = gtk_button_new_with_label("Graph");
3295 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3296 gtk_widget_show(graph_bt);
3297 SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
3300 #ifdef USE_CONVERSATION_GRAPH
3301 graph_bt = gtk_button_new_with_label("Graph");
3302 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3303 gtk_widget_show(graph_bt);
3304 SIGNAL_CONNECT(graph_bt, "clicked", on_graph_bt_clicked, user_data);
3307 next_bt = gtk_button_new_with_label("Next non-Ok");
3308 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3309 gtk_widget_show(next_bt);
3310 SIGNAL_CONNECT(next_bt, "clicked", on_next_bt_clicked, user_data);
3312 close_bt = BUTTON_NEW_FROM_STOCK(GTK_STOCK_CLOSE);
3313 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3314 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3315 gtk_widget_show(close_bt);
3316 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3318 SIGNAL_CONNECT(window, "delete_event", window_delete_event_cb, NULL);
3319 SIGNAL_CONNECT(window, "destroy", on_destroy, user_data);
3321 gtk_widget_show(window);
3322 window_present(window);
3324 /* sort by column feature */
3325 col_arrows_fwd = add_sort_by_column(window, clist_fwd, user_data);
3326 col_arrows_rev = add_sort_by_column(window, clist_rev, user_data);
3328 /* some widget references need to be saved for outside use */
3329 user_data->dlg.window = window;
3330 user_data->dlg.clist_fwd = GTK_CLIST(clist_fwd);
3331 user_data->dlg.clist_rev = GTK_CLIST(clist_rev);
3332 user_data->dlg.label_stats_fwd = label_stats_fwd;
3333 user_data->dlg.label_stats_rev = label_stats_rev;
3334 user_data->dlg.notebook = notebook;
3335 user_data->dlg.selected_clist = GTK_CLIST(clist_fwd);
3336 user_data->dlg.selected_row = 0;
3337 user_data->dlg.col_arrows_fwd = col_arrows_fwd;
3338 user_data->dlg.col_arrows_rev = col_arrows_rev;
3342 /****************************************************************************/
3343 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3344 gchar* proto_field, guint32* p_result)
3347 proto_node *proto_sibling_node;
3348 header_field_info *hfssrc;
3351 finfo = PITEM_FINFO(ptree_node);
3353 if (hfinformation==(finfo->hfinfo)) {
3354 hfssrc = proto_registrar_get_byname(proto_field);
3357 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3358 ptree_node=ptree_node->next) {
3359 finfo=PITEM_FINFO(ptree_node);
3360 if (hfssrc==finfo->hfinfo) {
3361 if (hfinformation->type==FT_IPv4) {
3362 ipv4 = fvalue_get(&finfo->value);
3363 *p_result = ipv4_get_net_order_addr(ipv4);
3366 *p_result = fvalue_get_integer(&finfo->value);
3373 proto_sibling_node = ptree_node->next;
3375 if (proto_sibling_node) {
3376 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3382 /****************************************************************************/
3383 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3388 proto_node *ptree_node;
3389 header_field_info *hfinformation;
3391 hfinformation = proto_registrar_get_byname(proto_name);
3392 if (hfinformation == NULL)
3395 ptree_node = ((proto_node *)protocol_tree)->first_child;
3399 return process_node(ptree_node, hfinformation, proto_field, p_result);
3403 /* XXX just copied from gtk/rpc_stat.c */
3404 void protect_thread_critical_region(void);
3405 void unprotect_thread_critical_region(void);
3407 /****************************************************************************/
3409 address *ip_src_fwd,
3410 guint16 port_src_fwd,
3411 address *ip_dst_fwd,
3412 guint16 port_dst_fwd,
3414 address *ip_src_rev,
3415 guint16 port_src_rev,
3416 address *ip_dst_rev,
3417 guint16 port_dst_rev,
3421 user_data_t *user_data;
3424 static color_t col[MAX_GRAPHS] = {
3425 {0, 0x0000, 0x0000, 0x0000},
3426 {0, 0xffff, 0x0000, 0x0000},
3427 {0, 0x0000, 0xffff, 0x0000},
3428 {0, 0x0000, 0x0000, 0xffff}
3432 user_data = g_malloc(sizeof(user_data_t));
3434 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3435 user_data->port_src_fwd = port_src_fwd;
3436 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3437 user_data->port_dst_fwd = port_dst_fwd;
3438 user_data->ssrc_fwd = ssrc_fwd;
3439 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3440 user_data->port_src_rev = port_src_rev;
3441 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3442 user_data->port_dst_rev = port_dst_rev;
3443 user_data->ssrc_rev = ssrc_rev;
3446 /* file names for storing sound data */
3447 /*XXX: check for errors*/
3448 fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
3451 fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
3454 user_data->forward.saveinfo.fp = NULL;
3455 user_data->reversed.saveinfo.fp = NULL;
3456 user_data->dlg.save_voice_as_w = NULL;
3457 user_data->dlg.save_csv_as_w = NULL;
3458 user_data->dlg.dialog_graph.window = NULL;
3460 #ifdef USE_CONVERSATION_GRAPH
3461 user_data->dlg.graph_window = NULL;
3462 user_data->series_fwd.value_pairs = NULL;
3463 user_data->series_rev.value_pairs = NULL;
3466 /* init dialog_graph */
3467 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3468 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3469 user_data->dlg.dialog_graph.draw_area=NULL;
3470 user_data->dlg.dialog_graph.pixmap=NULL;
3471 user_data->dlg.dialog_graph.scrollbar=NULL;
3472 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3473 user_data->dlg.dialog_graph.pixmap_width=500;
3474 user_data->dlg.dialog_graph.pixmap_height=200;
3475 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3476 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3477 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3478 user_data->dlg.dialog_graph.max_interval=0;
3479 user_data->dlg.dialog_graph.num_items=0;
3480 user_data->dlg.dialog_graph.start_time = -1;
3482 for(i=0;i<MAX_GRAPHS;i++){
3483 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3484 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3485 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3486 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3487 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3488 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3489 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3490 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3493 /* create the dialog box */
3494 create_rtp_dialog(user_data);
3496 /* proceed as if the Refresh button would have been pressed */
3497 on_refresh_bt_clicked(NULL, user_data);
3500 /****************************************************************************/
3501 /* entry point from main menu */
3502 void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3505 guint16 port_src_fwd;
3507 guint16 port_dst_fwd;
3508 guint32 ssrc_fwd = 0;
3510 guint16 port_src_rev;
3512 guint16 port_dst_rev;
3513 guint32 ssrc_rev = 0;
3514 unsigned int version_fwd;
3516 gchar filter_text[256];
3519 epan_dissect_t *edt;
3522 gboolean frame_matched;
3524 GList *strinfo_list;
3525 GList *filtered_list = NULL;
3526 rtp_stream_info_t *strinfo;
3529 /* Try to compile the filter. */
3530 strcpy(filter_text,"rtp && (ip || ipv6)");
3531 if (!dfilter_compile(filter_text, &sfcode)) {
3532 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, dfilter_error_msg);
3535 /* we load the current file into cf variable */
3537 fdata = cf->current_frame;
3539 /* we are on the selected frame now */
3541 return; /* if we exit here it's an error */
3543 /* dissect the current frame */
3544 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3545 cf->pd, fdata->cap_len, &err, &err_info)) {
3546 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3547 cf_read_error_message(err, err_info), cf->filename);
3550 edt = epan_dissect_new(TRUE, FALSE);
3551 epan_dissect_prime_dfilter(edt, sfcode);
3552 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, &cf->cinfo);
3553 frame_matched = dfilter_apply_edt(sfcode, edt);
3555 /* if it is not an rtp frame, show the rtpstream dialog */
3556 frame_matched = dfilter_apply_edt(sfcode, edt);
3557 if (frame_matched != 1) {
3558 epan_dissect_free(edt);
3559 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3560 "You didn't choose a RTP packet!");
3564 /* ok, it is a RTP frame, so let's get the ip and port values */
3565 COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
3566 COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
3567 port_src_fwd = edt->pi.srcport;
3568 port_dst_fwd = edt->pi.destport;
3570 /* assume the inverse ip/port combination for the reverse direction */
3571 COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
3572 COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
3573 port_src_rev = edt->pi.destport;
3574 port_dst_rev = edt->pi.srcport;
3576 /* check if it is RTP Version 2 */
3577 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3578 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3579 "RTP Version != 2 isn't supported!");
3583 /* now we need the SSRC value of the current frame */
3584 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3585 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3586 "SSRC value couldn't be found!");
3590 /* Scan for rtpstream */
3592 /* search for reversed direction in the global rtp streams list */
3594 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3595 while (strinfo_list)
3597 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3598 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3599 && strinfo->src_port==port_src_fwd
3600 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3601 && strinfo->dest_port==port_dst_fwd)
3603 filtered_list = g_list_prepend(filtered_list, strinfo);
3606 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3607 && strinfo->src_port==port_src_rev
3608 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3609 && strinfo->dest_port==port_dst_rev)
3612 filtered_list = g_list_append(filtered_list, strinfo);
3614 ssrc_rev = strinfo->ssrc;
3617 strinfo_list = g_list_next(strinfo_list);
3620 /* if more than one reverse streams found, we let the user choose the right one */
3622 rtpstream_dlg_show(filtered_list);
3641 /****************************************************************************/
3643 rtp_analysis_init(char *dummy _U_)
3645 rtp_analysis_cb(NULL, NULL);
3648 /****************************************************************************/
3650 register_tap_listener_rtp_analysis(void)
3652 register_ethereal_tap("rtp", rtp_analysis_init);
3654 register_tap_menu_item("RTP/Stream Analysis...", REGISTER_TAP_GROUP_NONE,
3655 rtp_analysis_cb, NULL, NULL, NULL);