2 * RTP analysis addition for Wireshark
6 * Copyright 2003, Alcatel Business Systems
7 * By Lars Ruoff <lars.ruoff@gmx.net>
10 * Copyright 2003, Iskratel, Ltd, Kranj
11 * By Miha Jemec <m.jemec@iskratel.si>
13 * Graph. Copyright 2004, Verso Technology
14 * By Alejandro Vaquero <alejandro.vaquero@verso.com>
15 * Based on io_stat.c by Ronnie Sahlberg
17 * Wireshark - Network traffic analyzer
18 * By Gerald Combs <gerald@wireshark.org>
19 * Copyright 1998 Gerald Combs
21 * This program is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU General Public License
23 * as published by the Free Software Foundation; either version 2
24 * of the License, or (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
53 #include <epan/epan_dissect.h>
54 #include <epan/filesystem.h>
55 #include <epan/pint.h>
57 #include <epan/dissectors/packet-rtp.h>
58 #include <epan/rtp_pt.h>
59 #include <epan/addr_resolv.h>
60 #include <epan/stat_cmd_args.h>
61 #include <epan/strutil.h>
64 #include "../register.h"
66 #include "../alert_box.h"
67 #include "../simple_dialog.h"
68 #include "../stat_menu.h"
69 #include "../progress_dlg.h"
71 #include "../tempfile.h"
72 #include <wsutil/file_util.h>
74 #include "gtk/gtkglobals.h"
75 #include "gtk/dlg_utils.h"
76 #include "gtk/file_dlg.h"
77 #include "gtk/gui_utils.h"
78 #include "gtk/gui_stat_menu.h"
80 #include "gtk/rtp_analysis.h"
81 #include "gtk/rtp_stream.h"
82 #include "gtk/rtp_stream_dlg.h"
84 #include "image/clist_ascend.xpm"
85 #include "image/clist_descend.xpm"
88 /****************************************************************************/
90 typedef struct column_arrows {
93 GtkWidget *descend_pm;
97 #define NUM_GRAPH_ITEMS 100000
99 #define AUTO_MAX_YSCALE 0
101 #define GRAPH_FWD_JITTER 0
102 #define GRAPH_FWD_DIFF 1
103 #define GRAPH_REV_JITTER 2
104 #define GRAPH_REV_DIFF 3
105 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
107 #define MAX_PIXELS_PER_TICK 4
108 #define DEFAULT_PIXELS_PER_TICK 1
109 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
110 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
112 #define MAX_TICK_VALUES 5
113 #define DEFAULT_TICK_VALUE 1
114 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
115 typedef struct _dialog_graph_graph_item_t {
118 } dialog_graph_graph_item_t;
120 typedef struct _dialog_graph_graph_t {
121 struct _user_data_t *ud;
122 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
125 GtkWidget *display_button;
130 } dialog_graph_graph_t;
133 typedef struct _dialog_graph_t {
134 gboolean needs_redraw;
135 gint32 interval; /* measurement interval in ms */
136 guint32 last_interval;
137 guint32 max_interval; /* XXX max_interval and num_items are redundant */
139 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
141 GtkWidget *draw_area;
143 GtkAdjustment *scrollbar_adjustment;
144 GtkWidget *scrollbar;
152 typedef struct _dialog_data_t {
156 GtkWidget *label_stats_fwd;
157 GtkWidget *label_stats_rev;
158 column_arrows *col_arrows_fwd;
159 column_arrows *col_arrows_rev;
161 GtkCList *selected_clist;
162 GtkWidget *save_voice_as_w;
163 GtkWidget *save_csv_as_w;
164 gint notebook_signal_id;
166 dialog_graph_t dialog_graph;
167 #ifdef USE_CONVERSATION_GRAPH
168 GtkWidget *graph_window;
172 #define OK_TEXT "[ Ok ]"
174 /* type of error when saving voice in a file didn't succeed */
177 TAP_RTP_WRONG_LENGTH,
178 TAP_RTP_PADDING_ERROR,
180 TAP_RTP_FILE_OPEN_ERROR,
184 typedef struct _tap_rtp_save_info_t {
187 error_type_t error_type;
189 } tap_rtp_save_info_t;
192 /* structure that holds the information about the forward and reversed direction */
193 struct _info_direction {
194 tap_rtp_stat_t statinfo;
195 tap_rtp_save_info_t saveinfo;
198 #define TMPNAMSIZE 100
200 #define SILENCE_PCMU (guint8)0xFF
201 #define SILENCE_PCMA (guint8)0x55
203 /* structure that holds general information about the connection
204 * and structures for both directions */
205 typedef struct _user_data_t {
206 /* tap associated data*/
208 guint16 port_src_fwd;
210 guint16 port_dst_fwd;
213 guint16 port_src_rev;
215 guint16 port_dst_rev;
218 struct _info_direction forward;
219 struct _info_direction reversed;
221 char f_tempname[TMPNAMSIZE];
222 char r_tempname[TMPNAMSIZE];
224 /* dialog associated data */
227 #ifdef USE_CONVERSATION_GRAPH
228 time_series_t series_fwd;
229 time_series_t series_rev;
235 static const gchar *titles[9] = {
247 #define SAVE_FORWARD_DIRECTION_MASK 0x01
248 #define SAVE_REVERSE_DIRECTION_MASK 0x02
249 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
251 #define SAVE_NONE_FORMAT 0
252 #define SAVE_WAV_FORMAT 1
253 #define SAVE_AU_FORMAT 2
254 #define SAVE_SW_FORMAT 3
255 #define SAVE_RAW_FORMAT 4
258 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_);
259 /****************************************************************************/
260 static void enable_graph(dialog_graph_graph_t *dgg)
267 static void dialog_graph_reset(user_data_t* user_data);
271 /****************************************************************************/
274 /****************************************************************************/
275 /* when there is a [re]reading of packet's */
277 rtp_reset(void *user_data_arg)
279 user_data_t *user_data = user_data_arg;
280 user_data->forward.statinfo.first_packet = TRUE;
281 user_data->reversed.statinfo.first_packet = TRUE;
282 user_data->forward.statinfo.max_delta = 0;
283 user_data->reversed.statinfo.max_delta = 0;
284 user_data->forward.statinfo.max_jitter = 0;
285 user_data->reversed.statinfo.max_jitter = 0;
286 user_data->forward.statinfo.mean_jitter = 0;
287 user_data->reversed.statinfo.mean_jitter = 0;
288 user_data->forward.statinfo.delta = 0;
289 user_data->reversed.statinfo.delta = 0;
290 user_data->forward.statinfo.diff = 0;
291 user_data->reversed.statinfo.diff = 0;
292 user_data->forward.statinfo.jitter = 0;
293 user_data->reversed.statinfo.jitter = 0;
294 user_data->forward.statinfo.bandwidth = 0;
295 user_data->reversed.statinfo.bandwidth = 0;
296 user_data->forward.statinfo.total_bytes = 0;
297 user_data->reversed.statinfo.total_bytes = 0;
298 user_data->forward.statinfo.bw_start_index = 0;
299 user_data->reversed.statinfo.bw_start_index = 0;
300 user_data->forward.statinfo.bw_index = 0;
301 user_data->reversed.statinfo.bw_index = 0;
302 user_data->forward.statinfo.timestamp = 0;
303 user_data->reversed.statinfo.timestamp = 0;
304 user_data->forward.statinfo.max_nr = 0;
305 user_data->reversed.statinfo.max_nr = 0;
306 user_data->forward.statinfo.total_nr = 0;
307 user_data->reversed.statinfo.total_nr = 0;
308 user_data->forward.statinfo.sequence = 0;
309 user_data->reversed.statinfo.sequence = 0;
310 user_data->forward.statinfo.start_seq_nr = 0;
311 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
312 user_data->forward.statinfo.stop_seq_nr = 0;
313 user_data->reversed.statinfo.stop_seq_nr = 0;
314 user_data->forward.statinfo.cycles = 0;
315 user_data->reversed.statinfo.cycles = 0;
316 user_data->forward.statinfo.under = FALSE;
317 user_data->reversed.statinfo.under = FALSE;
318 user_data->forward.statinfo.start_time = 0;
319 user_data->reversed.statinfo.start_time = 0;
320 user_data->forward.statinfo.time = 0;
321 user_data->reversed.statinfo.time = 0;
322 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
323 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
325 user_data->forward.saveinfo.count = 0;
326 user_data->reversed.saveinfo.count = 0;
327 user_data->forward.saveinfo.saved = FALSE;
328 user_data->reversed.saveinfo.saved = FALSE;
330 /* clear the dialog box clists */
331 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_fwd));
332 gtk_clist_clear(GTK_CLIST(user_data->dlg.clist_rev));
334 /* reset graph info */
335 dialog_graph_reset(user_data);
337 #ifdef USE_CONVERSATION_GRAPH
338 if (user_data->dlg.graph_window != NULL)
339 window_destroy(user_data->dlg.graph_window);
341 g_array_free(user_data->series_fwd.value_pairs, TRUE);
342 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
344 g_array_free(user_data->series_rev.value_pairs, TRUE);
345 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
348 /* XXX check for error at fclose? */
349 if (user_data->forward.saveinfo.fp != NULL)
350 fclose(user_data->forward.saveinfo.fp);
351 if (user_data->reversed.saveinfo.fp != NULL)
352 fclose(user_data->reversed.saveinfo.fp);
353 user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
354 if (user_data->forward.saveinfo.fp == NULL)
355 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
356 user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
357 if (user_data->reversed.saveinfo.fp == NULL)
358 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
362 /****************************************************************************/
363 static int rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
365 dialog_graph_graph_item_t *it;
369 /* we sometimes get called when dgg is disabled.
370 this is a bug since the tap listener should be removed first */
375 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
378 * Find which interval this is supposed to to in and store the
379 * interval index as idx
381 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
382 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
384 rtp_time = nstime_to_sec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
388 idx = (guint32)(rtp_time*1000)/dgg->ud->dlg.dialog_graph.interval;
390 /* some sanity checks */
391 if((idx<0)||(idx>=NUM_GRAPH_ITEMS)){
395 /* update num_items */
396 if((guint32)idx > dgg->ud->dlg.dialog_graph.num_items){
397 dgg->ud->dlg.dialog_graph.num_items=idx;
398 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
402 * Find the appropriate dialog_graph_graph_item_t structure
407 * Use the max value to highlight RTP problems
409 if (value > it->value) {
412 it->flags = it->flags | statinfo->flags;
417 /****************************************************************************/
418 /* here we can redraw the output */
420 static void rtp_draw(void *prs _U_)
425 /* forward declarations */
426 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
427 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
428 gchar *timeStr, guint32 pkt_len, GdkColor *color);
430 static int rtp_packet_add_info(GtkCList *clist,
431 tap_rtp_stat_t *statinfo, packet_info *pinfo,
432 const struct _rtp_info *rtpinfo);
434 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
435 tap_rtp_stat_t *statinfo,
437 const struct _rtp_info *rtpinfo);
440 /****************************************************************************/
441 /* whenever a RTP packet is seen by the tap listener */
442 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
444 user_data_t *user_data = user_data_arg;
445 const struct _rtp_info *rtpinfo = rtpinfo_arg;
446 #ifdef USE_CONVERSATION_GRAPH
449 /* we ignore packets that are not displayed */
450 if (pinfo->fd->flags.passed_dfilter == 0)
452 /* also ignore RTP Version != 2 */
453 else if (rtpinfo->info_version !=2)
455 /* is it the forward direction? */
456 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
457 && CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
458 && user_data->port_src_fwd == pinfo->srcport
459 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
460 && user_data->port_dst_fwd == pinfo->destport) {
461 #ifdef USE_CONVERSATION_GRAPH
462 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
463 vp.fnumber = pinfo->fd->num;
464 g_array_append_val(user_data->series_fwd.value_pairs, vp);
466 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
467 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));
468 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));
469 rtp_packet_add_info(user_data->dlg.clist_fwd,
470 &(user_data->forward.statinfo), pinfo, rtpinfo);
471 rtp_packet_save_payload(&(user_data->forward.saveinfo),
472 &(user_data->forward.statinfo), pinfo, rtpinfo);
474 /* is it the reversed direction? */
475 else if (user_data->ssrc_rev == rtpinfo->info_sync_src
476 && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
477 && user_data->port_src_rev == pinfo->srcport
478 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
479 && user_data->port_dst_rev == pinfo->destport) {
480 #ifdef USE_CONVERSATION_GRAPH
481 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
482 vp.fnumber = pinfo->fd->num;
483 g_array_append_val(user_data->series_rev.value_pairs, vp);
485 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
486 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));
487 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));
488 rtp_packet_add_info(user_data->dlg.clist_rev,
489 &(user_data->reversed.statinfo), pinfo, rtpinfo);
490 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
491 &(user_data->reversed.statinfo), pinfo, rtpinfo);
498 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
499 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
500 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
501 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
502 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
504 /****************************************************************************/
505 /* adds statistics information from the packet to the clist */
506 static int rtp_packet_add_info(GtkCList *clist,
507 tap_rtp_stat_t *statinfo, packet_info *pinfo,
508 const struct _rtp_info *rtpinfo)
515 GdkColor color = COLOR_DEFAULT;
516 then = pinfo->fd->abs_ts.secs;
517 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
518 tm_tmp = localtime(&then);
519 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
522 tm_tmp->tm_year + 1900,
528 if (statinfo->pt == PT_CN) {
529 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
532 else if (statinfo->pt == PT_CN_OLD) {
533 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
536 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
537 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
540 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
541 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
542 color = COLOR_WARNING;
544 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
545 g_snprintf(status,sizeof(status),"Incorrect timestamp");
546 color = COLOR_WARNING;
548 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
549 && !(statinfo->flags & STAT_FLAG_FIRST)
550 && !(statinfo->flags & STAT_FLAG_PT_CN)
551 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
552 && !(statinfo->flags & STAT_FLAG_MARKER)) {
553 g_snprintf(status,sizeof(status),"Marker missing?");
554 color = COLOR_WARNING;
557 if (statinfo->flags & STAT_FLAG_MARKER) {
558 color = COLOR_WARNING;
560 g_snprintf(status,sizeof(status),OK_TEXT);
562 /* is this the first packet we got in this direction? */
563 if (statinfo->flags & STAT_FLAG_FIRST) {
565 pinfo->fd->num, rtpinfo->info_seq_num,
570 rtpinfo->info_marker_set,
571 timeStr, pinfo->fd->pkt_len,
576 pinfo->fd->num, rtpinfo->info_seq_num,
577 statinfo->delta*1000,
578 statinfo->jitter*1000,
581 rtpinfo->info_marker_set,
582 timeStr, pinfo->fd->pkt_len,
588 #define MAX_SILENCE_TICKS 1000000
589 /****************************************************************************/
590 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
591 tap_rtp_stat_t *statinfo,
593 const struct _rtp_info *rtpinfo)
600 /* is this the first packet we got in this direction? */
601 if (statinfo->flags & STAT_FLAG_FIRST) {
602 if (saveinfo->fp == NULL) {
603 saveinfo->saved = FALSE;
604 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
607 saveinfo->saved = TRUE;
610 /* save the voice information */
611 /* if there was already an error, we quit */
612 if (saveinfo->saved == FALSE)
615 /* if the captured length and packet length aren't equal, we quit
616 * if also the RTP dissector thinks there is some information missing */
617 if ((pinfo->fd->pkt_len != pinfo->fd->cap_len) &&
618 (!rtpinfo->info_all_data_present)) {
619 saveinfo->saved = FALSE;
620 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
624 /* if padding bit is set, but the padding count is bigger
625 * then the whole RTP data - error with padding count */
626 if ( (rtpinfo->info_padding_set != FALSE) &&
627 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
628 saveinfo->saved = FALSE;
629 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
633 /* do we need to insert some silence? */
634 if ((rtpinfo->info_marker_set) &&
635 !(statinfo->flags & STAT_FLAG_FIRST) &&
636 !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) &&
637 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
638 /* the amount of silence should be the difference between
639 * the last timestamp and the current one minus x
640 * x should equal the amount of information in the last frame
641 * XXX not done yet */
642 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
643 rtpinfo->info_padding_count) && i < MAX_SILENCE_TICKS; i++) {
644 switch (statinfo->reg_pt) {
655 nchars=fwrite(&tmp, 1, 1, saveinfo->fp);
658 fflush(saveinfo->fp);
662 if (rtpinfo->info_payload_type == PT_CN
663 || rtpinfo->info_payload_type == PT_CN_OLD) {
665 /*all other payloads*/
667 if (!rtpinfo->info_all_data_present) {
668 /* Not all the data was captured. */
669 saveinfo->saved = FALSE;
670 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
674 /* we put the pointer at the beginning of the RTP
675 * payload, that is, at the beginning of the RTP data
676 * plus the offset of the payload from the beginning
678 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
679 nchars=fwrite(data, sizeof(unsigned char), (rtpinfo->info_payload_len - rtpinfo->info_padding_count), saveinfo->fp);
680 saveinfo->count+=(rtpinfo->info_payload_len - rtpinfo->info_padding_count);
682 fflush(saveinfo->fp);
683 saveinfo->saved = TRUE;
691 /****************************************************************************/
694 /****************************************************************************/
696 /****************************************************************************/
697 /* close the dialog window and remove the tap listener */
698 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
700 /* remove tap listener */
701 protect_thread_critical_region();
702 remove_tap_listener(user_data);
703 unprotect_thread_critical_region();
705 /* close and remove temporary files */
706 if (user_data->forward.saveinfo.fp != NULL)
707 fclose(user_data->forward.saveinfo.fp);
708 if (user_data->reversed.saveinfo.fp != NULL)
709 fclose(user_data->reversed.saveinfo.fp);
710 /*XXX: test for error **/
711 ws_remove(user_data->f_tempname);
712 ws_remove(user_data->r_tempname);
714 /* destroy save_voice_as window if open */
715 if (user_data->dlg.save_voice_as_w != NULL)
716 window_destroy(user_data->dlg.save_voice_as_w);
718 /* destroy graph window if open */
719 if (user_data->dlg.dialog_graph.window != NULL)
720 window_destroy(user_data->dlg.dialog_graph.window);
722 #ifdef USE_CONVERSATION_GRAPH
723 /* destroy graph window if open */
724 if (user_data->dlg.graph_window != NULL)
725 window_destroy(user_data->dlg.graph_window);
728 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
729 g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
731 g_free(user_data->dlg.col_arrows_fwd);
732 g_free(user_data->dlg.col_arrows_rev);
737 /****************************************************************************/
738 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
739 GtkNotebookPage *page _U_,
741 user_data_t *user_data _U_)
743 user_data->dlg.selected_clist =
744 (page_num==0) ? user_data->dlg.clist_fwd : user_data->dlg.clist_rev ;
745 user_data->dlg.selected_row = 0;
748 /****************************************************************************/
749 static void on_clist_select_row(GtkCList *clist _U_,
753 user_data_t *user_data _U_)
755 user_data->dlg.selected_clist = clist;
756 user_data->dlg.selected_row = row;
760 #ifdef USE_CONVERSATION_GRAPH
761 /****************************************************************************/
762 /* when the graph window gets destroyed */
763 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
765 /* note that graph window has been destroyed */
766 user_data->dlg.graph_window = NULL;
769 /****************************************************************************/
770 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
773 GtkCList *clist = NULL;
774 if (vp.fnumber != 0) {
775 clist = GTK_CLIST(user_data->dlg.clist_fwd);
776 row = gtk_clist_find_row_from_data(clist,
777 GUINT_TO_POINTER(vp.fnumber));
779 clist = GTK_CLIST(user_data->dlg.clist_rev);
780 row = gtk_clist_find_row_from_data(clist,
781 GUINT_TO_POINTER(vp.fnumber));
784 gtk_notebook_set_current_page(GTK_NOTEBOOK(user_data->dlg.notebook),
785 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
786 gtk_clist_select_row(clist, row, 0);
787 gtk_clist_moveto(clist, row, 0, 0.5, 0);
793 /****************************************************************************/
794 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
800 if (user_data->dlg.graph_window != NULL) {
801 /* There's already a graph window; reactivate it. */
802 reactivate_window(user_data->dlg.graph_window);
805 list = g_list_append(list, &(user_data->series_fwd));
806 list = g_list_append(list, &(user_data->series_rev));
808 user_data->series_fwd.color.pixel = 0;
809 user_data->series_fwd.color.red = 0x80ff;
810 user_data->series_fwd.color.green = 0xe0ff;
811 user_data->series_fwd.color.blue = 0xffff;
812 user_data->series_fwd.yvalue = 0.5;
814 user_data->series_rev.color.pixel = 0;
815 user_data->series_rev.color.red = 0x60ff;
816 user_data->series_rev.color.green = 0xc0ff;
817 user_data->series_rev.color.blue = 0xffff;
818 user_data->series_rev.yvalue = -0.5;
820 g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=0x%X)",
821 get_addr_name(&(user_data->ip_src_fwd)),
822 user_data->port_src_fwd,
823 get_addr_name(&(user_data->ip_dst_fwd)),
824 user_data->port_dst_fwd,
825 user_data->ssrc_fwd);
827 g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=0x%X)",
828 get_addr_name(&(user_data->ip_src_rev)),
829 user_data->port_src_rev,
830 get_addr_name(&(user_data->ip_dst_rev)),
831 user_data->port_dst_rev,
832 user_data->ssrc_rev);
834 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
835 &graph_selection_callback, user_data);
836 g_signal_connect(user_data->dlg.graph_window, "destroy",
837 G_CALLBACK(on_destroy_graph), user_data);
839 #endif /*USE_CONVERSATION_GRAPH*/
841 /****************************************************************************/
842 static void dialog_graph_set_title(user_data_t* user_data)
845 if (!user_data->dlg.dialog_graph.window){
848 title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
849 get_addr_name(&(user_data->ip_src_fwd)),
850 user_data->port_src_fwd,
851 get_addr_name(&(user_data->ip_dst_fwd)),
852 user_data->port_dst_fwd,
853 get_addr_name(&(user_data->ip_src_rev)),
854 user_data->port_src_rev,
855 get_addr_name(&(user_data->ip_dst_rev)),
856 user_data->port_dst_rev);
858 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
864 /****************************************************************************/
865 static void dialog_graph_reset(user_data_t* user_data)
869 user_data->dlg.dialog_graph.needs_redraw=TRUE;
870 for(i=0;i<MAX_GRAPHS;i++){
871 for(j=0;j<NUM_GRAPH_ITEMS;j++){
872 dialog_graph_graph_item_t *dggi;
873 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
878 user_data->dlg.dialog_graph.last_interval=0xffffffff;
879 user_data->dlg.dialog_graph.max_interval=0;
880 user_data->dlg.dialog_graph.num_items=0;
882 /* create the color titles near the filter buttons */
883 for(i=0;i<MAX_GRAPHS;i++){
886 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=0x%X)",
888 get_addr_name(&(user_data->ip_src_fwd)),
889 user_data->port_src_fwd,
890 get_addr_name(&(user_data->ip_dst_fwd)),
891 user_data->port_dst_fwd,
892 user_data->ssrc_fwd);
895 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=0x%X)",
897 get_addr_name(&(user_data->ip_src_rev)),
898 user_data->port_src_rev,
899 get_addr_name(&(user_data->ip_dst_rev)),
900 user_data->port_dst_rev,
901 user_data->ssrc_rev);
905 dialog_graph_set_title(user_data);
908 /****************************************************************************/
909 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
911 dialog_graph_graph_item_t *it;
918 /****************************************************************************/
919 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
922 g_snprintf(buf, buf_len, "%ds",t/1000000);
923 } else if(t>=1000000){
924 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
926 g_snprintf(buf, buf_len, "%dms",t/1000);
928 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
930 g_snprintf(buf, buf_len, "%dus",t);
934 /****************************************************************************/
935 static void dialog_graph_draw(user_data_t* user_data)
938 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
939 gint32 current_interval;
940 guint32 left_x_border;
941 guint32 right_x_border;
942 guint32 top_y_border;
943 guint32 bottom_y_border;
945 int label_width, label_height;
946 guint32 draw_width, draw_height;
947 char label_string[15];
950 guint32 num_time_intervals;
951 guint32 max_value; /* max value of seen data */
952 guint32 max_y; /* max value of the Y scale */
954 if(!user_data->dlg.dialog_graph.needs_redraw){
957 user_data->dlg.dialog_graph.needs_redraw=FALSE;
960 * Find the length of the intervals we have data for
961 * so we know how large arrays we need to malloc()
963 num_time_intervals=user_data->dlg.dialog_graph.num_items;
964 /* if there isnt anything to do, just return */
965 if(num_time_intervals==0){
968 num_time_intervals+=1;
969 /* XXX move this check to _packet() */
970 if(num_time_intervals>NUM_GRAPH_ITEMS){
971 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
976 * find the max value so we can autoscale the y axis
979 for(i=0;i<MAX_GRAPHS;i++){
982 if(!user_data->dlg.dialog_graph.graph[i].display){
985 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
988 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
990 /* keep track of the max value we have encountered */
1000 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1001 user_data->dlg.dialog_graph.draw_area->style->white_gc,
1004 user_data->dlg.dialog_graph.draw_area->allocation.width,
1005 user_data->dlg.dialog_graph.draw_area->allocation.height);
1009 * Calculate the y scale we should use
1011 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1012 max_y=yscale_max[MAX_YSCALE-1];
1013 for(i=MAX_YSCALE-1;i>0;i--){
1014 if(max_value<yscale_max[i]){
1015 max_y=yscale_max[i];
1019 /* the user had specified an explicit y scale to use */
1020 max_y=user_data->dlg.dialog_graph.max_y_units;
1024 * Calculate size of borders surrounding the plot
1025 * The border on the right side needs to be adjusted depending
1026 * on the width of the text labels. For simplicity we assume that the
1027 * top y scale label will be the widest one
1029 print_time_scale_string(label_string, 15, max_y);
1030 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1031 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1033 right_x_border=label_width+20;
1035 bottom_y_border=label_height+20;
1039 * Calculate the size of the drawing area for the actual plot
1041 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1042 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1046 * Draw the y axis and labels
1047 * (we always draw the y scale with 11 ticks along the axis)
1049 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1050 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1052 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1053 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1059 /* first, middle and last tick are slightly longer */
1063 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1064 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1065 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1066 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1067 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1068 /* draw the labels */
1070 print_time_scale_string(label_string, 15, (max_y*i/10));
1071 pango_layout_set_text(layout, label_string, -1);
1072 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1073 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1074 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1075 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1076 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1080 print_time_scale_string(label_string, 15, (max_y*i/10));
1081 pango_layout_set_text(layout, label_string, -1);
1082 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1083 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1084 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1085 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1086 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1090 print_time_scale_string(label_string, 15, (max_y*i/10));
1091 pango_layout_set_text(layout, label_string, -1);
1092 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1093 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1094 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1095 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1096 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1104 * if we have not specified the last_interval via the gui,
1105 * then just pick the current end of the capture so that is scrolls
1106 * nicely when doing live captures
1108 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1109 last_interval=user_data->dlg.dialog_graph.max_interval;
1111 last_interval=user_data->dlg.dialog_graph.last_interval;
1118 /* plot the x-scale */
1119 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);
1121 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1122 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1123 first_interval*=user_data->dlg.dialog_graph.interval;
1130 while(interval_delta<((last_interval-first_interval)/10)){
1131 interval_delta*=delta_multiplier;
1132 if(delta_multiplier==5){
1139 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1142 /* if pixels_per_tick is <5, only draw every 10 ticks */
1143 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1147 if(current_interval%interval_delta){
1153 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1154 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1155 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1156 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1157 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1158 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1162 if(user_data->dlg.dialog_graph.interval>=1000){
1163 g_snprintf(label_string, 15, "%ds", current_interval/1000);
1164 } else if(user_data->dlg.dialog_graph.interval>=100){
1165 g_snprintf(label_string, 15, "%d.%1ds", current_interval/1000,(current_interval/100)%10)
1167 } else if(user_data->dlg.dialog_graph.interval>=10){
1168 g_snprintf(label_string, 15, "%d.%2ds", current_interval/1000,(current_interval/10)%100)
1171 g_snprintf(label_string, 15, "%d.%3ds", current_interval/1000,current_interval%1000);
1173 pango_layout_set_text(layout, label_string, -1);
1174 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1175 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1176 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1177 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1178 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1190 * Draw "x" for Sequence Errors and "m" for Marks
1192 /* Draw the labels Fwd and Rev */
1193 g_strlcpy(label_string,"<-Fwd",15);
1194 pango_layout_set_text(layout, label_string, -1);
1195 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1196 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1197 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1198 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1199 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1201 g_strlcpy(label_string,"<-Rev",15);
1202 pango_layout_set_text(layout, label_string, -1);
1203 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1204 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1205 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1206 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1207 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1210 /* Draw the marks */
1211 for(i=MAX_GRAPHS-1;i>=0;i--){
1213 guint32 x_pos, prev_x_pos;
1215 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1216 if (!user_data->dlg.dialog_graph.graph[i].display){
1219 /* initialize prev x/y to the low left corner of the graph */
1220 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;
1222 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1223 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;
1225 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1227 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1228 g_strlcpy(label_string,"x",15);
1230 g_strlcpy(label_string,"m",15);
1233 pango_layout_set_text(layout, label_string, -1);
1234 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1235 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1236 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1238 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1246 g_object_unref(G_OBJECT(layout));
1249 * Loop over all graphs and draw them
1251 for(i=MAX_GRAPHS-1;i>=0;i--){
1253 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1254 if (!user_data->dlg.dialog_graph.graph[i].display){
1257 /* initialize prev x/y to the low left corner of the graph */
1258 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;
1259 prev_y_pos=draw_height-1+top_y_border;
1261 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1263 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;
1264 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1268 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1271 /* dont need to draw anything if the segment
1272 * is entirely above the top of the graph
1274 if( (prev_y_pos==0) && (y_pos==0) ){
1281 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1282 x_pos, draw_height-1+top_y_border,
1292 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1293 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1294 user_data->dlg.dialog_graph.pixmap,
1297 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1300 /* update the scrollbar */
1301 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1302 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1303 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1304 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1305 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1307 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1309 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1310 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1311 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1315 /****************************************************************************/
1316 static void dialog_graph_redraw(user_data_t* user_data)
1318 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1319 dialog_graph_draw(user_data);
1322 /****************************************************************************/
1323 static gint quit(GtkWidget *widget, GdkEventExpose *event _U_)
1325 user_data_t *user_data;
1327 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1329 user_data->dlg.dialog_graph.window = NULL;
1333 /****************************************************************************/
1334 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1336 user_data_t *user_data;
1338 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1344 gdk_draw_pixmap(widget->window,
1345 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1346 user_data->dlg.dialog_graph.pixmap,
1347 event->area.x, event->area.y,
1348 event->area.x, event->area.y,
1349 event->area.width, event->area.height);
1354 /****************************************************************************/
1355 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1357 user_data_t *user_data;
1360 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1366 if(user_data->dlg.dialog_graph.pixmap){
1367 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1368 user_data->dlg.dialog_graph.pixmap=NULL;
1371 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1372 widget->allocation.width,
1373 widget->allocation.height,
1375 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1376 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1378 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1379 widget->style->white_gc,
1382 widget->allocation.width,
1383 widget->allocation.height);
1385 /* set up the colors and the GC structs for this pixmap */
1386 for(i=0;i<MAX_GRAPHS;i++){
1387 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1388 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1391 dialog_graph_redraw(user_data);
1395 /****************************************************************************/
1396 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1398 user_data_t *user_data=(user_data_t *)data;
1401 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1402 if(user_data->dlg.dialog_graph.last_interval==mi){
1405 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1406 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1410 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1412 dialog_graph_redraw(user_data);
1416 /****************************************************************************/
1417 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1419 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1420 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1421 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.draw_area), "user_data_t", user_data);
1423 gtk_widget_set_size_request(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1425 /* signals needed to handle backing pixmap */
1426 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1427 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1429 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1430 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1432 /* create the associated scrollbar */
1433 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1434 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1435 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1436 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1437 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1440 /****************************************************************************/
1441 static void disable_graph(dialog_graph_graph_t *dgg)
1445 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1450 /****************************************************************************/
1451 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1453 /* this graph is not active, just update display and redraw */
1454 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1456 dialog_graph_redraw(dgg->ud);
1461 cf_retap_packets(&cfile, FALSE);
1462 dialog_graph_redraw(dgg->ud);
1467 /****************************************************************************/
1468 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1474 hbox=gtk_hbox_new(FALSE, 3);
1475 gtk_container_add(GTK_CONTAINER(box), hbox);
1476 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1477 gtk_widget_show(hbox);
1479 g_snprintf(str, 256, "Graph %d", num);
1480 dgg->display_button=gtk_toggle_button_new_with_label(str);
1481 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1482 gtk_widget_show(dgg->display_button);
1483 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1484 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1486 label=gtk_label_new(dgg->title);
1487 gtk_widget_show(label);
1488 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1490 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1491 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1492 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1493 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1494 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1499 /****************************************************************************/
1500 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1507 frame=gtk_frame_new("Graphs");
1508 gtk_container_add(GTK_CONTAINER(box), frame);
1509 gtk_widget_show(frame);
1511 vbox=gtk_vbox_new(FALSE, 1);
1512 gtk_container_add(GTK_CONTAINER(frame), vbox);
1513 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1514 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1515 gtk_widget_show(vbox);
1517 for(i=0;i<MAX_GRAPHS;i++){
1518 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1521 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1522 gtk_widget_show(label);
1523 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1528 /****************************************************************************/
1529 static void yscale_select(GtkWidget *item, gpointer key)
1532 user_data_t *user_data;
1534 user_data=(user_data_t *)key;
1535 val=(long)g_object_get_data(G_OBJECT(item), "yscale_max");
1537 user_data->dlg.dialog_graph.max_y_units=val;
1538 dialog_graph_redraw(user_data);
1541 /****************************************************************************/
1542 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1545 user_data_t *user_data;
1547 user_data=(user_data_t *)key;
1548 val=(long)g_object_get_data(G_OBJECT(item), "pixels_per_tick");
1549 user_data->dlg.dialog_graph.pixels_per_tick=val;
1550 dialog_graph_redraw(user_data);
1553 /****************************************************************************/
1554 static void tick_interval_select(GtkWidget *item, gpointer key)
1557 user_data_t *user_data;
1559 user_data=(user_data_t *)key;
1560 val=(long)g_object_get_data(G_OBJECT(item), "tick_interval");
1562 user_data->dlg.dialog_graph.interval=val;
1563 cf_retap_packets(&cfile, FALSE);
1564 dialog_graph_redraw(user_data);
1567 /****************************************************************************/
1568 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1571 GtkWidget *menu_item;
1574 for(i=0;i<MAX_YSCALE;i++){
1575 if(yscale_max[i]==AUTO_MAX_YSCALE){
1576 g_strlcpy(str,"Auto",15);
1578 g_snprintf(str, 15, "%u ms", yscale_max[i]/1000);
1580 menu_item=gtk_menu_item_new_with_label(str);
1581 g_object_set_data(G_OBJECT(menu_item), "yscale_max",
1582 GUINT_TO_POINTER(yscale_max[i]));
1583 g_signal_connect(menu_item, "activate", G_CALLBACK(yscale_select), user_data);
1584 gtk_widget_show(menu_item);
1585 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1590 /****************************************************************************/
1591 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
1594 GtkWidget *menu_item;
1597 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1598 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1599 menu_item=gtk_menu_item_new_with_label(str);
1601 g_object_set_data(G_OBJECT(menu_item), "pixels_per_tick",
1602 GUINT_TO_POINTER(pixels_per_tick[i]));
1603 g_signal_connect(menu_item, "activate", G_CALLBACK(pixels_per_tick_select), user_data);
1604 gtk_widget_show(menu_item);
1605 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1607 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1612 /****************************************************************************/
1613 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
1616 GtkWidget *menu_item;
1619 for(i=0;i<MAX_TICK_VALUES;i++){
1620 if(tick_interval_values[i]>=1000){
1621 g_snprintf(str, 15, "%u sec", tick_interval_values[i]/1000);
1622 } else if(tick_interval_values[i]>=100){
1623 g_snprintf(str, 15, "0.%1u sec", (tick_interval_values[i]/100)%10);
1624 } else if(tick_interval_values[i]>=10){
1625 g_snprintf(str, 15, "0.%02u sec", (tick_interval_values[i]/10)%10);
1627 g_snprintf(str, 15, "0.%03u sec", (tick_interval_values[i])%10);
1630 menu_item=gtk_menu_item_new_with_label(str);
1631 g_object_set_data(G_OBJECT(menu_item), "tick_interval",
1632 GUINT_TO_POINTER(tick_interval_values[i]));
1633 g_signal_connect(menu_item, "activate", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1634 gtk_widget_show(menu_item);
1635 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1637 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1641 /****************************************************************************/
1642 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
1646 GtkWidget *option_menu;
1649 hbox=gtk_hbox_new(FALSE, 0);
1650 gtk_container_add(GTK_CONTAINER(box), hbox);
1651 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1652 gtk_widget_show(hbox);
1654 label=gtk_label_new(name);
1655 gtk_widget_show(label);
1656 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1658 option_menu=gtk_option_menu_new();
1659 menu=gtk_menu_new();
1660 (*func)(user_data, menu);
1661 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1662 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1663 gtk_widget_show(option_menu);
1666 /****************************************************************************/
1667 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1669 GtkWidget *frame_vbox;
1673 frame_vbox=gtk_vbox_new(FALSE, 0);
1674 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1675 gtk_widget_show(frame_vbox);
1677 frame = gtk_frame_new("X Axis");
1678 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1679 gtk_widget_show(frame);
1681 vbox=gtk_vbox_new(FALSE, 0);
1682 gtk_container_add(GTK_CONTAINER(frame), vbox);
1683 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1684 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1685 gtk_widget_show(vbox);
1687 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1688 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1690 frame = gtk_frame_new("Y Axis");
1691 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1692 gtk_widget_show(frame);
1694 vbox=gtk_vbox_new(FALSE, 0);
1695 gtk_container_add(GTK_CONTAINER(frame), vbox);
1696 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1697 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1698 gtk_widget_show(vbox);
1700 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1705 /****************************************************************************/
1706 static void dialog_graph_init_window(user_data_t* user_data)
1710 GtkWidget *bt_close;
1712 /* create the main window */
1713 user_data->dlg.dialog_graph.window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
1715 vbox=gtk_vbox_new(FALSE, 0);
1716 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1717 gtk_widget_show(vbox);
1719 create_draw_area(user_data, vbox);
1721 hbox=gtk_hbox_new(FALSE, 3);
1722 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1723 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1724 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1725 gtk_widget_show(hbox);
1727 create_filter_area(user_data, hbox);
1728 create_ctrl_area(user_data, hbox);
1730 dialog_graph_set_title(user_data);
1732 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1733 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1734 gtk_widget_show(hbox);
1736 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1737 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1739 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1741 gtk_widget_show(user_data->dlg.dialog_graph.window);
1742 window_present(user_data->dlg.dialog_graph.window);
1747 /****************************************************************************/
1748 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1750 if (user_data->dlg.dialog_graph.window != NULL) {
1751 /* There's already a graph window; reactivate it. */
1752 reactivate_window(user_data->dlg.dialog_graph.window);
1756 dialog_graph_init_window(user_data);
1760 /****************************************************************************/
1761 static void on_goto_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1765 if (user_data->dlg.selected_clist!=NULL) {
1766 fnumber = GPOINTER_TO_UINT(gtk_clist_get_row_data(
1767 GTK_CLIST(user_data->dlg.selected_clist), user_data->dlg.selected_row) );
1768 cf_goto_frame(&cfile, fnumber);
1773 static void draw_stat(user_data_t *user_data);
1775 /****************************************************************************/
1776 /* re-dissects all packets */
1777 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1779 GString *error_string;
1781 /* remove tap listener */
1782 protect_thread_critical_region();
1783 remove_tap_listener(user_data);
1784 unprotect_thread_critical_region();
1786 /* register tap listener */
1787 error_string = register_tap_listener("rtp", user_data, NULL,
1788 rtp_reset, rtp_packet, rtp_draw);
1789 if (error_string != NULL) {
1790 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, error_string->str);
1791 g_string_free(error_string, TRUE);
1795 /* retap all packets */
1796 cf_retap_packets(&cfile, FALSE);
1798 /* draw statistics info */
1799 draw_stat(user_data);
1801 gtk_clist_sort(user_data->dlg.clist_fwd);
1802 gtk_clist_sort(user_data->dlg.clist_rev);
1805 /****************************************************************************/
1806 static void on_next_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1811 if (user_data->dlg.selected_clist==NULL)
1814 clist = user_data->dlg.selected_clist;
1815 row = user_data->dlg.selected_row + 1;
1817 while (gtk_clist_get_text(clist,row,6,&text)) {
1818 if (strcmp(text, OK_TEXT) != 0) {
1819 gtk_clist_select_row(clist, row, 0);
1820 gtk_clist_moveto(clist, row, 0, 0.5, 0);
1828 while (gtk_clist_get_text(clist,row,6,&text) && row<user_data->dlg.selected_row) {
1829 if (strcmp(text, OK_TEXT) != 0) {
1830 gtk_clist_select_row(clist, row, 0);
1831 gtk_clist_moveto(clist, row, 0, 0.5, 0);
1838 /****************************************************************************/
1839 /* when we want to save the information */
1840 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
1843 GtkWidget *rev, *forw, *both;
1844 user_data_t *user_data;
1850 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
1852 /* Perhaps the user specified a directory instead of a file.
1853 Check whether they did. */
1854 if (test_for_directory(g_dest) == EISDIR) {
1855 /* It's a directory - set the file selection box to display it. */
1856 set_last_open_dir(g_dest);
1858 file_selection_set_current_folder(fs, get_last_open_dir());
1862 rev = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "reversed_rb");
1863 forw = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "forward_rb");
1864 both = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "both_rb");
1865 user_data = (user_data_t*)g_object_get_data(G_OBJECT(bt), "user_data");
1867 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
1868 fp = ws_fopen(g_dest, "w");
1870 open_failure_alert_box(g_dest, errno, TRUE);
1874 if (GTK_TOGGLE_BUTTON(both)->active) {
1875 fprintf(fp, "Forward\n");
1877 write_failure_alert_box(g_dest, errno);
1883 for(j = 0; j < NUM_COLS; j++) {
1885 fprintf(fp,"%s",titles[j]);
1887 fprintf(fp,",%s",titles[j]);
1892 write_failure_alert_box(g_dest, errno);
1896 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_fwd)->rows; i++) {
1897 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_fwd)->columns; j++) {
1898 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_fwd),i,j,&columnText);
1900 fprintf(fp,"%s",columnText);
1902 fprintf(fp,",%s",columnText);
1907 write_failure_alert_box(g_dest, errno);
1913 if (fclose(fp) == EOF) {
1914 write_failure_alert_box(g_dest, errno);
1919 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
1921 if (GTK_TOGGLE_BUTTON(both)->active) {
1922 fp = ws_fopen(g_dest, "a");
1924 open_failure_alert_box(g_dest, errno, TRUE);
1927 fprintf(fp, "\nReverse\n");
1929 write_failure_alert_box(g_dest, errno);
1934 fp = ws_fopen(g_dest, "w");
1936 open_failure_alert_box(g_dest, errno, TRUE);
1940 for(j = 0; j < NUM_COLS; j++) {
1942 fprintf(fp,"%s",titles[j]);
1944 fprintf(fp,",%s",titles[j]);
1949 write_failure_alert_box(g_dest, errno);
1953 for (i = 0; i < GTK_CLIST(user_data->dlg.clist_rev)->rows; i++) {
1954 for(j = 0; j < GTK_CLIST(user_data->dlg.clist_rev)->columns; j++) {
1955 gtk_clist_get_text(GTK_CLIST(user_data->dlg.clist_rev),i,j,&columnText);
1957 fprintf(fp,"%s",columnText);
1959 fprintf(fp,",%s",columnText);
1964 write_failure_alert_box(g_dest, errno);
1969 if (fclose(fp) == EOF) {
1970 write_failure_alert_box(g_dest, errno);
1975 window_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
1978 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
1980 user_data->dlg.save_csv_as_w = NULL;
1983 /* when the user wants to save the csv information in a file */
1984 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
1988 GtkWidget *label_format;
1989 GtkWidget *channels_label;
1990 GSList *channels_group = NULL;
1991 GtkWidget *forward_rb;
1992 GtkWidget *reversed_rb;
1996 if (user_data->dlg.save_csv_as_w != NULL) {
1997 /* There's already a Save CSV info dialog box; reactivate it. */
1998 reactivate_window(user_data->dlg.save_csv_as_w);
2002 user_data->dlg.save_csv_as_w = gtk_file_selection_new("Wireshark: Save Data As CSV");
2004 /* Container for each row of widgets */
2005 vertb = gtk_vbox_new(FALSE, 0);
2006 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2007 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->action_area),
2008 vertb, FALSE, FALSE, 0);
2009 gtk_widget_show (vertb);
2011 table1 = gtk_table_new (2, 4, FALSE);
2012 gtk_widget_show (table1);
2013 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2014 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2015 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2017 label_format = gtk_label_new ("Format: Comma Separated Values");
2018 gtk_widget_show (label_format);
2019 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2020 (GtkAttachOptions) (GTK_FILL),
2021 (GtkAttachOptions) (0), 0, 0);
2024 channels_label = gtk_label_new ("Channels:");
2025 gtk_widget_show (channels_label);
2026 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2027 (GtkAttachOptions) (GTK_FILL),
2028 (GtkAttachOptions) (0), 0, 0);
2029 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2031 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2032 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2033 gtk_widget_show (forward_rb);
2034 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2035 (GtkAttachOptions) (GTK_FILL),
2036 (GtkAttachOptions) (0), 0, 0);
2038 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2039 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2040 gtk_widget_show (reversed_rb);
2041 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2042 (GtkAttachOptions) (GTK_FILL),
2043 (GtkAttachOptions) (0), 0, 0);
2045 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2046 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2047 gtk_widget_show (both_rb);
2048 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2049 (GtkAttachOptions) (GTK_FILL),
2050 (GtkAttachOptions) (0), 0, 0);
2052 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2054 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->ok_button;
2055 g_object_set_data(G_OBJECT(ok_bt), "forward_rb", forward_rb);
2056 g_object_set_data(G_OBJECT(ok_bt), "reversed_rb", reversed_rb);
2057 g_object_set_data(G_OBJECT(ok_bt), "both_rb", both_rb);
2058 g_object_set_data(G_OBJECT(ok_bt), "user_data", user_data);
2059 g_signal_connect(ok_bt, "clicked", G_CALLBACK(save_csv_as_ok_cb),
2060 user_data->dlg.save_csv_as_w);
2062 window_set_cancel_button(user_data->dlg.save_csv_as_w,
2063 GTK_FILE_SELECTION(user_data->dlg.save_csv_as_w)->cancel_button, window_cancel_button_cb);
2065 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
2066 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2067 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2069 gtk_widget_show(user_data->dlg.save_csv_as_w);
2070 window_present(user_data->dlg.save_csv_as_w);
2074 /****************************************************************************/
2075 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2077 /* Note that we no longer have a Save voice info dialog box. */
2078 user_data->dlg.save_voice_as_w = NULL;
2081 /****************************************************************************/
2082 /* here we save it into a file that user specified */
2083 /* XXX what about endians here? could go something wrong? */
2084 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2086 int to_fd, forw_fd, rev_fd, fread = 0, rread = 0, fwritten, rwritten;
2087 gchar f_pd[1] = {0};
2088 gchar r_pd[1] = {0};
2091 guint32 f_write_silence = 0;
2092 guint32 r_write_silence = 0;
2094 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2095 gboolean stop_flag = FALSE;
2098 forw_fd = ws_open(user_data->f_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2101 rev_fd = ws_open(user_data->r_tempname, O_RDONLY | O_BINARY, 0000 /* no creation so don't matter */);
2107 /* open file for saving */
2108 to_fd = ws_open(dest, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
2115 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2117 if (format == SAVE_AU_FORMAT) /* au format */
2119 /* First we write the .au header. XXX Hope this is endian independant */
2120 /* the magic word 0x2e736e64 == .snd */
2121 phtonl(pd, 0x2e736e64);
2122 nchars=ws_write(to_fd, pd, 4);
2123 /* header offset == 24 bytes */
2125 nchars=ws_write(to_fd, pd, 4);
2126 /* total length, it is permited to set this to 0xffffffff */
2128 nchars=ws_write(to_fd, pd, 4);
2129 /* encoding format == 16-bit linear PCM */
2131 nchars=ws_write(to_fd, pd, 4);
2132 /* sample rate == 8000 Hz */
2134 nchars=ws_write(to_fd, pd, 4);
2137 nchars=ws_write(to_fd, pd, 4);
2141 /* only forward direction */
2142 case SAVE_FORWARD_DIRECTION_MASK: {
2143 progbar_count = user_data->forward.saveinfo.count;
2144 progbar_quantum = user_data->forward.saveinfo.count/100;
2145 while ((fread = read(forw_fd, f_pd, 1)) > 0) {
2148 if((count > progbar_nextstep) && (count <= progbar_count)) {
2149 update_progress_dlg(progbar,
2150 (gfloat) count/progbar_count, "Saving");
2151 progbar_nextstep = progbar_nextstep + progbar_quantum;
2155 if (user_data->forward.statinfo.pt == PT_PCMU){
2156 sample = ulaw2linear(*f_pd);
2159 else if(user_data->forward.statinfo.pt == PT_PCMA){
2160 sample = alaw2linear(*f_pd);
2167 destroy_progress_dlg(progbar);
2171 fwritten = ws_write(to_fd, pd, 2);
2172 if ((fwritten < 2) || (fwritten < 0) || (fread < 0)) {
2176 destroy_progress_dlg(progbar);
2182 /* only reversed direction */
2183 case SAVE_REVERSE_DIRECTION_MASK: {
2184 progbar_count = user_data->reversed.saveinfo.count;
2185 progbar_quantum = user_data->reversed.saveinfo.count/100;
2186 while ((rread = read(rev_fd, r_pd, 1)) > 0) {
2189 if((count > progbar_nextstep) && (count <= progbar_count)) {
2190 update_progress_dlg(progbar,
2191 (gfloat) count/progbar_count, "Saving");
2192 progbar_nextstep = progbar_nextstep + progbar_quantum;
2196 if (user_data->reversed.statinfo.pt == PT_PCMU){
2197 sample = ulaw2linear(*r_pd);
2200 else if(user_data->reversed.statinfo.pt == PT_PCMA){
2201 sample = alaw2linear(*r_pd);
2208 destroy_progress_dlg(progbar);
2212 rwritten = ws_write(to_fd, pd, 2);
2213 if ((rwritten < 2) || (rwritten < 0) || (rread < 0)) {
2217 destroy_progress_dlg(progbar);
2223 /* both directions */
2224 case SAVE_BOTH_DIRECTION_MASK: {
2225 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2226 (progbar_count = user_data->forward.saveinfo.count) :
2227 (progbar_count = user_data->reversed.saveinfo.count);
2228 progbar_quantum = progbar_count/100;
2229 /* since conversation in one way can start later than in the other one,
2230 * we have to write some silence information for one channel */
2231 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2232 f_write_silence = (guint32)
2233 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2235 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2236 r_write_silence = (guint32)
2237 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2242 if((count > progbar_nextstep) && (count <= progbar_count)) {
2243 update_progress_dlg(progbar,
2244 (gfloat) count/progbar_count, "Saving");
2245 progbar_nextstep = progbar_nextstep + progbar_quantum;
2248 if(f_write_silence > 0) {
2249 rread = read(rev_fd, r_pd, 1);
2250 switch (user_data->forward.statinfo.reg_pt) {
2252 *f_pd = SILENCE_PCMU;
2255 *f_pd = SILENCE_PCMA;
2261 else if(r_write_silence > 0) {
2262 fread = read(forw_fd, f_pd, 1);
2263 switch (user_data->reversed.statinfo.reg_pt) {
2265 *r_pd = SILENCE_PCMU;
2268 *r_pd = SILENCE_PCMA;
2275 fread = read(forw_fd, f_pd, 1);
2276 rread = read(rev_fd, r_pd, 1);
2278 if ((rread == 0) && (fread == 0))
2280 if ((user_data->forward.statinfo.pt == PT_PCMU) && (user_data->reversed.statinfo.pt == PT_PCMU)){
2281 sample = (ulaw2linear(*r_pd) + ulaw2linear(*f_pd)) / 2;
2284 else if((user_data->forward.statinfo.pt == PT_PCMA) && (user_data->reversed.statinfo.pt == PT_PCMA)){
2285 sample = (alaw2linear(*r_pd) + alaw2linear(*f_pd)) / 2;
2293 destroy_progress_dlg(progbar);
2298 rwritten = ws_write(to_fd, pd, 2);
2299 if ((rwritten < 2) || (rread < 0) || (fread < 0)) {
2303 destroy_progress_dlg(progbar);
2310 else if (format == SAVE_RAW_FORMAT) /* raw format */
2314 /* only forward direction */
2315 case SAVE_FORWARD_DIRECTION_MASK: {
2316 progbar_count = user_data->forward.saveinfo.count;
2317 progbar_quantum = user_data->forward.saveinfo.count/100;
2321 /* only reversed direction */
2322 case SAVE_REVERSE_DIRECTION_MASK: {
2323 progbar_count = user_data->reversed.saveinfo.count;
2324 progbar_quantum = user_data->reversed.saveinfo.count/100;
2332 destroy_progress_dlg(progbar);
2339 /* XXX how do you just copy the file? */
2340 while ((rread = read(fd, pd, 1)) > 0) {
2343 if((count > progbar_nextstep) && (count <= progbar_count)) {
2344 update_progress_dlg(progbar,
2345 (gfloat) count/progbar_count, "Saving");
2346 progbar_nextstep = progbar_nextstep + progbar_quantum;
2350 rwritten = ws_write(to_fd, pd, 1);
2352 if ((rwritten < rread) || (rwritten < 0) || (rread < 0)) {
2356 destroy_progress_dlg(progbar);
2362 destroy_progress_dlg(progbar);
2370 /****************************************************************************/
2371 /* the user wants to save in a file */
2372 /* XXX support for different formats is currently commented out */
2373 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
2376 /*GtkWidget *wav, *sw;*/
2377 GtkWidget *au, *raw;
2378 GtkWidget *rev, *forw, *both;
2379 user_data_t *user_data;
2380 gint channels , format;
2382 g_dest = g_strdup(gtk_file_selection_get_filename(GTK_FILE_SELECTION (fs)));
2384 /* Perhaps the user specified a directory instead of a file.
2385 Check whether they did. */
2386 if (test_for_directory(g_dest) == EISDIR) {
2387 /* It's a directory - set the file selection box to display it. */
2388 set_last_open_dir(g_dest);
2390 file_selection_set_current_folder(fs, get_last_open_dir());
2394 /*wav = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "wav_rb");
2395 sw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "sw_rb");*/
2396 au = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "au_rb");
2397 raw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "raw_rb");
2398 rev = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "reversed_rb");
2399 forw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "forward_rb");
2400 both = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "both_rb");
2401 user_data = (user_data_t *)g_object_get_data(G_OBJECT(ok_bt), "user_data");
2403 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2404 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2405 * disable the ok button or disable the buttons for direction if only one is not ok. The
2406 * problem is if we open the save voice dialog and then click the refresh button and maybe
2407 * the state changes, so we can't save anymore. In this case we should be able to update
2408 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2411 /* we can not save in both directions */
2412 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2413 /* there are many combinations here, we just exit when first matches */
2414 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2415 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2416 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2417 "Can't save in a file: Unsupported codec!");
2418 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2419 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2420 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2421 "Can't save in a file: Wrong length of captured packets!");
2422 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2423 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2424 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2425 "Can't save in a file: RTP data with padding!");
2426 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2427 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2428 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2429 "Can't save in a file: Not all data in all packets was captured!");
2431 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2432 "Can't save in a file: File I/O problem!");
2435 /* we can not save forward direction */
2436 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2437 (GTK_TOGGLE_BUTTON (both)->active))) {
2438 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2439 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2440 "Can't save forward direction in a file: Unsupported codec!");
2441 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2442 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2443 "Can't save forward direction in a file: Wrong length of captured packets!");
2444 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2445 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2446 "Can't save forward direction in a file: RTP data with padding!");
2447 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2448 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2449 "Can't save forward direction in a file: Not all data in all packets was captured!");
2451 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2452 "Can't save forward direction in a file: File I/O problem!");
2455 /* we can not save reversed direction */
2456 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2457 (GTK_TOGGLE_BUTTON (both)->active))) {
2458 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2459 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2460 "Can't save reversed direction in a file: Unsupported codec!");
2461 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2462 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2463 "Can't save reversed direction in a file: Wrong length of captured packets!");
2464 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2465 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2466 "Can't save reversed direction in a file: RTP data with padding!");
2467 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2468 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2469 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2470 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2471 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2472 "Can't save reversed direction in a file: No RTP data!");
2474 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2475 "Can't save reversed direction in a file: File I/O problem!");
2479 /*if (GTK_TOGGLE_BUTTON (wav)->active)
2480 format = SAVE_WAV_FORMAT;
2481 else */if (GTK_TOGGLE_BUTTON (au)->active)
2482 format = SAVE_AU_FORMAT;
2483 /*else if (GTK_TOGGLE_BUTTON (sw)->active)
2484 format = SAVE_SW_FORMAT;*/
2485 else if (GTK_TOGGLE_BUTTON (raw)->active)
2486 format = SAVE_RAW_FORMAT;
2488 format = SAVE_NONE_FORMAT;
2490 if (GTK_TOGGLE_BUTTON (rev)->active)
2491 channels = SAVE_REVERSE_DIRECTION_MASK;
2492 else if (GTK_TOGGLE_BUTTON (both)->active)
2493 channels = SAVE_BOTH_DIRECTION_MASK;
2495 channels = SAVE_FORWARD_DIRECTION_MASK;
2497 /* direction/format validity*/
2498 if (format == SAVE_AU_FORMAT)
2500 /* make sure streams are alaw/ulaw */
2501 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != PT_PCMA) && (user_data->forward.statinfo.pt != PT_PCMU)){
2502 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2503 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2506 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != PT_PCMA) && (user_data->reversed.statinfo.pt != PT_PCMU)){
2507 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2508 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2511 /* make sure pt's don't differ */
2512 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2513 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2514 "Can't save in a file: Forward and reverse direction differ in type");
2518 else if (format == SAVE_RAW_FORMAT)
2520 /* can't save raw in both directions */
2521 if (channels == SAVE_BOTH_DIRECTION_MASK){
2522 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2523 "Can't save in a file: Unable to save raw data in both directions");
2529 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2530 "Can't save in a file: Invalid save format");
2534 if(!copy_file(g_dest, channels, format, user_data)) {
2535 /* XXX - report the error type! */
2536 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2537 "An error occurred while saving voice in a file!");
2541 window_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
2544 /****************************************************************************/
2545 /* when the user wants to save the voice information in a file */
2546 /* XXX support for different formats is currently commented out */
2547 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2551 GtkWidget *label_format;
2552 GtkWidget *channels_label;
2553 GSList *format_group = NULL;
2554 GSList *channels_group = NULL;
2555 GtkWidget *forward_rb;
2556 GtkWidget *reversed_rb;
2558 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2563 /* if we can't save in a file: wrong codec, cut packets or other errors */
2564 /* shold the error arise here or later when you click ok button ?
2565 * if we do it here, then we must disable the refresh button, so we don't do it here */
2567 if (user_data->dlg.save_voice_as_w != NULL) {
2568 /* There's already a Save voice info dialog box; reactivate it. */
2569 reactivate_window(user_data->dlg.save_voice_as_w);
2573 /* XXX - use file_selection from dlg_utils instead! */
2574 user_data->dlg.save_voice_as_w = gtk_file_selection_new("Wireshark: Save Payload As ...");
2576 /* Container for each row of widgets */
2577 vertb = gtk_vbox_new(FALSE, 0);
2578 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2579 gtk_box_pack_start(GTK_BOX(GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->action_area),
2580 vertb, FALSE, FALSE, 0);
2581 gtk_widget_show (vertb);
2583 table1 = gtk_table_new (2, 4, FALSE);
2584 gtk_widget_show (table1);
2585 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2586 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2587 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2589 /*label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2590 gtk_widget_show (label_format);
2591 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2592 (GtkAttachOptions) (GTK_FILL),
2593 (GtkAttachOptions) (0), 0, 0);*/
2595 label_format = gtk_label_new ("Format: ");
2596 gtk_widget_show (label_format);
2597 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2598 (GtkAttachOptions) (GTK_FILL),
2599 (GtkAttachOptions) (0), 0, 0);
2601 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5);
2603 raw_rb = gtk_radio_button_new_with_label (format_group, ".raw");
2604 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (raw_rb));
2605 gtk_widget_show (raw_rb);
2606 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2607 (GtkAttachOptions) (GTK_FILL),
2608 (GtkAttachOptions) (0), 0, 0);
2611 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2612 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2613 gtk_widget_show (au_rb);
2614 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2615 (GtkAttachOptions) (GTK_FILL),
2616 (GtkAttachOptions) (0), 0, 0);
2618 /* we support .au - ulaw*/
2619 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2620 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (wav_rb));
2621 gtk_widget_show (wav_rb);
2622 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2623 (GtkAttachOptions) (GTK_FILL),
2624 (GtkAttachOptions) (0), 0, 0);
2626 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2627 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (sw_rb));
2628 gtk_widget_show (sw_rb);
2629 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2630 (GtkAttachOptions) (GTK_FILL),
2631 (GtkAttachOptions) (0), 0, 0);
2632 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2633 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2634 gtk_widget_show (au_rb);
2635 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2636 (GtkAttachOptions) (GTK_FILL),
2637 (GtkAttachOptions) (0), 0, 0);
2641 channels_label = gtk_label_new ("Channels:");
2642 gtk_widget_show (channels_label);
2643 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2644 (GtkAttachOptions) (GTK_FILL),
2645 (GtkAttachOptions) (0), 0, 0);
2646 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2648 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2649 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2650 gtk_widget_show (forward_rb);
2651 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2652 (GtkAttachOptions) (GTK_FILL),
2653 (GtkAttachOptions) (0), 0, 0);
2655 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2656 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2657 gtk_widget_show (reversed_rb);
2658 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2659 (GtkAttachOptions) (GTK_FILL),
2660 (GtkAttachOptions) (0), 0, 0);
2662 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2663 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2664 gtk_widget_show (both_rb);
2665 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2666 (GtkAttachOptions) (GTK_FILL),
2667 (GtkAttachOptions) (0), 0, 0);
2669 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2671 /* if one direction is nok we don't allow saving
2672 XXX this is not ok since the user can click the refresh button and cause changes
2673 but we can not update this window. So we move all the decision on the time the ok
2675 if (user_data->forward.saved == FALSE) {
2676 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2677 gtk_widget_set_sensitive(forward_rb, FALSE);
2678 gtk_widget_set_sensitive(both_rb, FALSE);
2680 else if (user_data->reversed.saved == FALSE) {
2681 gtk_widget_set_sensitive(reversed_rb, FALSE);
2682 gtk_widget_set_sensitive(both_rb, FALSE);
2686 ok_bt = GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->ok_button;
2687 /*g_object_set_data(G_OBJECT(ok_bt), "wav_rb", wav_rb);*/
2688 g_object_set_data(G_OBJECT(ok_bt), "au_rb", au_rb);
2689 /*g_object_set_data(G_OBJECT(ok_bt), "sw_rb", sw_rb);*/
2690 g_object_set_data(G_OBJECT(ok_bt), "raw_rb", raw_rb);
2691 g_object_set_data(G_OBJECT(ok_bt), "forward_rb", forward_rb);
2692 g_object_set_data(G_OBJECT(ok_bt), "reversed_rb", reversed_rb);
2693 g_object_set_data(G_OBJECT(ok_bt), "both_rb", both_rb);
2694 g_object_set_data(G_OBJECT(ok_bt), "user_data", user_data);
2695 g_signal_connect(ok_bt, "clicked", G_CALLBACK(save_voice_as_ok_cb),
2696 user_data->dlg.save_voice_as_w);
2698 window_set_cancel_button(user_data->dlg.save_voice_as_w,
2699 GTK_FILE_SELECTION(user_data->dlg.save_voice_as_w)->cancel_button, window_cancel_button_cb);
2701 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2702 G_CALLBACK(window_delete_event_cb), NULL);
2703 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2704 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2706 gtk_widget_show(user_data->dlg.save_voice_as_w);
2707 window_present(user_data->dlg.save_voice_as_w);
2711 /****************************************************************************/
2712 /* when we are finished with redisection, we add the label for the statistic */
2713 static void draw_stat(user_data_t *user_data)
2715 gchar label_max[200];
2716 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
2717 - user_data->forward.statinfo.start_seq_nr + 1;
2718 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
2719 - user_data->reversed.statinfo.start_seq_nr + 1;
2720 gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
2721 gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
2722 double f_perc, r_perc;
2724 f_perc = (double)(f_lost*100)/(double)f_expected;
2729 r_perc = (double)(r_lost*100)/(double)r_expected;
2734 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
2735 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2736 " Sequence errors = %u",
2737 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
2738 user_data->forward.statinfo.total_nr,
2739 f_expected, f_lost, f_perc, user_data->forward.statinfo.sequence);
2741 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2743 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
2744 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2745 " Sequence errors = %u",
2746 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
2747 user_data->reversed.statinfo.total_nr,
2748 r_expected, r_lost, r_perc, user_data->reversed.statinfo.sequence);
2750 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2757 /****************************************************************************/
2758 /* append a line to clist */
2759 static void add_to_clist(GtkCList *clist, guint32 number, guint16 seq_num,
2760 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
2761 gchar *timeStr, guint32 pkt_len, GdkColor *bg_color)
2767 GdkColor fg_color = COLOR_FOREGROUND;
2769 data[0]=&field[0][0];
2770 data[1]=&field[1][0];
2771 data[2]=&field[2][0];
2772 data[3]=&field[3][0];
2773 data[4]=&field[4][0];
2774 data[5]=&field[5][0];
2775 data[6]=&field[6][0];
2776 data[7]=&field[7][0];
2777 data[8]=&field[8][0];
2779 /* save the current locale */
2780 savelocale = setlocale(LC_NUMERIC, NULL);
2781 /* switch to "C" locale to avoid problems with localized decimal separators
2782 in g_snprintf("%f") functions */
2783 setlocale(LC_NUMERIC, "C");
2784 g_snprintf(field[0], 20, "%u", number);
2785 g_snprintf(field[1], 20, "%u", seq_num);
2786 g_snprintf(field[2], 20, "%.2f", delta);
2787 g_snprintf(field[3], 20, "%.2f", jitter);
2788 g_snprintf(field[4], 20, "%.2f", bandwidth);
2789 g_snprintf(field[5], 20, "%s", marker? "SET" : "");
2790 g_snprintf(field[6], 40, "%s", status);
2791 g_snprintf(field[7], 32, "%s", timeStr);
2792 g_snprintf(field[8], 20, "%u", pkt_len);
2793 /* restore previous locale setting */
2794 setlocale(LC_NUMERIC, savelocale);
2796 added_row = gtk_clist_append(GTK_CLIST(clist), data);
2797 gtk_clist_set_row_data(GTK_CLIST(clist), added_row, GUINT_TO_POINTER(number));
2798 gtk_clist_set_background(GTK_CLIST(clist), added_row, bg_color);
2799 gtk_clist_set_foreground(GTK_CLIST(clist), added_row, &fg_color);
2803 /****************************************************************************/
2804 /* callback for sorting columns of clist */
2805 static gint rtp_sort_column(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2)
2812 const GtkCListRow *row1 = ptr1;
2813 const GtkCListRow *row2 = ptr2;
2815 text1 = GTK_CELL_TEXT (row1->cell[clist->sort_column])->text;
2816 text2 = GTK_CELL_TEXT (row2->cell[clist->sort_column])->text;
2818 switch(clist->sort_column){
2819 /* columns representing strings */
2823 return strcmp (text1, text2);
2824 /* columns representing ints */
2831 /* columns representing floats */
2837 if (fabs(f1-f2)<0.0000005)
2843 g_assert_not_reached();
2848 /****************************************************************************/
2850 click_column_cb(GtkCList *clist, gint column, gpointer data)
2852 column_arrows *col_arrows = (column_arrows *) data;
2855 gtk_clist_freeze(clist);
2857 for (i = 0; i < NUM_COLS; i++) {
2858 gtk_widget_hide(col_arrows[i].ascend_pm);
2859 gtk_widget_hide(col_arrows[i].descend_pm);
2862 if (column == clist->sort_column) {
2863 if (clist->sort_type == GTK_SORT_ASCENDING) {
2864 clist->sort_type = GTK_SORT_DESCENDING;
2865 gtk_widget_show(col_arrows[column].descend_pm);
2867 clist->sort_type = GTK_SORT_ASCENDING;
2868 gtk_widget_show(col_arrows[column].ascend_pm);
2871 clist->sort_type = GTK_SORT_ASCENDING;
2872 gtk_widget_show(col_arrows[column].ascend_pm);
2873 gtk_clist_set_sort_column(clist, column);
2875 gtk_clist_thaw(clist);
2877 gtk_clist_sort(clist);
2881 /****************************************************************************/
2882 /* Add the packet list */
2884 GtkWidget* create_clist(user_data_t* user_data)
2886 GtkWidget* clist_fwd;
2888 /* clist for the information */
2889 clist_fwd = gtk_clist_new(NUM_COLS);
2890 gtk_widget_show(clist_fwd);
2891 g_signal_connect(clist_fwd, "select_row", G_CALLBACK(on_clist_select_row), user_data);
2893 gtk_clist_column_titles_show(GTK_CLIST(clist_fwd));
2894 gtk_clist_set_compare_func(GTK_CLIST(clist_fwd), rtp_sort_column);
2895 gtk_clist_set_sort_column(GTK_CLIST(clist_fwd), 0);
2896 gtk_clist_set_sort_type(GTK_CLIST(clist_fwd), GTK_SORT_ASCENDING);
2898 /* hide date and length column */
2899 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 7, FALSE);
2900 gtk_clist_set_column_visibility(GTK_CLIST(clist_fwd), 8, FALSE);
2902 /* column widths and justification */
2903 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 0, 60);
2904 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 1, 75);
2905 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 2, 75);
2906 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 3, 75);
2907 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 4, 50);
2908 gtk_clist_set_column_width(GTK_CLIST(clist_fwd), 5, 75);
2909 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 0, GTK_JUSTIFY_RIGHT);
2910 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 1, GTK_JUSTIFY_RIGHT);
2911 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 2, GTK_JUSTIFY_CENTER);
2912 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 3, GTK_JUSTIFY_CENTER);
2913 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 4, GTK_JUSTIFY_CENTER);
2914 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 5, GTK_JUSTIFY_CENTER);
2915 gtk_clist_set_column_justification(GTK_CLIST(clist_fwd), 6, GTK_JUSTIFY_CENTER);
2920 /****************************************************************************/
2921 /* Add the sort by column feature for a packet clist */
2923 column_arrows* add_sort_by_column(GtkWidget* window, GtkWidget* clist,
2924 user_data_t* user_data _U_)
2926 column_arrows *col_arrows;
2927 GdkBitmap *ascend_bm, *descend_bm;
2928 GdkPixmap *ascend_pm, *descend_pm;
2929 GtkStyle *win_style;
2930 GtkWidget *column_lb;
2933 col_arrows = (column_arrows *) g_malloc(sizeof(column_arrows) * NUM_COLS);
2934 win_style = gtk_widget_get_style(window);
2935 ascend_pm = gdk_pixmap_create_from_xpm_d(window->window,
2937 &win_style->bg[GTK_STATE_NORMAL],
2938 (gchar **)clist_ascend_xpm);
2939 descend_pm = gdk_pixmap_create_from_xpm_d(window->window,
2941 &win_style->bg[GTK_STATE_NORMAL],
2942 (gchar **)clist_descend_xpm);
2944 for (i=0; i<NUM_COLS; i++) {
2945 col_arrows[i].table = gtk_table_new(2, 2, FALSE);
2946 gtk_table_set_col_spacings(GTK_TABLE(col_arrows[i].table), 5);
2947 column_lb = gtk_label_new(titles[i]);
2948 gtk_table_attach(GTK_TABLE(col_arrows[i].table), column_lb, 0, 1, 0, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
2949 gtk_widget_show(column_lb);
2951 col_arrows[i].ascend_pm = gtk_pixmap_new(ascend_pm, ascend_bm);
2952 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].ascend_pm, 1, 2, 1, 2, GTK_SHRINK, GTK_SHRINK, 0, 0);
2953 col_arrows[i].descend_pm = gtk_pixmap_new(descend_pm, descend_bm);
2954 gtk_table_attach(GTK_TABLE(col_arrows[i].table), col_arrows[i].descend_pm, 1, 2, 0, 1, GTK_SHRINK, GTK_SHRINK, 0, 0);
2955 /* make packet-nr be the default sort order */
2957 gtk_widget_show(col_arrows[i].ascend_pm);
2959 gtk_clist_set_column_widget(GTK_CLIST(clist), i, col_arrows[i].table);
2960 gtk_widget_show(col_arrows[i].table);
2963 g_signal_connect(clist, "click-column", G_CALLBACK(click_column_cb), col_arrows);
2968 /****************************************************************************/
2969 /* Create the dialog box with all widgets */
2970 static void create_rtp_dialog(user_data_t* user_data)
2972 GtkWidget *window = NULL;
2973 GtkWidget *clist_fwd;
2974 GtkWidget *clist_rev;
2975 GtkWidget *label_stats_fwd;
2976 GtkWidget *label_stats_rev;
2977 GtkWidget *notebook;
2979 GtkWidget *main_vb, *page, *page_r;
2981 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
2982 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
2983 #ifdef USE_CONVERSATION_GRAPH
2984 GtkWidget *graph_bt;
2986 GtkWidget *graph_bt;
2987 gchar label_forward[150];
2988 gchar label_reverse[150];
2990 gchar str_ip_src[16];
2991 gchar str_ip_dst[16];
2992 column_arrows *col_arrows_fwd;
2993 column_arrows *col_arrows_rev;
2995 window = window_new(GTK_WINDOW_TOPLEVEL, "Wireshark: RTP Stream Analysis");
2996 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
2998 /* Container for each row of widgets */
2999 main_vb = gtk_vbox_new(FALSE, 2);
3000 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3001 gtk_container_add(GTK_CONTAINER(window), main_vb);
3002 gtk_widget_show(main_vb);
3005 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), 16);
3006 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), 16);
3008 g_snprintf(label_forward, 149,
3009 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3010 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3013 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), 16);
3014 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), 16);
3016 g_snprintf(label_reverse, 149,
3017 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3018 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3020 /* Start a notebook for flipping between sets of changes */
3021 notebook = gtk_notebook_new();
3022 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3023 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3025 user_data->dlg.notebook_signal_id =
3026 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3028 /* page for forward connection */
3029 page = gtk_vbox_new(FALSE, 8);
3030 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3032 /* direction label */
3033 label = gtk_label_new(label_forward);
3034 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3036 /* place for some statistics */
3037 label_stats_fwd = gtk_label_new("\n");
3038 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3040 /* scrolled window */
3041 scrolled_window = scrolled_window_new(NULL, NULL);
3044 clist_fwd = create_clist(user_data);
3045 gtk_widget_show(clist_fwd);
3046 gtk_container_add(GTK_CONTAINER(scrolled_window), clist_fwd);
3047 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3048 gtk_widget_show(scrolled_window);
3051 label = gtk_label_new(" Forward Direction ");
3052 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3054 /* same page for reversed connection */
3055 page_r = gtk_vbox_new(FALSE, 8);
3056 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3057 label = gtk_label_new(label_reverse);
3058 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3059 label_stats_rev = gtk_label_new("\n");
3060 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3062 scrolled_window_r = scrolled_window_new(NULL, NULL);
3064 clist_rev = create_clist(user_data);
3065 gtk_widget_show(clist_rev);
3066 gtk_container_add(GTK_CONTAINER(scrolled_window_r), clist_rev);
3067 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3068 gtk_widget_show(scrolled_window_r);
3070 label = gtk_label_new(" Reversed Direction ");
3071 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3073 /* page for help&about or future
3074 page_help = gtk_hbox_new(FALSE, 5);
3075 label = gtk_label_new(" Future ");
3076 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3077 frame = gtk_frame_new("");
3078 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3079 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3080 gtk_container_add(GTK_CONTAINER(frame), text);
3081 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3082 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3085 /* show all notebooks */
3086 gtk_widget_show_all(notebook);
3089 box4 = gtk_hbutton_box_new();
3090 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3091 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3092 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3093 gtk_box_set_spacing(GTK_BOX (box4), 0);
3094 gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
3095 gtk_widget_show(box4);
3097 voice_bt = gtk_button_new_with_label("Save payload...");
3098 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3099 gtk_widget_show(voice_bt);
3100 g_signal_connect(voice_bt, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
3102 csv_bt = gtk_button_new_with_label("Save as CSV...");
3103 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3104 gtk_widget_show(csv_bt);
3105 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3107 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3108 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3109 gtk_widget_show(refresh_bt);
3110 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3112 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3113 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3114 gtk_widget_show(goto_bt);
3115 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked), user_data);
3117 graph_bt = gtk_button_new_with_label("Graph");
3118 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3119 gtk_widget_show(graph_bt);
3120 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3123 #ifdef USE_CONVERSATION_GRAPH
3124 graph_bt = gtk_button_new_with_label("Graph");
3125 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3126 gtk_widget_show(graph_bt);
3127 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3130 next_bt = gtk_button_new_with_label("Next non-Ok");
3131 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3132 gtk_widget_show(next_bt);
3133 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked), user_data);
3135 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3136 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3137 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3138 gtk_widget_show(close_bt);
3139 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3141 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3142 g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3144 gtk_widget_show(window);
3145 window_present(window);
3147 /* sort by column feature */
3148 col_arrows_fwd = add_sort_by_column(window, clist_fwd, user_data);
3149 col_arrows_rev = add_sort_by_column(window, clist_rev, user_data);
3151 /* some widget references need to be saved for outside use */
3152 user_data->dlg.window = window;
3153 user_data->dlg.clist_fwd = GTK_CLIST(clist_fwd);
3154 user_data->dlg.clist_rev = GTK_CLIST(clist_rev);
3155 user_data->dlg.label_stats_fwd = label_stats_fwd;
3156 user_data->dlg.label_stats_rev = label_stats_rev;
3157 user_data->dlg.notebook = notebook;
3158 user_data->dlg.selected_clist = GTK_CLIST(clist_fwd);
3159 user_data->dlg.selected_row = 0;
3160 user_data->dlg.col_arrows_fwd = col_arrows_fwd;
3161 user_data->dlg.col_arrows_rev = col_arrows_rev;
3165 /****************************************************************************/
3166 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3167 const gchar* proto_field, guint32* p_result)
3170 proto_node *proto_sibling_node;
3171 header_field_info *hfssrc;
3174 finfo = PITEM_FINFO(ptree_node);
3176 if (hfinformation==(finfo->hfinfo)) {
3177 hfssrc = proto_registrar_get_byname(proto_field);
3180 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3181 ptree_node=ptree_node->next) {
3182 finfo=PITEM_FINFO(ptree_node);
3183 if (hfssrc==finfo->hfinfo) {
3184 if (hfinformation->type==FT_IPv4) {
3185 ipv4 = fvalue_get(&finfo->value);
3186 *p_result = ipv4_get_net_order_addr(ipv4);
3189 *p_result = fvalue_get_uinteger(&finfo->value);
3198 proto_sibling_node = ptree_node->next;
3200 if (proto_sibling_node) {
3201 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3207 /****************************************************************************/
3208 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3209 const gchar* proto_name,
3210 const gchar* proto_field,
3213 proto_node *ptree_node;
3214 header_field_info *hfinformation;
3216 hfinformation = proto_registrar_get_byname(proto_name);
3217 if (hfinformation == NULL)
3220 ptree_node = ((proto_node *)protocol_tree)->first_child;
3224 return process_node(ptree_node, hfinformation, proto_field, p_result);
3228 /****************************************************************************/
3230 address *ip_src_fwd,
3231 guint16 port_src_fwd,
3232 address *ip_dst_fwd,
3233 guint16 port_dst_fwd,
3235 address *ip_src_rev,
3236 guint16 port_src_rev,
3237 address *ip_dst_rev,
3238 guint16 port_dst_rev,
3242 user_data_t *user_data;
3245 static color_t col[MAX_GRAPHS] = {
3246 {0, 0x0000, 0x0000, 0x0000},
3247 {0, 0xffff, 0x0000, 0x0000},
3248 {0, 0x0000, 0xffff, 0x0000},
3249 {0, 0x0000, 0x0000, 0xffff}
3253 user_data = g_malloc(sizeof(user_data_t));
3255 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3256 user_data->port_src_fwd = port_src_fwd;
3257 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3258 user_data->port_dst_fwd = port_dst_fwd;
3259 user_data->ssrc_fwd = ssrc_fwd;
3260 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3261 user_data->port_src_rev = port_src_rev;
3262 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3263 user_data->port_dst_rev = port_dst_rev;
3264 user_data->ssrc_rev = ssrc_rev;
3267 /* file names for storing sound data */
3268 /*XXX: check for errors*/
3269 fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
3272 fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
3275 user_data->forward.saveinfo.fp = NULL;
3276 user_data->reversed.saveinfo.fp = NULL;
3277 user_data->dlg.save_voice_as_w = NULL;
3278 user_data->dlg.save_csv_as_w = NULL;
3279 user_data->dlg.dialog_graph.window = NULL;
3281 #ifdef USE_CONVERSATION_GRAPH
3282 user_data->dlg.graph_window = NULL;
3283 user_data->series_fwd.value_pairs = NULL;
3284 user_data->series_rev.value_pairs = NULL;
3287 /* init dialog_graph */
3288 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3289 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3290 user_data->dlg.dialog_graph.draw_area=NULL;
3291 user_data->dlg.dialog_graph.pixmap=NULL;
3292 user_data->dlg.dialog_graph.scrollbar=NULL;
3293 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3294 user_data->dlg.dialog_graph.pixmap_width=500;
3295 user_data->dlg.dialog_graph.pixmap_height=200;
3296 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3297 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3298 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3299 user_data->dlg.dialog_graph.max_interval=0;
3300 user_data->dlg.dialog_graph.num_items=0;
3301 user_data->dlg.dialog_graph.start_time = -1;
3303 for(i=0;i<MAX_GRAPHS;i++){
3304 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3305 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3306 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3307 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3308 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3309 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3310 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3311 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3314 /* create the dialog box */
3315 create_rtp_dialog(user_data);
3317 /* proceed as if the Refresh button would have been pressed */
3318 on_refresh_bt_clicked(NULL, user_data);
3321 /****************************************************************************/
3322 /* entry point from main menu */
3323 static void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3326 guint16 port_src_fwd;
3328 guint16 port_dst_fwd;
3329 guint32 ssrc_fwd = 0;
3331 guint16 port_src_rev;
3333 guint16 port_dst_rev;
3334 guint32 ssrc_rev = 0;
3335 unsigned int version_fwd;
3337 gchar filter_text[256];
3340 epan_dissect_t *edt;
3343 gboolean frame_matched;
3345 GList *strinfo_list;
3346 GList *filtered_list = NULL;
3347 rtp_stream_info_t *strinfo;
3350 /* Try to compile the filter. */
3351 g_strlcpy(filter_text,"rtp && rtp.version && rtp.ssrc && (ip || ipv6)",256);
3352 if (!dfilter_compile(filter_text, &sfcode)) {
3353 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, dfilter_error_msg);
3356 /* we load the current file into cf variable */
3358 fdata = cf->current_frame;
3360 /* we are on the selected frame now */
3362 return; /* if we exit here it's an error */
3364 /* dissect the current frame */
3365 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3366 cf->pd, fdata->cap_len, &err, &err_info)) {
3367 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3368 cf_read_error_message(err, err_info), cf->filename);
3371 edt = epan_dissect_new(TRUE, FALSE);
3372 epan_dissect_prime_dfilter(edt, sfcode);
3373 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3374 frame_matched = dfilter_apply_edt(sfcode, edt);
3376 /* if it is not an rtp frame, show the rtpstream dialog */
3377 frame_matched = dfilter_apply_edt(sfcode, edt);
3378 if (frame_matched != 1) {
3379 epan_dissect_free(edt);
3380 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3381 "You didn't choose a RTP packet!");
3385 /* ok, it is a RTP frame, so let's get the ip and port values */
3386 COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
3387 COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
3388 port_src_fwd = edt->pi.srcport;
3389 port_dst_fwd = edt->pi.destport;
3391 /* assume the inverse ip/port combination for the reverse direction */
3392 COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
3393 COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
3394 port_src_rev = edt->pi.destport;
3395 port_dst_rev = edt->pi.srcport;
3397 /* check if it is RTP Version 2 */
3398 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3399 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3400 "RTP Version != 2 isn't supported!");
3404 /* now we need the SSRC value of the current frame */
3405 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3406 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3407 "SSRC value couldn't be found!");
3411 /* Scan for rtpstream */
3413 /* search for reversed direction in the global rtp streams list */
3415 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3416 while (strinfo_list)
3418 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3419 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3420 && strinfo->src_port==port_src_fwd
3421 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3422 && strinfo->dest_port==port_dst_fwd)
3424 filtered_list = g_list_prepend(filtered_list, strinfo);
3427 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3428 && strinfo->src_port==port_src_rev
3429 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3430 && strinfo->dest_port==port_dst_rev)
3433 filtered_list = g_list_append(filtered_list, strinfo);
3435 ssrc_rev = strinfo->ssrc;
3438 strinfo_list = g_list_next(strinfo_list);
3441 /* if more than one reverse streams found, we let the user choose the right one */
3443 rtpstream_dlg_show(filtered_list);
3462 /****************************************************************************/
3464 rtp_analysis_init(const char *dummy _U_,void* userdata _U_)
3466 rtp_analysis_cb(NULL, NULL);
3469 /****************************************************************************/
3471 register_tap_listener_rtp_analysis(void)
3473 register_stat_cmd_arg("rtp", rtp_analysis_init,NULL);
3475 register_stat_menu_item("RTP/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3476 rtp_analysis_cb, NULL, NULL, NULL);