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"
97 N_COLUMN /* The number of columns */
99 /****************************************************************************/
102 #define NUM_GRAPH_ITEMS 100000
103 #define MAX_YSCALE 16
104 #define AUTO_MAX_YSCALE 0
106 #define GRAPH_FWD_JITTER 0
107 #define GRAPH_FWD_DIFF 1
108 #define GRAPH_REV_JITTER 2
109 #define GRAPH_REV_DIFF 3
110 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
112 #define MAX_PIXELS_PER_TICK 4
113 #define DEFAULT_PIXELS_PER_TICK 1
114 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
115 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
117 #define MAX_TICK_VALUES 5
118 #define DEFAULT_TICK_VALUE 1
119 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
120 typedef struct _dialog_graph_graph_item_t {
123 } dialog_graph_graph_item_t;
125 typedef struct _dialog_graph_graph_t {
126 struct _user_data_t *ud;
127 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
130 GtkWidget *display_button;
135 } dialog_graph_graph_t;
138 typedef struct _dialog_graph_t {
139 gboolean needs_redraw;
140 gint32 interval; /* measurement interval in ms */
141 guint32 last_interval;
142 guint32 max_interval; /* XXX max_interval and num_items are redundant */
144 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
146 GtkWidget *draw_area;
148 GtkAdjustment *scrollbar_adjustment;
149 GtkWidget *scrollbar;
157 typedef struct _dialog_data_t {
162 GtkWidget *label_stats_fwd;
163 GtkWidget *label_stats_rev;
164 GtkWidget *selected_list;
166 GtkTreeSelection *selected_list_sel;
167 gint selected_list_row;
169 GtkWidget *save_voice_as_w;
170 GtkWidget *save_csv_as_w;
171 gint notebook_signal_id;
172 dialog_graph_t dialog_graph;
173 #ifdef USE_CONVERSATION_GRAPH
174 GtkWidget *graph_window;
178 #define OK_TEXT "[ Ok ]"
180 /* type of error when saving voice in a file didn't succeed */
183 TAP_RTP_WRONG_LENGTH,
184 TAP_RTP_PADDING_ERROR,
186 TAP_RTP_FILE_OPEN_ERROR,
190 typedef struct _tap_rtp_save_info_t {
193 error_type_t error_type;
195 } tap_rtp_save_info_t;
198 /* structure that holds the information about the forward and reversed direction */
199 struct _info_direction {
200 tap_rtp_stat_t statinfo;
201 tap_rtp_save_info_t saveinfo;
204 #define TMPNAMSIZE 100
206 #define SILENCE_PCMU (guint8)0xFF
207 #define SILENCE_PCMA (guint8)0x55
209 /* structure that holds general information about the connection
210 * and structures for both directions */
211 typedef struct _user_data_t {
212 /* tap associated data*/
214 guint16 port_src_fwd;
216 guint16 port_dst_fwd;
219 guint16 port_src_rev;
221 guint16 port_dst_rev;
224 struct _info_direction forward;
225 struct _info_direction reversed;
227 char f_tempname[TMPNAMSIZE];
228 char r_tempname[TMPNAMSIZE];
230 /* dialog associated data */
233 #ifdef USE_CONVERSATION_GRAPH
234 time_series_t series_fwd;
235 time_series_t series_rev;
241 static const gchar *titles[9] = {
253 #define SAVE_FORWARD_DIRECTION_MASK 0x01
254 #define SAVE_REVERSE_DIRECTION_MASK 0x02
255 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
257 #define SAVE_NONE_FORMAT 0
258 #define SAVE_WAV_FORMAT 1
259 #define SAVE_AU_FORMAT 2
260 #define SAVE_SW_FORMAT 3
261 #define SAVE_RAW_FORMAT 4
264 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_);
265 /****************************************************************************/
266 static void enable_graph(dialog_graph_graph_t *dgg)
273 static void dialog_graph_reset(user_data_t* user_data);
277 /****************************************************************************/
280 /****************************************************************************/
281 /* when there is a [re]reading of packet's */
283 rtp_reset(void *user_data_arg)
285 user_data_t *user_data = user_data_arg;
286 user_data->forward.statinfo.first_packet = TRUE;
287 user_data->reversed.statinfo.first_packet = TRUE;
288 user_data->forward.statinfo.max_delta = 0;
289 user_data->reversed.statinfo.max_delta = 0;
290 user_data->forward.statinfo.max_jitter = 0;
291 user_data->reversed.statinfo.max_jitter = 0;
292 user_data->forward.statinfo.mean_jitter = 0;
293 user_data->reversed.statinfo.mean_jitter = 0;
294 user_data->forward.statinfo.delta = 0;
295 user_data->reversed.statinfo.delta = 0;
296 user_data->forward.statinfo.diff = 0;
297 user_data->reversed.statinfo.diff = 0;
298 user_data->forward.statinfo.jitter = 0;
299 user_data->reversed.statinfo.jitter = 0;
300 user_data->forward.statinfo.bandwidth = 0;
301 user_data->reversed.statinfo.bandwidth = 0;
302 user_data->forward.statinfo.total_bytes = 0;
303 user_data->reversed.statinfo.total_bytes = 0;
304 user_data->forward.statinfo.bw_start_index = 0;
305 user_data->reversed.statinfo.bw_start_index = 0;
306 user_data->forward.statinfo.bw_index = 0;
307 user_data->reversed.statinfo.bw_index = 0;
308 user_data->forward.statinfo.timestamp = 0;
309 user_data->reversed.statinfo.timestamp = 0;
310 user_data->forward.statinfo.max_nr = 0;
311 user_data->reversed.statinfo.max_nr = 0;
312 user_data->forward.statinfo.total_nr = 0;
313 user_data->reversed.statinfo.total_nr = 0;
314 user_data->forward.statinfo.sequence = 0;
315 user_data->reversed.statinfo.sequence = 0;
316 user_data->forward.statinfo.start_seq_nr = 0;
317 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
318 user_data->forward.statinfo.stop_seq_nr = 0;
319 user_data->reversed.statinfo.stop_seq_nr = 0;
320 user_data->forward.statinfo.cycles = 0;
321 user_data->reversed.statinfo.cycles = 0;
322 user_data->forward.statinfo.under = FALSE;
323 user_data->reversed.statinfo.under = FALSE;
324 user_data->forward.statinfo.start_time = 0;
325 user_data->reversed.statinfo.start_time = 0;
326 user_data->forward.statinfo.time = 0;
327 user_data->reversed.statinfo.time = 0;
328 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
329 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
331 user_data->forward.saveinfo.count = 0;
332 user_data->reversed.saveinfo.count = 0;
333 user_data->forward.saveinfo.saved = FALSE;
334 user_data->reversed.saveinfo.saved = FALSE;
336 /* clear the dialog box lists */
337 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
338 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
340 /* reset graph info */
341 dialog_graph_reset(user_data);
343 #ifdef USE_CONVERSATION_GRAPH
344 if (user_data->dlg.graph_window != NULL)
345 window_destroy(user_data->dlg.graph_window);
347 g_array_free(user_data->series_fwd.value_pairs, TRUE);
348 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
350 g_array_free(user_data->series_rev.value_pairs, TRUE);
351 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
354 /* XXX check for error at fclose? */
355 if (user_data->forward.saveinfo.fp != NULL)
356 fclose(user_data->forward.saveinfo.fp);
357 if (user_data->reversed.saveinfo.fp != NULL)
358 fclose(user_data->reversed.saveinfo.fp);
359 user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
360 if (user_data->forward.saveinfo.fp == NULL)
361 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
362 user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
363 if (user_data->reversed.saveinfo.fp == NULL)
364 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
368 /****************************************************************************/
369 static int rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
371 dialog_graph_graph_item_t *it;
375 /* we sometimes get called when dgg is disabled.
376 this is a bug since the tap listener should be removed first */
381 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
384 * Find which interval this is supposed to to in and store the
385 * interval index as idx
387 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
388 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
390 rtp_time = nstime_to_sec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
394 idx = (guint32)(rtp_time*1000)/dgg->ud->dlg.dialog_graph.interval;
396 /* some sanity checks */
397 if((idx<0)||(idx>=NUM_GRAPH_ITEMS)){
401 /* update num_items */
402 if((guint32)idx > dgg->ud->dlg.dialog_graph.num_items){
403 dgg->ud->dlg.dialog_graph.num_items=idx;
404 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
408 * Find the appropriate dialog_graph_graph_item_t structure
413 * Use the max value to highlight RTP problems
415 if (value > it->value) {
418 it->flags = it->flags | statinfo->flags;
423 /****************************************************************************/
424 /* here we can redraw the output */
426 static void rtp_draw(void *prs _U_)
431 /* forward declarations */
432 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num,
433 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
434 gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
436 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
437 tap_rtp_stat_t *statinfo, packet_info *pinfo,
438 const struct _rtp_info *rtpinfo);
440 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
441 tap_rtp_stat_t *statinfo,
443 const struct _rtp_info *rtpinfo);
446 /****************************************************************************/
447 /* whenever a RTP packet is seen by the tap listener */
448 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
450 user_data_t *user_data = user_data_arg;
451 const struct _rtp_info *rtpinfo = rtpinfo_arg;
452 #ifdef USE_CONVERSATION_GRAPH
455 /* we ignore packets that are not displayed */
456 if (pinfo->fd->flags.passed_dfilter == 0)
458 /* also ignore RTP Version != 2 */
459 else if (rtpinfo->info_version !=2)
461 /* is it the forward direction? */
462 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
463 && CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
464 && user_data->port_src_fwd == pinfo->srcport
465 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
466 && user_data->port_dst_fwd == pinfo->destport) {
467 #ifdef USE_CONVERSATION_GRAPH
468 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
469 vp.fnumber = pinfo->fd->num;
470 g_array_append_val(user_data->series_fwd.value_pairs, vp);
472 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
473 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));
474 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));
475 rtp_packet_add_info(user_data->dlg.list_fwd, user_data,
476 &(user_data->forward.statinfo), pinfo, rtpinfo);
477 rtp_packet_save_payload(&(user_data->forward.saveinfo),
478 &(user_data->forward.statinfo), pinfo, rtpinfo);
480 /* is it the reversed direction? */
481 else if (user_data->ssrc_rev == rtpinfo->info_sync_src
482 && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
483 && user_data->port_src_rev == pinfo->srcport
484 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
485 && user_data->port_dst_rev == pinfo->destport) {
486 #ifdef USE_CONVERSATION_GRAPH
487 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
488 vp.fnumber = pinfo->fd->num;
489 g_array_append_val(user_data->series_rev.value_pairs, vp);
491 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
492 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));
493 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));
494 rtp_packet_add_info(user_data->dlg.list_rev, user_data,
495 &(user_data->reversed.statinfo), pinfo, rtpinfo);
496 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
497 &(user_data->reversed.statinfo), pinfo, rtpinfo);
504 Replaced by using the strings instead.
505 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
506 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
507 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
508 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
509 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
511 /****************************************************************************/
512 /* adds statistics information from the packet to the list */
513 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
514 tap_rtp_stat_t *statinfo, packet_info *pinfo,
515 const struct _rtp_info *rtpinfo)
523 then = pinfo->fd->abs_ts.secs;
524 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
525 tm_tmp = localtime(&then);
526 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
529 tm_tmp->tm_year + 1900,
535 /* Default to using black on white text if nothing below overrides it */
536 g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
538 if (statinfo->pt == PT_CN) {
539 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
540 /* color = COLOR_CN; */
541 g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
543 else if (statinfo->pt == PT_CN_OLD) {
544 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
545 /* color = COLOR_CN; */
546 g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
548 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
549 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
550 /* color = COLOR_ERROR; */
551 g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
553 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
554 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
555 /* color = COLOR_WARNING; */
556 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
558 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
559 g_snprintf(status,sizeof(status),"Incorrect timestamp");
560 /* color = COLOR_WARNING; */
561 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
563 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
564 && !(statinfo->flags & STAT_FLAG_FIRST)
565 && !(statinfo->flags & STAT_FLAG_PT_CN)
566 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
567 && !(statinfo->flags & STAT_FLAG_MARKER)) {
568 g_snprintf(status,sizeof(status),"Marker missing?");
569 /* color = COLOR_WARNING; */
570 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
573 if (statinfo->flags & STAT_FLAG_MARKER) {
574 /* color = COLOR_WARNING; */
575 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
577 g_snprintf(status,sizeof(status),OK_TEXT);
579 /* is this the first packet we got in this direction? */
580 if (statinfo->flags & STAT_FLAG_FIRST) {
581 add_to_list(list, user_data,
582 pinfo->fd->num, rtpinfo->info_seq_num,
587 rtpinfo->info_marker_set,
588 timeStr, pinfo->fd->pkt_len,
593 add_to_list(list, user_data,
594 pinfo->fd->num, rtpinfo->info_seq_num,
595 statinfo->delta*1000,
596 statinfo->jitter*1000,
599 rtpinfo->info_marker_set,
600 timeStr, pinfo->fd->pkt_len,
607 #define MAX_SILENCE_TICKS 1000000
608 /****************************************************************************/
609 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
610 tap_rtp_stat_t *statinfo,
612 const struct _rtp_info *rtpinfo)
619 /* is this the first packet we got in this direction? */
620 if (statinfo->flags & STAT_FLAG_FIRST) {
621 if (saveinfo->fp == NULL) {
622 saveinfo->saved = FALSE;
623 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
626 saveinfo->saved = TRUE;
629 /* save the voice information */
630 /* if there was already an error, we quit */
631 if (saveinfo->saved == FALSE)
634 /* if the captured length and packet length aren't equal, we quit
635 * if also the RTP dissector thinks there is some information missing */
636 if ((pinfo->fd->pkt_len != pinfo->fd->cap_len) &&
637 (!rtpinfo->info_all_data_present)) {
638 saveinfo->saved = FALSE;
639 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
643 /* if padding bit is set, but the padding count is bigger
644 * then the whole RTP data - error with padding count */
645 if ( (rtpinfo->info_padding_set != FALSE) &&
646 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
647 saveinfo->saved = FALSE;
648 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
652 /* do we need to insert some silence? */
653 if ((rtpinfo->info_marker_set) &&
654 !(statinfo->flags & STAT_FLAG_FIRST) &&
655 !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) &&
656 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
657 /* the amount of silence should be the difference between
658 * the last timestamp and the current one minus x
659 * x should equal the amount of information in the last frame
660 * XXX not done yet */
661 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
662 rtpinfo->info_padding_count) && i < MAX_SILENCE_TICKS; i++) {
663 switch (statinfo->reg_pt) {
674 nchars=fwrite(&tmp, 1, 1, saveinfo->fp);
677 fflush(saveinfo->fp);
681 if (rtpinfo->info_payload_type == PT_CN
682 || rtpinfo->info_payload_type == PT_CN_OLD) {
684 /*all other payloads*/
686 if (!rtpinfo->info_all_data_present) {
687 /* Not all the data was captured. */
688 saveinfo->saved = FALSE;
689 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
693 /* we put the pointer at the beginning of the RTP
694 * payload, that is, at the beginning of the RTP data
695 * plus the offset of the payload from the beginning
697 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
698 nchars=fwrite(data, sizeof(unsigned char), (rtpinfo->info_payload_len - rtpinfo->info_padding_count), saveinfo->fp);
699 saveinfo->count+=(rtpinfo->info_payload_len - rtpinfo->info_padding_count);
701 fflush(saveinfo->fp);
702 saveinfo->saved = TRUE;
710 /****************************************************************************/
713 /****************************************************************************/
715 /****************************************************************************/
716 /* close the dialog window and remove the tap listener */
717 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data _U_)
719 /* remove tap listener */
720 protect_thread_critical_region();
721 remove_tap_listener(user_data);
722 unprotect_thread_critical_region();
724 /* close and remove temporary files */
725 if (user_data->forward.saveinfo.fp != NULL)
726 fclose(user_data->forward.saveinfo.fp);
727 if (user_data->reversed.saveinfo.fp != NULL)
728 fclose(user_data->reversed.saveinfo.fp);
729 /*XXX: test for error **/
730 ws_remove(user_data->f_tempname);
731 ws_remove(user_data->r_tempname);
733 /* destroy save_voice_as window if open */
734 if (user_data->dlg.save_voice_as_w != NULL)
735 window_destroy(user_data->dlg.save_voice_as_w);
737 /* destroy graph window if open */
738 if (user_data->dlg.dialog_graph.window != NULL)
739 window_destroy(user_data->dlg.dialog_graph.window);
741 #ifdef USE_CONVERSATION_GRAPH
742 /* destroy graph window if open */
743 if (user_data->dlg.graph_window != NULL)
744 window_destroy(user_data->dlg.graph_window);
747 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
748 g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
754 /****************************************************************************/
755 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
756 GtkNotebookPage *page _U_,
758 user_data_t *user_data _U_)
760 user_data->dlg.selected_list =
761 (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
763 user_data->dlg.selected_list_row = 0;
766 /****************************************************************************/
768 static void on_list_select_row(GtkTreeSelection *selection,
769 user_data_t *user_data _U_/*gpointer data */)
771 user_data->dlg.selected_list_sel = selection;
774 #ifdef USE_CONVERSATION_GRAPH
775 Note this will not work any more as clist is removed.
776 /****************************************************************************/
777 /* when the graph window gets destroyed */
778 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data _U_)
780 /* note that graph window has been destroyed */
781 user_data->dlg.graph_window = NULL;
784 /****************************************************************************/
785 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
788 GtkCList *clist = NULL;
789 if (vp.fnumber != 0) {
790 clist = GTK_CLIST(user_data->dlg.clist_fwd);
791 row = gtk_clist_find_row_from_data(clist,
792 GUINT_TO_POINTER(vp.fnumber));
794 clist = GTK_CLIST(user_data->dlg.clist_rev);
795 row = gtk_clist_find_row_from_data(clist,
796 GUINT_TO_POINTER(vp.fnumber));
799 gtk_notebook_set_current_page(GTK_NOTEBOOK(user_data->dlg.notebook),
800 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
801 gtk_clist_select_row(clist, row, 0);
802 gtk_clist_moveto(clist, row, 0, 0.5, 0);
808 /****************************************************************************/
809 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
815 if (user_data->dlg.graph_window != NULL) {
816 /* There's already a graph window; reactivate it. */
817 reactivate_window(user_data->dlg.graph_window);
820 list = g_list_append(list, &(user_data->series_fwd));
821 list = g_list_append(list, &(user_data->series_rev));
823 user_data->series_fwd.color.pixel = 0;
824 user_data->series_fwd.color.red = 0x80ff;
825 user_data->series_fwd.color.green = 0xe0ff;
826 user_data->series_fwd.color.blue = 0xffff;
827 user_data->series_fwd.yvalue = 0.5;
829 user_data->series_rev.color.pixel = 0;
830 user_data->series_rev.color.red = 0x60ff;
831 user_data->series_rev.color.green = 0xc0ff;
832 user_data->series_rev.color.blue = 0xffff;
833 user_data->series_rev.yvalue = -0.5;
835 g_snprintf(title1, 80, "Forward: %s:%u to %s:%u (SSRC=0x%X)",
836 get_addr_name(&(user_data->ip_src_fwd)),
837 user_data->port_src_fwd,
838 get_addr_name(&(user_data->ip_dst_fwd)),
839 user_data->port_dst_fwd,
840 user_data->ssrc_fwd);
842 g_snprintf(title2, 80, "Reverse: %s:%u to %s:%u (SSRC=0x%X)",
843 get_addr_name(&(user_data->ip_src_rev)),
844 user_data->port_src_rev,
845 get_addr_name(&(user_data->ip_dst_rev)),
846 user_data->port_dst_rev,
847 user_data->ssrc_rev);
849 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
850 &graph_selection_callback, user_data);
851 g_signal_connect(user_data->dlg.graph_window, "destroy",
852 G_CALLBACK(on_destroy_graph), user_data);
854 #endif /*USE_CONVERSATION_GRAPH*/
856 /****************************************************************************/
857 static void dialog_graph_set_title(user_data_t* user_data)
860 if (!user_data->dlg.dialog_graph.window){
863 title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
864 get_addr_name(&(user_data->ip_src_fwd)),
865 user_data->port_src_fwd,
866 get_addr_name(&(user_data->ip_dst_fwd)),
867 user_data->port_dst_fwd,
868 get_addr_name(&(user_data->ip_src_rev)),
869 user_data->port_src_rev,
870 get_addr_name(&(user_data->ip_dst_rev)),
871 user_data->port_dst_rev);
873 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
879 /****************************************************************************/
880 static void dialog_graph_reset(user_data_t* user_data)
884 user_data->dlg.dialog_graph.needs_redraw=TRUE;
885 for(i=0;i<MAX_GRAPHS;i++){
886 for(j=0;j<NUM_GRAPH_ITEMS;j++){
887 dialog_graph_graph_item_t *dggi;
888 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
893 user_data->dlg.dialog_graph.last_interval=0xffffffff;
894 user_data->dlg.dialog_graph.max_interval=0;
895 user_data->dlg.dialog_graph.num_items=0;
897 /* create the color titles near the filter buttons */
898 for(i=0;i<MAX_GRAPHS;i++){
901 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=0x%X)",
903 get_addr_name(&(user_data->ip_src_fwd)),
904 user_data->port_src_fwd,
905 get_addr_name(&(user_data->ip_dst_fwd)),
906 user_data->port_dst_fwd,
907 user_data->ssrc_fwd);
910 g_snprintf(user_data->dlg.dialog_graph.graph[i].title, 100, "%s: %s:%u to %s:%u (SSRC=0x%X)",
912 get_addr_name(&(user_data->ip_src_rev)),
913 user_data->port_src_rev,
914 get_addr_name(&(user_data->ip_dst_rev)),
915 user_data->port_dst_rev,
916 user_data->ssrc_rev);
920 dialog_graph_set_title(user_data);
923 /****************************************************************************/
924 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
926 dialog_graph_graph_item_t *it;
933 /****************************************************************************/
934 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
937 g_snprintf(buf, buf_len, "%ds",t/1000000);
938 } else if(t>=1000000){
939 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
941 g_snprintf(buf, buf_len, "%dms",t/1000);
943 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
945 g_snprintf(buf, buf_len, "%dus",t);
949 /****************************************************************************/
950 static void dialog_graph_draw(user_data_t* user_data)
953 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
954 gint32 current_interval;
955 guint32 left_x_border;
956 guint32 right_x_border;
957 guint32 top_y_border;
958 guint32 bottom_y_border;
960 int label_width, label_height;
961 guint32 draw_width, draw_height;
962 char label_string[15];
965 guint32 num_time_intervals;
966 guint32 max_value; /* max value of seen data */
967 guint32 max_y; /* max value of the Y scale */
969 if(!user_data->dlg.dialog_graph.needs_redraw){
972 user_data->dlg.dialog_graph.needs_redraw=FALSE;
975 * Find the length of the intervals we have data for
976 * so we know how large arrays we need to malloc()
978 num_time_intervals=user_data->dlg.dialog_graph.num_items;
979 /* if there isnt anything to do, just return */
980 if(num_time_intervals==0){
983 num_time_intervals+=1;
984 /* XXX move this check to _packet() */
985 if(num_time_intervals>NUM_GRAPH_ITEMS){
986 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
991 * find the max value so we can autoscale the y axis
994 for(i=0;i<MAX_GRAPHS;i++){
997 if(!user_data->dlg.dialog_graph.graph[i].display){
1000 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
1003 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1005 /* keep track of the max value we have encountered */
1013 * Clear out old plot
1015 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1016 user_data->dlg.dialog_graph.draw_area->style->white_gc,
1019 user_data->dlg.dialog_graph.draw_area->allocation.width,
1020 user_data->dlg.dialog_graph.draw_area->allocation.height);
1024 * Calculate the y scale we should use
1026 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1027 max_y=yscale_max[MAX_YSCALE-1];
1028 for(i=MAX_YSCALE-1;i>0;i--){
1029 if(max_value<yscale_max[i]){
1030 max_y=yscale_max[i];
1034 /* the user had specified an explicit y scale to use */
1035 max_y=user_data->dlg.dialog_graph.max_y_units;
1039 * Calculate size of borders surrounding the plot
1040 * The border on the right side needs to be adjusted depending
1041 * on the width of the text labels. For simplicity we assume that the
1042 * top y scale label will be the widest one
1044 print_time_scale_string(label_string, 15, max_y);
1045 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1046 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1048 right_x_border=label_width+20;
1050 bottom_y_border=label_height+20;
1054 * Calculate the size of the drawing area for the actual plot
1056 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1057 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1061 * Draw the y axis and labels
1062 * (we always draw the y scale with 11 ticks along the axis)
1064 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1065 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1067 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1068 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1074 /* first, middle and last tick are slightly longer */
1078 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1079 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1080 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1081 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1082 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1083 /* draw the labels */
1085 print_time_scale_string(label_string, 15, (max_y*i/10));
1086 pango_layout_set_text(layout, label_string, -1);
1087 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1088 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1089 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1090 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1091 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1095 print_time_scale_string(label_string, 15, (max_y*i/10));
1096 pango_layout_set_text(layout, label_string, -1);
1097 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1098 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1099 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1100 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1101 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1105 print_time_scale_string(label_string, 15, (max_y*i/10));
1106 pango_layout_set_text(layout, label_string, -1);
1107 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1108 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1109 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1110 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1111 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1119 * if we have not specified the last_interval via the gui,
1120 * then just pick the current end of the capture so that is scrolls
1121 * nicely when doing live captures
1123 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1124 last_interval=user_data->dlg.dialog_graph.max_interval;
1126 last_interval=user_data->dlg.dialog_graph.last_interval;
1133 /* plot the x-scale */
1134 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);
1136 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1137 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1138 first_interval*=user_data->dlg.dialog_graph.interval;
1145 while(interval_delta<((last_interval-first_interval)/10)){
1146 interval_delta*=delta_multiplier;
1147 if(delta_multiplier==5){
1154 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1157 /* if pixels_per_tick is <5, only draw every 10 ticks */
1158 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1162 if(current_interval%interval_delta){
1168 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1169 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1170 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1171 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1172 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1173 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1177 if(user_data->dlg.dialog_graph.interval>=1000){
1178 g_snprintf(label_string, 15, "%ds", current_interval/1000);
1179 } else if(user_data->dlg.dialog_graph.interval>=100){
1180 g_snprintf(label_string, 15, "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1181 } else if(user_data->dlg.dialog_graph.interval>=10){
1182 g_snprintf(label_string, 15, "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1184 g_snprintf(label_string, 15, "%d.%3ds", current_interval/1000,current_interval%1000);
1186 pango_layout_set_text(layout, label_string, -1);
1187 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1188 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1189 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1190 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1191 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1203 * Draw "x" for Sequence Errors and "m" for Marks
1205 /* Draw the labels Fwd and Rev */
1206 g_strlcpy(label_string,"<-Fwd",15);
1207 pango_layout_set_text(layout, label_string, -1);
1208 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1209 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1210 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1211 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1212 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1214 g_strlcpy(label_string,"<-Rev",15);
1215 pango_layout_set_text(layout, label_string, -1);
1216 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1217 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1218 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1219 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1220 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1223 /* Draw the marks */
1224 for(i=MAX_GRAPHS-1;i>=0;i--){
1226 guint32 x_pos, prev_x_pos;
1228 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1229 if (!user_data->dlg.dialog_graph.graph[i].display){
1232 /* initialize prev x/y to the low left corner of the graph */
1233 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;
1235 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1236 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;
1238 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1240 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1241 g_strlcpy(label_string,"x",15);
1243 g_strlcpy(label_string,"m",15);
1246 pango_layout_set_text(layout, label_string, -1);
1247 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1248 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1249 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1251 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1259 g_object_unref(G_OBJECT(layout));
1262 * Loop over all graphs and draw them
1264 for(i=MAX_GRAPHS-1;i>=0;i--){
1266 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1267 if (!user_data->dlg.dialog_graph.graph[i].display){
1270 /* initialize prev x/y to the low left corner of the graph */
1271 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;
1272 prev_y_pos=draw_height-1+top_y_border;
1274 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1276 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;
1277 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1281 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1284 /* dont need to draw anything if the segment
1285 * is entirely above the top of the graph
1287 if( (prev_y_pos==0) && (y_pos==0) ){
1294 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1295 x_pos, draw_height-1+top_y_border,
1305 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1306 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1307 user_data->dlg.dialog_graph.pixmap,
1310 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1313 /* update the scrollbar */
1314 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1315 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1316 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1317 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1318 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1320 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1322 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1323 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1324 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1328 /****************************************************************************/
1329 static void dialog_graph_redraw(user_data_t* user_data)
1331 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1332 dialog_graph_draw(user_data);
1335 /****************************************************************************/
1336 static gint quit(GtkWidget *widget, GdkEventExpose *event _U_)
1338 user_data_t *user_data;
1340 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1342 user_data->dlg.dialog_graph.window = NULL;
1346 /****************************************************************************/
1347 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1349 user_data_t *user_data;
1351 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1357 gdk_draw_pixmap(widget->window,
1358 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1359 user_data->dlg.dialog_graph.pixmap,
1360 event->area.x, event->area.y,
1361 event->area.x, event->area.y,
1362 event->area.width, event->area.height);
1367 /****************************************************************************/
1368 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1370 user_data_t *user_data;
1373 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1379 if(user_data->dlg.dialog_graph.pixmap){
1380 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1381 user_data->dlg.dialog_graph.pixmap=NULL;
1384 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1385 widget->allocation.width,
1386 widget->allocation.height,
1388 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1389 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1391 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1392 widget->style->white_gc,
1395 widget->allocation.width,
1396 widget->allocation.height);
1398 /* set up the colors and the GC structs for this pixmap */
1399 for(i=0;i<MAX_GRAPHS;i++){
1400 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1401 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1404 dialog_graph_redraw(user_data);
1408 /****************************************************************************/
1409 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1411 user_data_t *user_data=(user_data_t *)data;
1414 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1415 if(user_data->dlg.dialog_graph.last_interval==mi){
1418 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1419 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1423 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1425 dialog_graph_redraw(user_data);
1429 /****************************************************************************/
1430 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1432 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1433 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1434 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.draw_area), "user_data_t", user_data);
1436 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);
1438 /* signals needed to handle backing pixmap */
1439 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1440 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1442 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1443 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1445 /* create the associated scrollbar */
1446 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1447 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1448 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1449 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1450 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1453 /****************************************************************************/
1454 static void disable_graph(dialog_graph_graph_t *dgg)
1458 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1463 /****************************************************************************/
1464 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1466 /* this graph is not active, just update display and redraw */
1467 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1469 dialog_graph_redraw(dgg->ud);
1474 cf_retap_packets(&cfile, FALSE);
1475 dialog_graph_redraw(dgg->ud);
1480 /****************************************************************************/
1481 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1487 hbox=gtk_hbox_new(FALSE, 3);
1488 gtk_container_add(GTK_CONTAINER(box), hbox);
1489 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1490 gtk_widget_show(hbox);
1492 g_snprintf(str, 256, "Graph %d", num);
1493 dgg->display_button=gtk_toggle_button_new_with_label(str);
1494 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1495 gtk_widget_show(dgg->display_button);
1496 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1497 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1499 label=gtk_label_new(dgg->title);
1500 gtk_widget_show(label);
1501 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1503 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1504 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1505 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1506 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1507 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1512 /****************************************************************************/
1513 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1520 frame=gtk_frame_new("Graphs");
1521 gtk_container_add(GTK_CONTAINER(box), frame);
1522 gtk_widget_show(frame);
1524 vbox=gtk_vbox_new(FALSE, 1);
1525 gtk_container_add(GTK_CONTAINER(frame), vbox);
1526 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1527 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1528 gtk_widget_show(vbox);
1530 for(i=0;i<MAX_GRAPHS;i++){
1531 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1534 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1535 gtk_widget_show(label);
1536 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1541 /****************************************************************************/
1542 static void yscale_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), "yscale_max");
1550 user_data->dlg.dialog_graph.max_y_units=val;
1551 dialog_graph_redraw(user_data);
1554 /****************************************************************************/
1555 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1558 user_data_t *user_data;
1560 user_data=(user_data_t *)key;
1561 val=(long)g_object_get_data(G_OBJECT(item), "pixels_per_tick");
1562 user_data->dlg.dialog_graph.pixels_per_tick=val;
1563 dialog_graph_redraw(user_data);
1566 /****************************************************************************/
1567 static void tick_interval_select(GtkWidget *item, gpointer key)
1570 user_data_t *user_data;
1572 user_data=(user_data_t *)key;
1573 val=(long)g_object_get_data(G_OBJECT(item), "tick_interval");
1575 user_data->dlg.dialog_graph.interval=val;
1576 cf_retap_packets(&cfile, FALSE);
1577 dialog_graph_redraw(user_data);
1580 /****************************************************************************/
1581 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1584 GtkWidget *menu_item;
1587 for(i=0;i<MAX_YSCALE;i++){
1588 if(yscale_max[i]==AUTO_MAX_YSCALE){
1589 g_strlcpy(str,"Auto",15);
1591 g_snprintf(str, 15, "%u ms", yscale_max[i]/1000);
1593 menu_item=gtk_menu_item_new_with_label(str);
1594 g_object_set_data(G_OBJECT(menu_item), "yscale_max",
1595 GUINT_TO_POINTER(yscale_max[i]));
1596 g_signal_connect(menu_item, "activate", G_CALLBACK(yscale_select), user_data);
1597 gtk_widget_show(menu_item);
1598 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1603 /****************************************************************************/
1604 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
1607 GtkWidget *menu_item;
1610 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1611 g_snprintf(str, 5, "%u", pixels_per_tick[i]);
1612 menu_item=gtk_menu_item_new_with_label(str);
1614 g_object_set_data(G_OBJECT(menu_item), "pixels_per_tick",
1615 GUINT_TO_POINTER(pixels_per_tick[i]));
1616 g_signal_connect(menu_item, "activate", G_CALLBACK(pixels_per_tick_select), user_data);
1617 gtk_widget_show(menu_item);
1618 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1620 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1625 /****************************************************************************/
1626 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
1629 GtkWidget *menu_item;
1632 for(i=0;i<MAX_TICK_VALUES;i++){
1633 if(tick_interval_values[i]>=1000){
1634 g_snprintf(str, 15, "%u sec", tick_interval_values[i]/1000);
1635 } else if(tick_interval_values[i]>=100){
1636 g_snprintf(str, 15, "0.%1u sec", (tick_interval_values[i]/100)%10);
1637 } else if(tick_interval_values[i]>=10){
1638 g_snprintf(str, 15, "0.%02u sec", (tick_interval_values[i]/10)%10);
1640 g_snprintf(str, 15, "0.%03u sec", (tick_interval_values[i])%10);
1643 menu_item=gtk_menu_item_new_with_label(str);
1644 g_object_set_data(G_OBJECT(menu_item), "tick_interval",
1645 GUINT_TO_POINTER(tick_interval_values[i]));
1646 g_signal_connect(menu_item, "activate", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1647 gtk_widget_show(menu_item);
1648 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1650 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1654 /****************************************************************************/
1655 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
1659 GtkWidget *option_menu;
1662 hbox=gtk_hbox_new(FALSE, 0);
1663 gtk_container_add(GTK_CONTAINER(box), hbox);
1664 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1665 gtk_widget_show(hbox);
1667 label=gtk_label_new(name);
1668 gtk_widget_show(label);
1669 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1671 option_menu=gtk_option_menu_new();
1672 menu=gtk_menu_new();
1673 (*func)(user_data, menu);
1674 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1675 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1676 gtk_widget_show(option_menu);
1679 /****************************************************************************/
1680 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1682 GtkWidget *frame_vbox;
1686 frame_vbox=gtk_vbox_new(FALSE, 0);
1687 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1688 gtk_widget_show(frame_vbox);
1690 frame = gtk_frame_new("X 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, "Tick interval:", create_tick_interval_menu_items);
1701 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1703 frame = gtk_frame_new("Y Axis");
1704 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1705 gtk_widget_show(frame);
1707 vbox=gtk_vbox_new(FALSE, 0);
1708 gtk_container_add(GTK_CONTAINER(frame), vbox);
1709 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1710 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1711 gtk_widget_show(vbox);
1713 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1718 /****************************************************************************/
1719 static void dialog_graph_init_window(user_data_t* user_data)
1723 GtkWidget *bt_close;
1725 /* create the main window */
1726 user_data->dlg.dialog_graph.window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
1728 vbox=gtk_vbox_new(FALSE, 0);
1729 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1730 gtk_widget_show(vbox);
1732 create_draw_area(user_data, vbox);
1734 hbox=gtk_hbox_new(FALSE, 3);
1735 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1736 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1737 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1738 gtk_widget_show(hbox);
1740 create_filter_area(user_data, hbox);
1741 create_ctrl_area(user_data, hbox);
1743 dialog_graph_set_title(user_data);
1745 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1746 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1747 gtk_widget_show(hbox);
1749 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1750 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1752 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1754 gtk_widget_show(user_data->dlg.dialog_graph.window);
1755 window_present(user_data->dlg.dialog_graph.window);
1760 /****************************************************************************/
1761 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1763 if (user_data->dlg.dialog_graph.window != NULL) {
1764 /* There's already a graph window; reactivate it. */
1765 reactivate_window(user_data->dlg.dialog_graph.window);
1769 dialog_graph_init_window(user_data);
1773 /****************************************************************************/
1775 static void on_goto_bt_clicked_lst(GtkWidget *bt _U_, user_data_t *user_data _U_)
1778 GtkTreeModel *model;
1779 GtkTreeSelection *selection;
1782 selection = user_data->dlg.selected_list_sel;
1784 if (selection==NULL)
1787 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1788 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1789 cf_goto_frame(&cfile, fnumber);
1794 static void draw_stat(user_data_t *user_data);
1796 /****************************************************************************/
1797 /* re-dissects all packets */
1798 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1800 GString *error_string;
1802 /* remove tap listener */
1803 protect_thread_critical_region();
1804 remove_tap_listener(user_data);
1805 unprotect_thread_critical_region();
1807 /* register tap listener */
1808 error_string = register_tap_listener("rtp", user_data, NULL,
1809 rtp_reset, rtp_packet, rtp_draw);
1810 if (error_string != NULL) {
1811 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1812 g_string_free(error_string, TRUE);
1816 /* retap all packets */
1817 cf_retap_packets(&cfile, FALSE);
1819 /* draw statistics info */
1820 draw_stat(user_data);
1824 static void on_next_bt_clicked_list(GtkWidget *bt _U_, user_data_t *user_data _U_)
1827 GtkTreeModel *model;
1829 GtkTreeSelection *selection;
1832 selection = user_data->dlg.selected_list_sel;
1834 if (selection==NULL)
1838 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1839 while (gtk_tree_model_iter_next (model,&iter)) {
1840 gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1841 if (strcmp(text, OK_TEXT) != 0) {
1842 gtk_tree_selection_select_iter (selection, &iter);
1843 path = gtk_tree_model_get_path(model, &iter);
1844 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1847 gtk_tree_path_free(path);
1854 if (user_data->dlg.number_of_nok>1){
1855 /* Get the first iter and select it before starting over */
1856 gtk_tree_model_get_iter_first(model, &iter);
1857 gtk_tree_selection_select_iter (selection, &iter);
1864 /****************************************************************************/
1865 /* when we want to save the information */
1866 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
1869 GtkWidget *rev, *forw, *both;
1870 user_data_t *user_data;
1872 GtkListStore *store;
1874 GtkTreeSelection *selection;
1875 GtkTreeModel *model;
1877 /* To Hold data from the list row */
1878 guint packet; /* Packet */
1879 guint sequence; /* Sequence */
1880 gfloat delta; /* Delta(ms) */
1881 gfloat jitter; /* Jitter(ms) */
1882 gfloat ipbw; /* IP BW(kbps) */
1883 gboolean marker; /* Marker */
1884 char * status_str; /* Status */
1885 char * date_str; /* Date */
1886 guint length; /* Length */
1892 g_dest = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1894 /* Perhaps the user specified a directory instead of a file.
1895 * Check whether they did.
1897 if (test_for_directory(g_dest) == EISDIR) {
1898 /* It's a directory - set the file selection box to display it. */
1899 set_last_open_dir(g_dest);
1901 file_selection_set_current_folder(fs, get_last_open_dir());
1905 rev = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "reversed_rb");
1906 forw = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "forward_rb");
1907 both = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "both_rb");
1908 user_data = (user_data_t*)g_object_get_data(G_OBJECT(bt), "user_data");
1910 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
1911 fp = ws_fopen(g_dest, "w");
1913 open_failure_alert_box(g_dest, errno, TRUE);
1917 if (GTK_TOGGLE_BUTTON(both)->active) {
1918 fprintf(fp, "Forward\n");
1920 write_failure_alert_box(g_dest, errno);
1926 for(j = 0; j < NUM_COLS; j++) {
1928 fprintf(fp,"%s",titles[j]);
1930 fprintf(fp,",%s",titles[j]);
1935 write_failure_alert_box(g_dest, errno);
1939 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
1940 store = GTK_LIST_STORE(model);
1941 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
1943 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(user_data->dlg.list_fwd));
1945 while (gtk_tree_model_iter_next (model,&iter)) {
1946 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1957 fprintf(fp, "%u",packet);
1958 fprintf(fp, ",%u", sequence);
1959 fprintf(fp, ",%.2f", delta);
1960 fprintf(fp, ",%.2f", jitter);
1961 fprintf(fp, ",%.2f", ipbw);
1962 fprintf(fp, ",%s", marker? "SET" : "");
1963 fprintf(fp, ",%s", status_str);
1964 fprintf(fp, ",%s", date_str);
1965 fprintf(fp, ",%u", length);
1969 write_failure_alert_box(g_dest, errno);
1975 if (fclose(fp) == EOF) {
1976 write_failure_alert_box(g_dest, errno);
1981 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
1983 if (GTK_TOGGLE_BUTTON(both)->active) {
1984 fp = ws_fopen(g_dest, "a");
1986 open_failure_alert_box(g_dest, errno, TRUE);
1989 fprintf(fp, "\nReverse\n");
1991 write_failure_alert_box(g_dest, errno);
1996 fp = ws_fopen(g_dest, "w");
1998 open_failure_alert_box(g_dest, errno, TRUE);
2002 for(j = 0; j < NUM_COLS; j++) {
2004 fprintf(fp,"%s",titles[j]);
2006 fprintf(fp,",%s",titles[j]);
2011 write_failure_alert_box(g_dest, errno);
2015 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
2016 store = GTK_LIST_STORE(model);
2017 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2019 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(user_data->dlg.list_rev));
2021 while (gtk_tree_model_iter_next (model,&iter)) {
2022 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2033 fprintf(fp, "%u",packet);
2034 fprintf(fp, ",%u", sequence);
2035 fprintf(fp, ",%.2f", delta);
2036 fprintf(fp, ",%.2f", jitter);
2037 fprintf(fp, ",%.2f", ipbw);
2038 fprintf(fp, ",%s", marker? "SET" : "");
2039 fprintf(fp, ",%s", status_str);
2040 fprintf(fp, ",%s", date_str);
2041 fprintf(fp, ",%u", length);
2045 write_failure_alert_box(g_dest, errno);
2050 if (fclose(fp) == EOF) {
2051 write_failure_alert_box(g_dest, errno);
2056 window_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
2059 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2061 user_data->dlg.save_csv_as_w = NULL;
2064 /* when the user wants to save the csv information in a file */
2065 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
2069 GtkWidget *label_format;
2070 GtkWidget *channels_label;
2071 GSList *channels_group = NULL;
2072 GtkWidget *forward_rb;
2073 GtkWidget *reversed_rb;
2076 if (user_data->dlg.save_csv_as_w != NULL) {
2077 /* There's already a Save CSV info dialog box; reactivate it. */
2078 reactivate_window(user_data->dlg.save_csv_as_w);
2082 user_data->dlg.save_csv_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV", GTK_WINDOW(user_data->dlg.notebook), GTK_FILE_CHOOSER_ACTION_SAVE,
2083 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2084 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2087 /* Container for each row of widgets */
2088 vertb = gtk_vbox_new(FALSE, 0);
2089 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2090 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2091 gtk_widget_show (vertb);
2093 table1 = gtk_table_new (2, 4, FALSE);
2094 gtk_widget_show (table1);
2095 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2096 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2097 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2099 label_format = gtk_label_new ("Format: Comma Separated Values");
2100 gtk_widget_show (label_format);
2101 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2102 (GtkAttachOptions) (GTK_FILL),
2103 (GtkAttachOptions) (0), 0, 0);
2106 channels_label = gtk_label_new ("Channels:");
2107 gtk_widget_show (channels_label);
2108 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2109 (GtkAttachOptions) (GTK_FILL),
2110 (GtkAttachOptions) (0), 0, 0);
2111 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2113 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2114 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2115 gtk_widget_show (forward_rb);
2116 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2117 (GtkAttachOptions) (GTK_FILL),
2118 (GtkAttachOptions) (0), 0, 0);
2120 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2121 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2122 gtk_widget_show (reversed_rb);
2123 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2124 (GtkAttachOptions) (GTK_FILL),
2125 (GtkAttachOptions) (0), 0, 0);
2127 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2128 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2129 gtk_widget_show (both_rb);
2130 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2131 (GtkAttachOptions) (GTK_FILL),
2132 (GtkAttachOptions) (0), 0, 0);
2134 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2136 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2137 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2138 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2139 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2141 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2142 G_CALLBACK(window_delete_event_cb), NULL);
2143 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2144 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2146 gtk_widget_show(user_data->dlg.save_csv_as_w);
2147 window_present(user_data->dlg.save_csv_as_w);
2149 if (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT){
2150 save_csv_as_ok_cb(user_data->dlg.save_csv_as_w, user_data->dlg.save_csv_as_w);
2152 window_destroy(user_data->dlg.save_csv_as_w);
2158 /****************************************************************************/
2159 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2161 /* Note that we no longer have a Save voice info dialog box. */
2162 user_data->dlg.save_voice_as_w = NULL;
2165 /****************************************************************************/
2166 /* here we save it into a file that user specified */
2167 /* XXX what about endians here? could go something wrong? */
2168 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2170 FILE *to_stream, *forw_stream, *rev_stream;
2171 size_t fwritten, rwritten;
2172 int f_rawvalue, r_rawvalue, rawvalue;
2175 guint32 f_write_silence = 0;
2176 guint32 r_write_silence = 0;
2178 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2179 gboolean stop_flag = FALSE;
2182 forw_stream = ws_fopen(user_data->f_tempname, "rb");
2183 if (forw_stream == NULL)
2185 rev_stream = ws_fopen(user_data->r_tempname, "rb");
2186 if (rev_stream == NULL) {
2187 fclose(forw_stream);
2191 /* open file for saving */
2192 to_stream = ws_fopen(dest, "wb");
2193 if (to_stream == NULL) {
2194 fclose(forw_stream);
2199 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2201 if (format == SAVE_AU_FORMAT) /* au format */
2203 /* First we write the .au header. XXX Hope this is endian independant */
2204 /* the magic word 0x2e736e64 == .snd */
2205 phtonl(pd, 0x2e736e64);
2206 nchars=fwrite(pd, 1, 4, to_stream);
2207 /* header offset == 24 bytes */
2209 nchars=fwrite(pd, 1, 4, to_stream);
2210 /* total length; it is permitted to set this to 0xffffffff */
2212 nchars=fwrite(pd, 1, 4, to_stream);
2213 /* encoding format == 16-bit linear PCM */
2215 nchars=fwrite(pd, 1, 4, to_stream);
2216 /* sample rate == 8000 Hz */
2218 nchars=fwrite(pd, 1, 4, to_stream);
2221 nchars=fwrite(pd, 1, 4, to_stream);
2225 /* only forward direction */
2226 case SAVE_FORWARD_DIRECTION_MASK: {
2227 progbar_count = user_data->forward.saveinfo.count;
2228 progbar_quantum = user_data->forward.saveinfo.count/100;
2229 while ((f_rawvalue = getc(forw_stream)) != EOF) {
2232 if((count > progbar_nextstep) && (count <= progbar_count)) {
2233 update_progress_dlg(progbar,
2234 (gfloat) count/progbar_count, "Saving");
2235 progbar_nextstep = progbar_nextstep + progbar_quantum;
2239 if (user_data->forward.statinfo.pt == PT_PCMU){
2240 sample = ulaw2linear((unsigned char)f_rawvalue);
2243 else if(user_data->forward.statinfo.pt == PT_PCMA){
2244 sample = alaw2linear((unsigned char)f_rawvalue);
2248 fclose(forw_stream);
2251 destroy_progress_dlg(progbar);
2255 fwritten = fwrite(pd, 1, 2, to_stream);
2257 fclose(forw_stream);
2260 destroy_progress_dlg(progbar);
2266 /* only reversed direction */
2267 case SAVE_REVERSE_DIRECTION_MASK: {
2268 progbar_count = user_data->reversed.saveinfo.count;
2269 progbar_quantum = user_data->reversed.saveinfo.count/100;
2270 while ((r_rawvalue = getc(rev_stream)) != EOF) {
2273 if((count > progbar_nextstep) && (count <= progbar_count)) {
2274 update_progress_dlg(progbar,
2275 (gfloat) count/progbar_count, "Saving");
2276 progbar_nextstep = progbar_nextstep + progbar_quantum;
2280 if (user_data->reversed.statinfo.pt == PT_PCMU){
2281 sample = ulaw2linear((unsigned char)r_rawvalue);
2284 else if(user_data->reversed.statinfo.pt == PT_PCMA){
2285 sample = alaw2linear((unsigned char)r_rawvalue);
2289 fclose(forw_stream);
2292 destroy_progress_dlg(progbar);
2296 rwritten = fwrite(pd, 1, 2, to_stream);
2298 fclose(forw_stream);
2301 destroy_progress_dlg(progbar);
2307 /* both directions */
2308 case SAVE_BOTH_DIRECTION_MASK: {
2309 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2310 (progbar_count = user_data->forward.saveinfo.count) :
2311 (progbar_count = user_data->reversed.saveinfo.count);
2312 progbar_quantum = progbar_count/100;
2313 /* since conversation in one way can start later than in the other one,
2314 * we have to write some silence information for one channel */
2315 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2316 f_write_silence = (guint32)
2317 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2319 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2320 r_write_silence = (guint32)
2321 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2326 if((count > progbar_nextstep) && (count <= progbar_count)) {
2327 update_progress_dlg(progbar,
2328 (gfloat) count/progbar_count, "Saving");
2329 progbar_nextstep = progbar_nextstep + progbar_quantum;
2332 if(f_write_silence > 0) {
2333 r_rawvalue = getc(rev_stream);
2334 switch (user_data->forward.statinfo.reg_pt) {
2336 f_rawvalue = SILENCE_PCMU;
2339 f_rawvalue = SILENCE_PCMA;
2347 else if(r_write_silence > 0) {
2348 f_rawvalue = getc(forw_stream);
2349 switch (user_data->reversed.statinfo.reg_pt) {
2351 r_rawvalue = SILENCE_PCMU;
2354 r_rawvalue = SILENCE_PCMA;
2363 f_rawvalue = getc(forw_stream);
2364 r_rawvalue = getc(rev_stream);
2366 if ((r_rawvalue == EOF) && (f_rawvalue == EOF))
2368 if ((user_data->forward.statinfo.pt == PT_PCMU) && (user_data->reversed.statinfo.pt == PT_PCMU)){
2369 sample = (ulaw2linear((unsigned char)r_rawvalue) + ulaw2linear((unsigned char)f_rawvalue)) / 2;
2372 else if((user_data->forward.statinfo.pt == PT_PCMA) && (user_data->reversed.statinfo.pt == PT_PCMA)){
2373 sample = (alaw2linear((unsigned char)r_rawvalue) + alaw2linear((unsigned char)f_rawvalue)) / 2;
2378 fclose(forw_stream);
2381 destroy_progress_dlg(progbar);
2386 rwritten = fwrite(pd, 1, 2, to_stream);
2388 fclose(forw_stream);
2391 destroy_progress_dlg(progbar);
2398 else if (format == SAVE_RAW_FORMAT) /* raw format */
2402 /* only forward direction */
2403 case SAVE_FORWARD_DIRECTION_MASK: {
2404 progbar_count = user_data->forward.saveinfo.count;
2405 progbar_quantum = user_data->forward.saveinfo.count/100;
2406 stream = forw_stream;
2409 /* only reversed direction */
2410 case SAVE_REVERSE_DIRECTION_MASK: {
2411 progbar_count = user_data->reversed.saveinfo.count;
2412 progbar_quantum = user_data->reversed.saveinfo.count/100;
2413 stream = rev_stream;
2417 fclose(forw_stream);
2420 destroy_progress_dlg(progbar);
2427 /* XXX how do you just copy the file? */
2428 while ((rawvalue = getc(stream)) != EOF) {
2431 if((count > progbar_nextstep) && (count <= progbar_count)) {
2432 update_progress_dlg(progbar,
2433 (gfloat) count/progbar_count, "Saving");
2434 progbar_nextstep = progbar_nextstep + progbar_quantum;
2438 if (putc(rawvalue, to_stream) == EOF) {
2439 fclose(forw_stream);
2442 destroy_progress_dlg(progbar);
2448 destroy_progress_dlg(progbar);
2449 fclose(forw_stream);
2456 /****************************************************************************/
2457 /* the user wants to save in a file */
2458 /* XXX support for different formats is currently commented out */
2459 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
2462 /*GtkWidget *wav, *sw;*/
2463 GtkWidget *au, *raw;
2464 GtkWidget *rev, *forw, *both;
2465 user_data_t *user_data;
2466 gint channels , format;
2468 g_dest = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
2470 /* Perhaps the user specified a directory instead of a file.
2471 Check whether they did. */
2472 if (test_for_directory(g_dest) == EISDIR) {
2473 /* It's a directory - set the file selection box to display it. */
2474 set_last_open_dir(g_dest);
2476 file_selection_set_current_folder(fs, get_last_open_dir());
2480 /*wav = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "wav_rb");
2481 sw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "sw_rb");*/
2482 au = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "au_rb");
2483 raw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "raw_rb");
2484 rev = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "reversed_rb");
2485 forw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "forward_rb");
2486 both = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "both_rb");
2487 user_data = (user_data_t *)g_object_get_data(G_OBJECT(ok_bt), "user_data");
2489 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2490 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2491 * disable the ok button or disable the buttons for direction if only one is not ok. The
2492 * problem is if we open the save voice dialog and then click the refresh button and maybe
2493 * the state changes, so we can't save anymore. In this case we should be able to update
2494 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2497 /* we can not save in both directions */
2498 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2499 /* there are many combinations here, we just exit when first matches */
2500 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2501 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2502 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2503 "Can't save in a file: Unsupported codec!");
2504 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2505 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2506 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2507 "Can't save in a file: Wrong length of captured packets!");
2508 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2509 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2510 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2511 "Can't save in a file: RTP data with padding!");
2512 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2513 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2514 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2515 "Can't save in a file: Not all data in all packets was captured!");
2517 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2518 "Can't save in a file: File I/O problem!");
2521 /* we can not save forward direction */
2522 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2523 (GTK_TOGGLE_BUTTON (both)->active))) {
2524 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2525 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2526 "Can't save forward direction in a file: Unsupported codec!");
2527 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2528 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2529 "Can't save forward direction in a file: Wrong length of captured packets!");
2530 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2531 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2532 "Can't save forward direction in a file: RTP data with padding!");
2533 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2534 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2535 "Can't save forward direction in a file: Not all data in all packets was captured!");
2537 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2538 "Can't save forward direction in a file: File I/O problem!");
2541 /* we can not save reversed direction */
2542 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2543 (GTK_TOGGLE_BUTTON (both)->active))) {
2544 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2545 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2546 "Can't save reversed direction in a file: Unsupported codec!");
2547 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2548 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2549 "Can't save reversed direction in a file: Wrong length of captured packets!");
2550 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2551 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2552 "Can't save reversed direction in a file: RTP data with padding!");
2553 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2554 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2555 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2556 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2557 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2558 "Can't save reversed direction in a file: No RTP data!");
2560 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2561 "Can't save reversed direction in a file: File I/O problem!");
2565 /*if (GTK_TOGGLE_BUTTON (wav)->active)
2566 format = SAVE_WAV_FORMAT;
2567 else */if (GTK_TOGGLE_BUTTON (au)->active)
2568 format = SAVE_AU_FORMAT;
2569 /*else if (GTK_TOGGLE_BUTTON (sw)->active)
2570 format = SAVE_SW_FORMAT;*/
2571 else if (GTK_TOGGLE_BUTTON (raw)->active)
2572 format = SAVE_RAW_FORMAT;
2574 format = SAVE_NONE_FORMAT;
2576 if (GTK_TOGGLE_BUTTON (rev)->active)
2577 channels = SAVE_REVERSE_DIRECTION_MASK;
2578 else if (GTK_TOGGLE_BUTTON (both)->active)
2579 channels = SAVE_BOTH_DIRECTION_MASK;
2581 channels = SAVE_FORWARD_DIRECTION_MASK;
2583 /* direction/format validity*/
2584 if (format == SAVE_AU_FORMAT)
2586 /* make sure streams are alaw/ulaw */
2587 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != PT_PCMA) && (user_data->forward.statinfo.pt != PT_PCMU)){
2588 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2589 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2592 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != PT_PCMA) && (user_data->reversed.statinfo.pt != PT_PCMU)){
2593 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2594 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2597 /* make sure pt's don't differ */
2598 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2599 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2600 "Can't save in a file: Forward and reverse direction differ in type");
2604 else if (format == SAVE_RAW_FORMAT)
2606 /* can't save raw in both directions */
2607 if (channels == SAVE_BOTH_DIRECTION_MASK){
2608 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2609 "Can't save in a file: Unable to save raw data in both directions");
2615 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2616 "Can't save in a file: Invalid save format");
2620 if(!copy_file(g_dest, channels, format, user_data)) {
2621 /* XXX - report the error type! */
2622 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2623 "An error occurred while saving voice in a file!");
2627 window_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
2630 /****************************************************************************/
2631 /* when the user wants to save the voice information in a file */
2632 /* XXX support for different formats is currently commented out */
2633 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2637 GtkWidget *label_format;
2638 GtkWidget *channels_label;
2639 GSList *format_group = NULL;
2640 GSList *channels_group = NULL;
2641 GtkWidget *forward_rb;
2642 GtkWidget *reversed_rb;
2644 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2648 /* if we can't save in a file: wrong codec, cut packets or other errors */
2649 /* shold the error arise here or later when you click ok button ?
2650 * if we do it here, then we must disable the refresh button, so we don't do it here */
2652 if (user_data->dlg.save_voice_as_w != NULL) {
2653 /* There's already a Save voice info dialog box; reactivate it. */
2654 reactivate_window(user_data->dlg.save_voice_as_w);
2658 /* XXX - use file_selection from dlg_utils instead! */
2659 user_data->dlg.save_voice_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...", GTK_WINDOW(user_data->dlg.notebook), GTK_FILE_CHOOSER_ACTION_SAVE,
2660 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2661 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2665 /* Container for each row of widgets */
2666 vertb = gtk_vbox_new(FALSE, 0);
2667 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2668 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2669 gtk_widget_show (vertb);
2671 table1 = gtk_table_new (2, 4, FALSE);
2672 gtk_widget_show (table1);
2673 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2674 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2675 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2677 /*label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2678 gtk_widget_show (label_format);
2679 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2680 (GtkAttachOptions) (GTK_FILL),
2681 (GtkAttachOptions) (0), 0, 0);*/
2683 label_format = gtk_label_new ("Format: ");
2684 gtk_widget_show (label_format);
2685 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2686 (GtkAttachOptions) (GTK_FILL),
2687 (GtkAttachOptions) (0), 0, 0);
2689 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5);
2691 raw_rb = gtk_radio_button_new_with_label (format_group, ".raw");
2692 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (raw_rb));
2693 gtk_widget_show (raw_rb);
2694 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2695 (GtkAttachOptions) (GTK_FILL),
2696 (GtkAttachOptions) (0), 0, 0);
2699 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2700 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2701 gtk_widget_show (au_rb);
2702 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2703 (GtkAttachOptions) (GTK_FILL),
2704 (GtkAttachOptions) (0), 0, 0);
2706 /* we support .au - ulaw*/
2707 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2708 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (wav_rb));
2709 gtk_widget_show (wav_rb);
2710 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2711 (GtkAttachOptions) (GTK_FILL),
2712 (GtkAttachOptions) (0), 0, 0);
2714 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2715 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (sw_rb));
2716 gtk_widget_show (sw_rb);
2717 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2718 (GtkAttachOptions) (GTK_FILL),
2719 (GtkAttachOptions) (0), 0, 0);
2720 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2721 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2722 gtk_widget_show (au_rb);
2723 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2724 (GtkAttachOptions) (GTK_FILL),
2725 (GtkAttachOptions) (0), 0, 0);
2729 channels_label = gtk_label_new ("Channels:");
2730 gtk_widget_show (channels_label);
2731 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2732 (GtkAttachOptions) (GTK_FILL),
2733 (GtkAttachOptions) (0), 0, 0);
2734 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5);
2736 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2737 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2738 gtk_widget_show (forward_rb);
2739 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2740 (GtkAttachOptions) (GTK_FILL),
2741 (GtkAttachOptions) (0), 0, 0);
2743 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2744 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2745 gtk_widget_show (reversed_rb);
2746 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2747 (GtkAttachOptions) (GTK_FILL),
2748 (GtkAttachOptions) (0), 0, 0);
2750 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2751 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2752 gtk_widget_show (both_rb);
2753 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2754 (GtkAttachOptions) (GTK_FILL),
2755 (GtkAttachOptions) (0), 0, 0);
2757 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2759 /* if one direction is nok we don't allow saving
2760 XXX this is not ok since the user can click the refresh button and cause changes
2761 but we can not update this window. So we move all the decision on the time the ok
2763 if (user_data->forward.saved == FALSE) {
2764 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2765 gtk_widget_set_sensitive(forward_rb, FALSE);
2766 gtk_widget_set_sensitive(both_rb, FALSE);
2768 else if (user_data->reversed.saved == FALSE) {
2769 gtk_widget_set_sensitive(reversed_rb, FALSE);
2770 gtk_widget_set_sensitive(both_rb, FALSE);
2774 /*g_object_set_data(G_OBJECT(ok_bt), "wav_rb", wav_rb);*/
2775 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2776 /*g_object_set_data(G_OBJECT(ok_bt), "sw_rb", sw_rb);*/
2777 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2778 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2779 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2780 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2781 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2783 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2784 G_CALLBACK(window_delete_event_cb), NULL);
2785 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2786 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2788 gtk_widget_show(user_data->dlg.save_voice_as_w);
2789 window_present(user_data->dlg.save_voice_as_w);
2791 if (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT){
2792 save_voice_as_ok_cb(user_data->dlg.save_voice_as_w, user_data->dlg.save_voice_as_w);
2794 window_destroy(user_data->dlg.save_voice_as_w);
2800 /****************************************************************************/
2801 /* when we are finished with redisection, we add the label for the statistic */
2802 static void draw_stat(user_data_t *user_data)
2804 gchar label_max[200];
2805 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
2806 - user_data->forward.statinfo.start_seq_nr + 1;
2807 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
2808 - user_data->reversed.statinfo.start_seq_nr + 1;
2809 gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
2810 gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
2811 double f_perc, r_perc;
2813 f_perc = (double)(f_lost*100)/(double)f_expected;
2818 r_perc = (double)(r_lost*100)/(double)r_expected;
2823 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
2824 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2825 " Sequence errors = %u",
2826 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
2827 user_data->forward.statinfo.total_nr,
2828 f_expected, f_lost, f_perc, user_data->forward.statinfo.sequence);
2830 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2832 g_snprintf(label_max, 199, "Max delta = %f sec at packet no. %u \n"
2833 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2834 " Sequence errors = %u",
2835 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
2836 user_data->reversed.statinfo.total_nr,
2837 r_expected, r_lost, r_perc, user_data->reversed.statinfo.sequence);
2839 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2846 /****************************************************************************/
2847 /* append a line to list */
2848 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num,
2849 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
2850 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
2852 GtkListStore *list_store;
2854 if (strcmp(status, OK_TEXT) != 0) {
2855 user_data->dlg.number_of_nok++;
2858 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
2860 /* Creates a new row at position. iter will be changed to point to this new row.
2861 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
2862 * The row will be filled with the values given to this function.
2864 * should generally be preferred when inserting rows in a sorted list store.
2866 #if GTK_CHECK_VERSION(2,6,0)
2867 gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
2869 gtk_list_store_append (list_store, &user_data->dlg.iter);
2870 gtk_list_store_set (list_store, &user_data->dlg.iter,
2872 PACKET_COLUMN, number,
2873 SEQUENCE_COLUMN, seq_num,
2874 DELTA_COLUMN, delta,
2875 JITTER_COLUMN, jitter,
2876 IPBW_COLUMN, bandwidth,
2877 MARKER_COLUMN, marker,
2878 STATUS_COLUMN, (char *)status,
2879 DATE_COLUMN, (char *)timeStr,
2880 LENGTH_COLUMN, pkt_len,
2881 FOREGROUND_COLOR_COL, NULL,
2882 BACKGROUND_COLOR_COL, (char *)color_str,
2885 if(flags & STAT_FLAG_FIRST){
2886 /* Set first row as active */
2887 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
2891 /****************************************************************************
2892 * Functions needed to present values from the list
2896 /* Present boolean value */
2898 rtp_boolean_data_func (GtkTreeViewColumn *column _U_,
2899 GtkCellRenderer *renderer,
2900 GtkTreeModel *model,
2906 /* the col to get data from is in userdata */
2907 gint bool_col = GPOINTER_TO_INT(user_data);
2909 gtk_tree_model_get(model, iter, bool_col, &bool_val, -1);
2913 g_snprintf(buf, sizeof(buf), "%s", bool_val? "SET" : "");
2916 g_assert_not_reached();
2919 g_object_set(renderer, "text", buf, NULL);
2924 GtkWidget* create_list(user_data_t* user_data)
2927 GtkListStore *list_store;
2929 GtkTreeViewColumn *column;
2930 GtkCellRenderer *renderer;
2931 GtkTreeSortable *sortable;
2932 GtkTreeView *list_view;
2933 GtkTreeSelection *selection;
2935 /* Create the store */
2936 list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX*/
2937 G_TYPE_UINT, /* Packet */
2938 G_TYPE_UINT, /* Sequence */
2939 G_TYPE_FLOAT, /* Delta(ms) */
2940 G_TYPE_FLOAT, /* Jitter(ms) */
2941 G_TYPE_FLOAT, /* IP BW(kbps) */
2942 G_TYPE_BOOLEAN, /* Marker */
2943 G_TYPE_STRING, /* Status */
2944 G_TYPE_STRING, /* Date */
2945 G_TYPE_UINT, /* Length */
2946 G_TYPE_STRING, /* Foreground color */
2947 G_TYPE_STRING); /* Background color */
2950 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
2952 list_view = GTK_TREE_VIEW(list);
2953 sortable = GTK_TREE_SORTABLE(list_store);
2955 #if GTK_CHECK_VERSION(2,6,0)
2956 /* Speed up the list display */
2957 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
2960 /* Setup the sortable columns */
2961 gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
2962 gtk_tree_view_set_headers_clickable(list_view, FALSE);
2964 /* The view now holds a reference. We can get rid of our own reference */
2965 g_object_unref (G_OBJECT (list_store));
2968 * Create the first column packet, associating the "text" attribute of the
2969 * cell_renderer to the first column of the model
2971 renderer = gtk_cell_renderer_text_new ();
2972 column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
2973 "text", PACKET_COLUMN,
2974 "foreground", FOREGROUND_COLOR_COL,
2975 "background", BACKGROUND_COLOR_COL,
2977 gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
2978 gtk_tree_view_column_set_resizable(column, TRUE);
2979 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2980 gtk_tree_view_column_set_min_width(column, 100);
2982 /* Add the column to the view. */
2983 gtk_tree_view_append_column (list_view, column);
2985 /* Second column.. Sequence. */
2986 renderer = gtk_cell_renderer_text_new ();
2987 column = gtk_tree_view_column_new_with_attributes ("Sequence", renderer,
2988 "text", SEQUENCE_COLUMN,
2989 "foreground", FOREGROUND_COLOR_COL,
2990 "background", BACKGROUND_COLOR_COL,
2992 gtk_tree_view_column_set_sort_column_id(column, SEQUENCE_COLUMN);
2993 gtk_tree_view_column_set_resizable(column, TRUE);
2994 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2995 gtk_tree_view_column_set_min_width(column, 100);
2996 gtk_tree_view_append_column (list_view, column);
2998 /* Third column.. Delta(ms). */
2999 renderer = gtk_cell_renderer_text_new ();
3000 column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
3001 "text", DELTA_COLUMN,
3002 "foreground", FOREGROUND_COLOR_COL,
3003 "background", BACKGROUND_COLOR_COL,
3006 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3007 GINT_TO_POINTER(DELTA_COLUMN), NULL);
3009 gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
3010 gtk_tree_view_column_set_resizable(column, TRUE);
3011 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3012 gtk_tree_view_column_set_min_width(column, 100);
3013 gtk_tree_view_append_column (list_view, column);
3015 /* Forth column.. Jitter(ms). */
3016 renderer = gtk_cell_renderer_text_new ();
3017 column = gtk_tree_view_column_new_with_attributes ("Jitter(ms)", renderer,
3018 "text", JITTER_COLUMN,
3019 "foreground", FOREGROUND_COLOR_COL,
3020 "background", BACKGROUND_COLOR_COL,
3023 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3024 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3026 gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3027 gtk_tree_view_column_set_resizable(column, TRUE);
3028 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3029 gtk_tree_view_column_set_min_width(column, 100);
3030 gtk_tree_view_append_column (list_view, column);
3032 /* Fifth column.. IP BW(kbps). */
3033 renderer = gtk_cell_renderer_text_new ();
3034 column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
3035 "text", IPBW_COLUMN,
3036 "foreground", FOREGROUND_COLOR_COL,
3037 "background", BACKGROUND_COLOR_COL,
3040 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3041 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3043 gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3044 gtk_tree_view_column_set_resizable(column, TRUE);
3045 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3046 gtk_tree_view_column_set_min_width(column, 100);
3047 gtk_tree_view_append_column (list_view, column);
3049 /* Sixth column.. Marker. */
3050 renderer = gtk_cell_renderer_text_new ();
3051 column = gtk_tree_view_column_new_with_attributes ("Marker", renderer,
3052 "text", MARKER_COLUMN,
3053 "foreground", FOREGROUND_COLOR_COL,
3054 "background", BACKGROUND_COLOR_COL,
3057 gtk_tree_view_column_set_cell_data_func(column, renderer, rtp_boolean_data_func,
3058 GINT_TO_POINTER(MARKER_COLUMN), NULL);
3060 gtk_tree_view_column_set_sort_column_id(column, MARKER_COLUMN);
3061 gtk_tree_view_column_set_resizable(column, TRUE);
3062 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3063 gtk_tree_view_column_set_min_width(column, 75);
3064 gtk_tree_view_append_column (list_view, column);
3066 /* Seventh column.. Status. */
3067 renderer = gtk_cell_renderer_text_new ();
3068 column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3069 "text", STATUS_COLUMN,
3070 "foreground", FOREGROUND_COLOR_COL,
3071 "background", BACKGROUND_COLOR_COL,
3073 gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3074 gtk_tree_view_column_set_resizable(column, TRUE);
3075 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3076 gtk_tree_view_column_set_min_width(column, 100);
3077 gtk_tree_view_append_column (list_view, column);
3079 /* Now enable the sorting of each column */
3080 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3081 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3083 /* Setup the selection handler */
3084 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3085 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3087 g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3088 G_CALLBACK (on_list_select_row),
3093 /****************************************************************************/
3094 /* Create the dialog box with all widgets */
3095 static void create_rtp_dialog(user_data_t* user_data)
3097 GtkWidget *window = NULL;
3098 GtkWidget *list_fwd;
3099 GtkWidget *list_rev;
3100 GtkWidget *label_stats_fwd;
3101 GtkWidget *label_stats_rev;
3102 GtkWidget *notebook;
3104 GtkWidget *main_vb, *page, *page_r;
3106 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3107 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3108 #ifdef USE_CONVERSATION_GRAPH
3109 GtkWidget *graph_bt;
3111 GtkWidget *graph_bt;
3112 gchar label_forward[150];
3113 gchar label_forward_tree[150];
3114 gchar label_reverse[150];
3116 gchar str_ip_src[16];
3117 gchar str_ip_dst[16];
3119 window = window_new(GTK_WINDOW_TOPLEVEL, "Wireshark: RTP Stream Analysis");
3120 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3122 /* Container for each row of widgets */
3123 main_vb = gtk_vbox_new(FALSE, 2);
3124 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3125 gtk_container_add(GTK_CONTAINER(window), main_vb);
3126 gtk_widget_show(main_vb);
3129 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), 16);
3130 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), 16);
3132 g_snprintf(label_forward, 149,
3133 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3134 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3136 g_snprintf(label_forward_tree, 149,
3137 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3138 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3141 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), 16);
3142 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), 16);
3144 g_snprintf(label_reverse, 149,
3145 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3146 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3148 /* Start a notebook for flipping between sets of changes */
3149 notebook = gtk_notebook_new();
3150 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3151 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3153 user_data->dlg.notebook_signal_id =
3154 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3156 /* page for forward connection */
3157 page = gtk_vbox_new(FALSE, 8);
3158 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3160 /* direction label */
3161 label = gtk_label_new(label_forward);
3162 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3164 /* place for some statistics */
3165 label_stats_fwd = gtk_label_new("\n");
3166 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3168 /* scrolled window */
3169 scrolled_window = scrolled_window_new(NULL, NULL);
3172 list_fwd = create_list(user_data);
3173 gtk_widget_show(list_fwd);
3174 gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3175 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3176 gtk_widget_show(scrolled_window);
3179 label = gtk_label_new(" Forward Direction ");
3180 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3182 /* same page for reversed connection */
3183 page_r = gtk_vbox_new(FALSE, 8);
3184 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3185 label = gtk_label_new(label_reverse);
3186 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3187 label_stats_rev = gtk_label_new("\n");
3188 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3190 scrolled_window_r = scrolled_window_new(NULL, NULL);
3192 list_rev = create_list(user_data);
3193 gtk_widget_show(list_rev);
3194 gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3195 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3196 gtk_widget_show(scrolled_window_r);
3198 label = gtk_label_new(" Reversed Direction ");
3199 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3201 /* page for help&about or future
3202 page_help = gtk_hbox_new(FALSE, 5);
3203 label = gtk_label_new(" Future ");
3204 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3205 frame = gtk_frame_new("");
3206 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3207 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3208 gtk_container_add(GTK_CONTAINER(frame), text);
3209 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3210 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3213 /* show all notebooks */
3214 gtk_widget_show_all(notebook);
3217 box4 = gtk_hbutton_box_new();
3218 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3219 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3220 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3221 gtk_box_set_spacing(GTK_BOX (box4), 0);
3222 gtk_button_box_set_child_ipadding(GTK_BUTTON_BOX (box4), 4, 0);
3223 gtk_widget_show(box4);
3225 voice_bt = gtk_button_new_with_label("Save payload...");
3226 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3227 gtk_widget_show(voice_bt);
3228 g_signal_connect(voice_bt, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
3230 csv_bt = gtk_button_new_with_label("Save as CSV...");
3231 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3232 gtk_widget_show(csv_bt);
3233 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3235 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3236 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3237 gtk_widget_show(refresh_bt);
3238 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3240 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3241 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3242 gtk_widget_show(goto_bt);
3243 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked_lst), user_data);
3245 graph_bt = gtk_button_new_with_label("Graph");
3246 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3247 gtk_widget_show(graph_bt);
3248 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3251 #ifdef USE_CONVERSATION_GRAPH
3252 graph_bt = gtk_button_new_with_label("Graph");
3253 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3254 gtk_widget_show(graph_bt);
3255 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3258 next_bt = gtk_button_new_with_label("Next non-Ok");
3259 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3260 gtk_widget_show(next_bt);
3261 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked_list), user_data);
3263 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3264 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3265 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3266 gtk_widget_show(close_bt);
3267 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3269 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3270 g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3272 gtk_widget_show(window);
3273 window_present(window);
3276 /* some widget references need to be saved for outside use */
3277 user_data->dlg.window = window;
3278 user_data->dlg.list_fwd = list_fwd;
3279 user_data->dlg.list_rev = list_rev;
3280 user_data->dlg.label_stats_fwd = label_stats_fwd;
3281 user_data->dlg.label_stats_rev = label_stats_rev;
3282 user_data->dlg.notebook = notebook;
3283 user_data->dlg.selected_list = list_fwd;
3284 user_data->dlg.number_of_nok = 0;
3287 * select the initial row
3289 gtk_widget_grab_focus(list_fwd);
3294 /****************************************************************************/
3295 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3296 const gchar* proto_field, guint32* p_result)
3299 proto_node *proto_sibling_node;
3300 header_field_info *hfssrc;
3303 finfo = PITEM_FINFO(ptree_node);
3305 if (hfinformation==(finfo->hfinfo)) {
3306 hfssrc = proto_registrar_get_byname(proto_field);
3309 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3310 ptree_node=ptree_node->next) {
3311 finfo=PITEM_FINFO(ptree_node);
3312 if (hfssrc==finfo->hfinfo) {
3313 if (hfinformation->type==FT_IPv4) {
3314 ipv4 = fvalue_get(&finfo->value);
3315 *p_result = ipv4_get_net_order_addr(ipv4);
3318 *p_result = fvalue_get_uinteger(&finfo->value);
3327 proto_sibling_node = ptree_node->next;
3329 if (proto_sibling_node) {
3330 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3336 /****************************************************************************/
3337 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3338 const gchar* proto_name,
3339 const gchar* proto_field,
3342 proto_node *ptree_node;
3343 header_field_info *hfinformation;
3345 hfinformation = proto_registrar_get_byname(proto_name);
3346 if (hfinformation == NULL)
3349 ptree_node = ((proto_node *)protocol_tree)->first_child;
3353 return process_node(ptree_node, hfinformation, proto_field, p_result);
3357 /****************************************************************************/
3359 address *ip_src_fwd,
3360 guint16 port_src_fwd,
3361 address *ip_dst_fwd,
3362 guint16 port_dst_fwd,
3364 address *ip_src_rev,
3365 guint16 port_src_rev,
3366 address *ip_dst_rev,
3367 guint16 port_dst_rev,
3371 user_data_t *user_data;
3374 static color_t col[MAX_GRAPHS] = {
3375 {0, 0x0000, 0x0000, 0x0000},
3376 {0, 0xffff, 0x0000, 0x0000},
3377 {0, 0x0000, 0xffff, 0x0000},
3378 {0, 0x0000, 0x0000, 0xffff}
3382 user_data = g_malloc(sizeof(user_data_t));
3384 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3385 user_data->port_src_fwd = port_src_fwd;
3386 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3387 user_data->port_dst_fwd = port_dst_fwd;
3388 user_data->ssrc_fwd = ssrc_fwd;
3389 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3390 user_data->port_src_rev = port_src_rev;
3391 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3392 user_data->port_dst_rev = port_dst_rev;
3393 user_data->ssrc_rev = ssrc_rev;
3396 /* file names for storing sound data */
3397 /*XXX: check for errors*/
3398 fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
3401 fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
3404 user_data->forward.saveinfo.fp = NULL;
3405 user_data->reversed.saveinfo.fp = NULL;
3406 user_data->dlg.save_voice_as_w = NULL;
3407 user_data->dlg.save_csv_as_w = NULL;
3408 user_data->dlg.dialog_graph.window = NULL;
3410 #ifdef USE_CONVERSATION_GRAPH
3411 user_data->dlg.graph_window = NULL;
3412 user_data->series_fwd.value_pairs = NULL;
3413 user_data->series_rev.value_pairs = NULL;
3416 /* init dialog_graph */
3417 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3418 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3419 user_data->dlg.dialog_graph.draw_area=NULL;
3420 user_data->dlg.dialog_graph.pixmap=NULL;
3421 user_data->dlg.dialog_graph.scrollbar=NULL;
3422 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3423 user_data->dlg.dialog_graph.pixmap_width=500;
3424 user_data->dlg.dialog_graph.pixmap_height=200;
3425 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3426 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3427 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3428 user_data->dlg.dialog_graph.max_interval=0;
3429 user_data->dlg.dialog_graph.num_items=0;
3430 user_data->dlg.dialog_graph.start_time = -1;
3432 for(i=0;i<MAX_GRAPHS;i++){
3433 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3434 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3435 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3436 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3437 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3438 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3439 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3440 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3443 /* create the dialog box */
3444 create_rtp_dialog(user_data);
3446 /* proceed as if the Refresh button would have been pressed */
3447 on_refresh_bt_clicked(NULL, user_data);
3450 /****************************************************************************/
3451 /* entry point from main menu */
3452 static void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3455 guint16 port_src_fwd;
3457 guint16 port_dst_fwd;
3458 guint32 ssrc_fwd = 0;
3460 guint16 port_src_rev;
3462 guint16 port_dst_rev;
3463 guint32 ssrc_rev = 0;
3464 unsigned int version_fwd;
3466 gchar filter_text[256];
3469 epan_dissect_t *edt;
3472 gboolean frame_matched;
3474 GList *strinfo_list;
3475 GList *filtered_list = NULL;
3476 rtp_stream_info_t *strinfo;
3479 /* Try to compile the filter. */
3480 g_strlcpy(filter_text,"rtp && rtp.version && rtp.ssrc && (ip || ipv6)",256);
3481 if (!dfilter_compile(filter_text, &sfcode)) {
3482 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3485 /* we load the current file into cf variable */
3487 fdata = cf->current_frame;
3489 /* we are on the selected frame now */
3491 return; /* if we exit here it's an error */
3493 /* dissect the current frame */
3494 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3495 cf->pd, fdata->cap_len, &err, &err_info)) {
3496 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3497 cf_read_error_message(err, err_info), cf->filename);
3500 edt = epan_dissect_new(TRUE, FALSE);
3501 epan_dissect_prime_dfilter(edt, sfcode);
3502 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3503 frame_matched = dfilter_apply_edt(sfcode, edt);
3505 /* if it is not an rtp frame, show the rtpstream dialog */
3506 frame_matched = dfilter_apply_edt(sfcode, edt);
3507 if (frame_matched != 1) {
3508 epan_dissect_free(edt);
3509 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3510 "You didn't choose a RTP packet!");
3514 /* ok, it is a RTP frame, so let's get the ip and port values */
3515 COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
3516 COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
3517 port_src_fwd = edt->pi.srcport;
3518 port_dst_fwd = edt->pi.destport;
3520 /* assume the inverse ip/port combination for the reverse direction */
3521 COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
3522 COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
3523 port_src_rev = edt->pi.destport;
3524 port_dst_rev = edt->pi.srcport;
3526 /* check if it is RTP Version 2 */
3527 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3528 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3529 "RTP Version != 2 isn't supported!");
3533 /* now we need the SSRC value of the current frame */
3534 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3535 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3536 "SSRC value couldn't be found!");
3540 /* Scan for rtpstream */
3542 /* search for reversed direction in the global rtp streams list */
3544 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3545 while (strinfo_list)
3547 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3548 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3549 && strinfo->src_port==port_src_fwd
3550 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3551 && strinfo->dest_port==port_dst_fwd)
3553 filtered_list = g_list_prepend(filtered_list, strinfo);
3556 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3557 && strinfo->src_port==port_src_rev
3558 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3559 && strinfo->dest_port==port_dst_rev)
3562 filtered_list = g_list_append(filtered_list, strinfo);
3564 ssrc_rev = strinfo->ssrc;
3567 strinfo_list = g_list_next(strinfo_list);
3570 /* if more than one reverse streams found, we let the user choose the right one */
3572 rtpstream_dlg_show(filtered_list);
3591 /****************************************************************************/
3593 rtp_analysis_init(const char *dummy _U_,void* userdata _U_)
3595 rtp_analysis_cb(NULL, NULL);
3598 /****************************************************************************/
3600 register_tap_listener_rtp_analysis(void)
3602 register_stat_cmd_arg("rtp", rtp_analysis_init,NULL);
3604 register_stat_menu_item("RTP/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3605 rtp_analysis_cb, NULL, NULL, NULL);