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,
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, sizeof(title1), "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, sizeof(title2), "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,
902 sizeof(user_data->dlg.dialog_graph.graph[0].title),
903 "%s: %s:%u to %s:%u (SSRC=0x%X)",
905 get_addr_name(&(user_data->ip_src_fwd)),
906 user_data->port_src_fwd,
907 get_addr_name(&(user_data->ip_dst_fwd)),
908 user_data->port_dst_fwd,
909 user_data->ssrc_fwd);
912 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
913 sizeof(user_data->dlg.dialog_graph.graph[0].title),
914 "%s: %s:%u to %s:%u (SSRC=0x%X)",
916 get_addr_name(&(user_data->ip_src_rev)),
917 user_data->port_src_rev,
918 get_addr_name(&(user_data->ip_dst_rev)),
919 user_data->port_dst_rev,
920 user_data->ssrc_rev);
924 dialog_graph_set_title(user_data);
927 /****************************************************************************/
928 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
930 dialog_graph_graph_item_t *it;
937 /****************************************************************************/
938 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
941 g_snprintf(buf, buf_len, "%ds",t/1000000);
942 } else if(t>=1000000){
943 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
945 g_snprintf(buf, buf_len, "%dms",t/1000);
947 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
949 g_snprintf(buf, buf_len, "%dus",t);
953 /****************************************************************************/
954 static void dialog_graph_draw(user_data_t* user_data)
957 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
958 gint32 current_interval;
959 guint32 left_x_border;
960 guint32 right_x_border;
961 guint32 top_y_border;
962 guint32 bottom_y_border;
964 int label_width, label_height;
965 guint32 draw_width, draw_height;
966 char label_string[15];
969 guint32 num_time_intervals;
970 guint32 max_value; /* max value of seen data */
971 guint32 max_y; /* max value of the Y scale */
973 if(!user_data->dlg.dialog_graph.needs_redraw){
976 user_data->dlg.dialog_graph.needs_redraw=FALSE;
979 * Find the length of the intervals we have data for
980 * so we know how large arrays we need to malloc()
982 num_time_intervals=user_data->dlg.dialog_graph.num_items;
983 /* if there isnt anything to do, just return */
984 if(num_time_intervals==0){
987 num_time_intervals+=1;
988 /* XXX move this check to _packet() */
989 if(num_time_intervals>NUM_GRAPH_ITEMS){
990 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
995 * find the max value so we can autoscale the y axis
998 for(i=0;i<MAX_GRAPHS;i++){
1001 if(!user_data->dlg.dialog_graph.graph[i].display){
1004 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
1007 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1009 /* keep track of the max value we have encountered */
1017 * Clear out old plot
1019 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1020 user_data->dlg.dialog_graph.draw_area->style->white_gc,
1023 user_data->dlg.dialog_graph.draw_area->allocation.width,
1024 user_data->dlg.dialog_graph.draw_area->allocation.height);
1028 * Calculate the y scale we should use
1030 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1031 max_y=yscale_max[MAX_YSCALE-1];
1032 for(i=MAX_YSCALE-1;i>0;i--){
1033 if(max_value<yscale_max[i]){
1034 max_y=yscale_max[i];
1038 /* the user had specified an explicit y scale to use */
1039 max_y=user_data->dlg.dialog_graph.max_y_units;
1043 * Calculate size of borders surrounding the plot
1044 * The border on the right side needs to be adjusted depending
1045 * on the width of the text labels. For simplicity we assume that the
1046 * top y scale label will be the widest one
1048 print_time_scale_string(label_string, sizeof(label_string), max_y);
1049 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1050 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1052 right_x_border=label_width+20;
1054 bottom_y_border=label_height+20;
1058 * Calculate the size of the drawing area for the actual plot
1060 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1061 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1065 * Draw the y axis and labels
1066 * (we always draw the y scale with 11 ticks along the axis)
1068 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1069 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1071 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1072 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1078 /* first, middle and last tick are slightly longer */
1082 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1083 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1084 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1085 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1086 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1087 /* draw the labels */
1089 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1090 pango_layout_set_text(layout, label_string, -1);
1091 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1092 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1093 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1094 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1095 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1099 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1100 pango_layout_set_text(layout, label_string, -1);
1101 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1102 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1103 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1104 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1105 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1109 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1110 pango_layout_set_text(layout, label_string, -1);
1111 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1112 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1113 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1114 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1115 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1123 * if we have not specified the last_interval via the gui,
1124 * then just pick the current end of the capture so that is scrolls
1125 * nicely when doing live captures
1127 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1128 last_interval=user_data->dlg.dialog_graph.max_interval;
1130 last_interval=user_data->dlg.dialog_graph.last_interval;
1137 /* plot the x-scale */
1138 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);
1140 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1141 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1142 first_interval*=user_data->dlg.dialog_graph.interval;
1149 while(interval_delta<((last_interval-first_interval)/10)){
1150 interval_delta*=delta_multiplier;
1151 if(delta_multiplier==5){
1158 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1161 /* if pixels_per_tick is <5, only draw every 10 ticks */
1162 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1166 if(current_interval%interval_delta){
1172 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1173 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1174 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1175 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1176 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1177 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1181 if(user_data->dlg.dialog_graph.interval>=1000){
1182 g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
1183 } else if(user_data->dlg.dialog_graph.interval>=100){
1184 g_snprintf(label_string, sizeof(label_string), "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1185 } else if(user_data->dlg.dialog_graph.interval>=10){
1186 g_snprintf(label_string, sizeof(label_string), "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1188 g_snprintf(label_string, sizeof(label_string), "%d.%3ds", current_interval/1000,current_interval%1000);
1190 pango_layout_set_text(layout, label_string, -1);
1191 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1192 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1193 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1194 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1195 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1207 * Draw "x" for Sequence Errors and "m" for Marks
1209 /* Draw the labels Fwd and Rev */
1210 g_strlcpy(label_string,"<-Fwd",sizeof(label_string));
1211 pango_layout_set_text(layout, label_string, -1);
1212 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1213 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1214 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1215 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1216 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1218 g_strlcpy(label_string,"<-Rev",sizeof(label_string));
1219 pango_layout_set_text(layout, label_string, -1);
1220 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1221 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1222 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1223 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1224 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1227 /* Draw the marks */
1228 for(i=MAX_GRAPHS-1;i>=0;i--){
1230 guint32 x_pos, prev_x_pos;
1232 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1233 if (!user_data->dlg.dialog_graph.graph[i].display){
1236 /* initialize prev x/y to the low left corner of the graph */
1237 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;
1239 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1240 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;
1242 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1244 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1245 g_strlcpy(label_string,"x",sizeof(label_string));
1247 g_strlcpy(label_string,"m",sizeof(label_string));
1250 pango_layout_set_text(layout, label_string, -1);
1251 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1252 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1253 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1255 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1263 g_object_unref(G_OBJECT(layout));
1266 * Loop over all graphs and draw them
1268 for(i=MAX_GRAPHS-1;i>=0;i--){
1270 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1271 if (!user_data->dlg.dialog_graph.graph[i].display){
1274 /* initialize prev x/y to the low left corner of the graph */
1275 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;
1276 prev_y_pos=draw_height-1+top_y_border;
1278 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1280 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;
1281 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1285 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1288 /* dont need to draw anything if the segment
1289 * is entirely above the top of the graph
1291 if( (prev_y_pos==0) && (y_pos==0) ){
1298 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1299 x_pos, draw_height-1+top_y_border,
1309 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1310 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1311 user_data->dlg.dialog_graph.pixmap,
1314 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1317 /* update the scrollbar */
1318 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1319 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1320 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1321 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1322 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1324 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1326 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1327 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1328 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1332 /****************************************************************************/
1333 static void dialog_graph_redraw(user_data_t* user_data)
1335 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1336 dialog_graph_draw(user_data);
1339 /****************************************************************************/
1340 static gint quit(GtkWidget *widget, GdkEventExpose *event _U_)
1342 user_data_t *user_data;
1344 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1346 user_data->dlg.dialog_graph.window = NULL;
1350 /****************************************************************************/
1351 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1353 user_data_t *user_data;
1355 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1361 gdk_draw_pixmap(widget->window,
1362 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1363 user_data->dlg.dialog_graph.pixmap,
1364 event->area.x, event->area.y,
1365 event->area.x, event->area.y,
1366 event->area.width, event->area.height);
1371 /****************************************************************************/
1372 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1374 user_data_t *user_data;
1377 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1383 if(user_data->dlg.dialog_graph.pixmap){
1384 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1385 user_data->dlg.dialog_graph.pixmap=NULL;
1388 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1389 widget->allocation.width,
1390 widget->allocation.height,
1392 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1393 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1395 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1396 widget->style->white_gc,
1399 widget->allocation.width,
1400 widget->allocation.height);
1402 /* set up the colors and the GC structs for this pixmap */
1403 for(i=0;i<MAX_GRAPHS;i++){
1404 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1405 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1408 dialog_graph_redraw(user_data);
1412 /****************************************************************************/
1413 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1415 user_data_t *user_data=(user_data_t *)data;
1418 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1419 if(user_data->dlg.dialog_graph.last_interval==mi){
1422 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1423 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1427 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1429 dialog_graph_redraw(user_data);
1433 /****************************************************************************/
1434 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1436 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1437 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1438 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.draw_area), "user_data_t", user_data);
1440 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);
1442 /* signals needed to handle backing pixmap */
1443 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1444 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1446 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1447 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1449 /* create the associated scrollbar */
1450 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1451 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1452 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1453 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1454 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1457 /****************************************************************************/
1458 static void disable_graph(dialog_graph_graph_t *dgg)
1462 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1467 /****************************************************************************/
1468 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1470 /* this graph is not active, just update display and redraw */
1471 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1473 dialog_graph_redraw(dgg->ud);
1478 cf_retap_packets(&cfile, FALSE);
1479 dialog_graph_redraw(dgg->ud);
1484 /****************************************************************************/
1485 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1491 hbox=gtk_hbox_new(FALSE, 3);
1492 gtk_container_add(GTK_CONTAINER(box), hbox);
1493 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1494 gtk_widget_show(hbox);
1496 g_snprintf(str, sizeof(str), "Graph %d", num);
1497 dgg->display_button=gtk_toggle_button_new_with_label(str);
1498 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1499 gtk_widget_show(dgg->display_button);
1500 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1501 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1503 label=gtk_label_new(dgg->title);
1504 gtk_widget_show(label);
1505 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1507 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1508 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1509 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1510 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1511 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1516 /****************************************************************************/
1517 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1524 frame=gtk_frame_new("Graphs");
1525 gtk_container_add(GTK_CONTAINER(box), frame);
1526 gtk_widget_show(frame);
1528 vbox=gtk_vbox_new(FALSE, 1);
1529 gtk_container_add(GTK_CONTAINER(frame), vbox);
1530 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1531 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1532 gtk_widget_show(vbox);
1534 for(i=0;i<MAX_GRAPHS;i++){
1535 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1538 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1539 gtk_widget_show(label);
1540 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1545 /****************************************************************************/
1546 static void yscale_select(GtkWidget *item, gpointer key)
1549 user_data_t *user_data;
1551 user_data=(user_data_t *)key;
1552 val=(long)g_object_get_data(G_OBJECT(item), "yscale_max");
1554 user_data->dlg.dialog_graph.max_y_units=val;
1555 dialog_graph_redraw(user_data);
1558 /****************************************************************************/
1559 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1562 user_data_t *user_data;
1564 user_data=(user_data_t *)key;
1565 val=(long)g_object_get_data(G_OBJECT(item), "pixels_per_tick");
1566 user_data->dlg.dialog_graph.pixels_per_tick=val;
1567 dialog_graph_redraw(user_data);
1570 /****************************************************************************/
1571 static void tick_interval_select(GtkWidget *item, gpointer key)
1574 user_data_t *user_data;
1576 user_data=(user_data_t *)key;
1577 val=(long)g_object_get_data(G_OBJECT(item), "tick_interval");
1579 user_data->dlg.dialog_graph.interval=val;
1580 cf_retap_packets(&cfile, FALSE);
1581 dialog_graph_redraw(user_data);
1584 /****************************************************************************/
1585 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1588 GtkWidget *menu_item;
1591 for(i=0;i<MAX_YSCALE;i++){
1592 if(yscale_max[i]==AUTO_MAX_YSCALE){
1593 g_strlcpy(str,"Auto",sizeof(str));
1595 g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1597 menu_item=gtk_menu_item_new_with_label(str);
1598 g_object_set_data(G_OBJECT(menu_item), "yscale_max",
1599 GUINT_TO_POINTER(yscale_max[i]));
1600 g_signal_connect(menu_item, "activate", G_CALLBACK(yscale_select), user_data);
1601 gtk_widget_show(menu_item);
1602 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1607 /****************************************************************************/
1608 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
1611 GtkWidget *menu_item;
1614 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1615 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1616 menu_item=gtk_menu_item_new_with_label(str);
1618 g_object_set_data(G_OBJECT(menu_item), "pixels_per_tick",
1619 GUINT_TO_POINTER(pixels_per_tick[i]));
1620 g_signal_connect(menu_item, "activate", G_CALLBACK(pixels_per_tick_select), user_data);
1621 gtk_widget_show(menu_item);
1622 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1624 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1629 /****************************************************************************/
1630 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
1633 GtkWidget *menu_item;
1636 for(i=0;i<MAX_TICK_VALUES;i++){
1637 if(tick_interval_values[i]>=1000){
1638 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1639 } else if(tick_interval_values[i]>=100){
1640 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1641 } else if(tick_interval_values[i]>=10){
1642 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1644 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1647 menu_item=gtk_menu_item_new_with_label(str);
1648 g_object_set_data(G_OBJECT(menu_item), "tick_interval",
1649 GUINT_TO_POINTER(tick_interval_values[i]));
1650 g_signal_connect(menu_item, "activate", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1651 gtk_widget_show(menu_item);
1652 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1654 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1658 /****************************************************************************/
1659 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
1663 GtkWidget *option_menu;
1666 hbox=gtk_hbox_new(FALSE, 0);
1667 gtk_container_add(GTK_CONTAINER(box), hbox);
1668 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1669 gtk_widget_show(hbox);
1671 label=gtk_label_new(name);
1672 gtk_widget_show(label);
1673 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1675 option_menu=gtk_option_menu_new();
1676 menu=gtk_menu_new();
1677 (*func)(user_data, menu);
1678 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1679 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1680 gtk_widget_show(option_menu);
1683 /****************************************************************************/
1684 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1686 GtkWidget *frame_vbox;
1690 frame_vbox=gtk_vbox_new(FALSE, 0);
1691 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1692 gtk_widget_show(frame_vbox);
1694 frame = gtk_frame_new("X Axis");
1695 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1696 gtk_widget_show(frame);
1698 vbox=gtk_vbox_new(FALSE, 0);
1699 gtk_container_add(GTK_CONTAINER(frame), vbox);
1700 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1701 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1702 gtk_widget_show(vbox);
1704 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1705 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1707 frame = gtk_frame_new("Y Axis");
1708 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1709 gtk_widget_show(frame);
1711 vbox=gtk_vbox_new(FALSE, 0);
1712 gtk_container_add(GTK_CONTAINER(frame), vbox);
1713 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1714 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1715 gtk_widget_show(vbox);
1717 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1722 /****************************************************************************/
1723 static void dialog_graph_init_window(user_data_t* user_data)
1727 GtkWidget *bt_close;
1729 /* create the main window */
1730 user_data->dlg.dialog_graph.window=window_new(GTK_WINDOW_TOPLEVEL, "I/O Graphs");
1732 vbox=gtk_vbox_new(FALSE, 0);
1733 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1734 gtk_widget_show(vbox);
1736 create_draw_area(user_data, vbox);
1738 hbox=gtk_hbox_new(FALSE, 3);
1739 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1740 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1741 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1742 gtk_widget_show(hbox);
1744 create_filter_area(user_data, hbox);
1745 create_ctrl_area(user_data, hbox);
1747 dialog_graph_set_title(user_data);
1749 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1750 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1751 gtk_widget_show(hbox);
1753 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1754 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1756 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1758 gtk_widget_show(user_data->dlg.dialog_graph.window);
1759 window_present(user_data->dlg.dialog_graph.window);
1764 /****************************************************************************/
1765 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1767 if (user_data->dlg.dialog_graph.window != NULL) {
1768 /* There's already a graph window; reactivate it. */
1769 reactivate_window(user_data->dlg.dialog_graph.window);
1773 dialog_graph_init_window(user_data);
1777 /****************************************************************************/
1779 static void on_goto_bt_clicked_lst(GtkWidget *bt _U_, user_data_t *user_data _U_)
1782 GtkTreeModel *model;
1783 GtkTreeSelection *selection;
1786 selection = user_data->dlg.selected_list_sel;
1788 if (selection==NULL)
1791 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1792 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1793 cf_goto_frame(&cfile, fnumber);
1798 static void draw_stat(user_data_t *user_data);
1800 /****************************************************************************/
1801 /* re-dissects all packets */
1802 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
1804 GString *error_string;
1806 /* remove tap listener */
1807 protect_thread_critical_region();
1808 remove_tap_listener(user_data);
1809 unprotect_thread_critical_region();
1811 /* register tap listener */
1812 error_string = register_tap_listener("rtp", user_data, NULL,
1813 rtp_reset, rtp_packet, rtp_draw);
1814 if (error_string != NULL) {
1815 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1816 g_string_free(error_string, TRUE);
1820 /* retap all packets */
1821 cf_retap_packets(&cfile, FALSE);
1823 /* draw statistics info */
1824 draw_stat(user_data);
1828 static void on_next_bt_clicked_list(GtkWidget *bt _U_, user_data_t *user_data _U_)
1831 GtkTreeModel *model;
1833 GtkTreeSelection *selection;
1836 selection = user_data->dlg.selected_list_sel;
1838 if (selection==NULL)
1842 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1843 while (gtk_tree_model_iter_next (model,&iter)) {
1844 gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1845 if (strcmp(text, OK_TEXT) != 0) {
1846 gtk_tree_selection_select_iter (selection, &iter);
1847 path = gtk_tree_model_get_path(model, &iter);
1848 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1851 gtk_tree_path_free(path);
1858 if (user_data->dlg.number_of_nok>1){
1859 /* Get the first iter and select it before starting over */
1860 gtk_tree_model_get_iter_first(model, &iter);
1861 gtk_tree_selection_select_iter (selection, &iter);
1868 /****************************************************************************/
1869 /* when we want to save the information */
1870 static void save_csv_as_ok_cb(GtkWidget *bt _U_, gpointer fs /*user_data_t *user_data*/ _U_)
1873 GtkWidget *rev, *forw, *both;
1874 user_data_t *user_data;
1876 GtkListStore *store;
1878 GtkTreeModel *model;
1879 gboolean more_items = TRUE;
1881 /* To Hold data from the list row */
1882 guint packet; /* Packet */
1883 guint sequence; /* Sequence */
1884 gfloat delta; /* Delta(ms) */
1885 gfloat jitter; /* Jitter(ms) */
1886 gfloat ipbw; /* IP BW(kbps) */
1887 gboolean marker; /* Marker */
1888 char * status_str; /* Status */
1889 char * date_str; /* Date */
1890 guint length; /* Length */
1896 g_dest = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
1898 /* Perhaps the user specified a directory instead of a file.
1899 * Check whether they did.
1901 if (test_for_directory(g_dest) == EISDIR) {
1902 /* It's a directory - set the file selection box to display it. */
1903 set_last_open_dir(g_dest);
1905 file_selection_set_current_folder(fs, get_last_open_dir());
1909 rev = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "reversed_rb");
1910 forw = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "forward_rb");
1911 both = (GtkWidget*)g_object_get_data(G_OBJECT(bt), "both_rb");
1912 user_data = (user_data_t*)g_object_get_data(G_OBJECT(bt), "user_data");
1914 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
1915 fp = ws_fopen(g_dest, "w");
1917 open_failure_alert_box(g_dest, errno, TRUE);
1921 if (GTK_TOGGLE_BUTTON(both)->active) {
1922 fprintf(fp, "Forward\n");
1924 write_failure_alert_box(g_dest, errno);
1930 for(j = 0; j < NUM_COLS; j++) {
1932 fprintf(fp,"%s",titles[j]);
1934 fprintf(fp,",%s",titles[j]);
1939 write_failure_alert_box(g_dest, errno);
1943 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
1944 store = GTK_LIST_STORE(model);
1945 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
1948 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1959 fprintf(fp, "%u",packet);
1960 fprintf(fp, ",%u", sequence);
1961 fprintf(fp, ",%.2f", delta);
1962 fprintf(fp, ",%.2f", jitter);
1963 fprintf(fp, ",%.2f", ipbw);
1964 fprintf(fp, ",%s", marker? "SET" : "");
1965 fprintf(fp, ",%s", status_str);
1966 fprintf(fp, ",%s", date_str);
1967 fprintf(fp, ",%u", length);
1970 write_failure_alert_box(g_dest, errno);
1975 more_items = gtk_tree_model_iter_next (model,&iter);
1979 if (fclose(fp) == EOF) {
1980 write_failure_alert_box(g_dest, errno);
1985 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
1987 if (GTK_TOGGLE_BUTTON(both)->active) {
1988 fp = ws_fopen(g_dest, "a");
1990 open_failure_alert_box(g_dest, errno, TRUE);
1993 fprintf(fp, "\nReverse\n");
1995 write_failure_alert_box(g_dest, errno);
2000 fp = ws_fopen(g_dest, "w");
2002 open_failure_alert_box(g_dest, errno, TRUE);
2006 for(j = 0; j < NUM_COLS; j++) {
2008 fprintf(fp,"%s",titles[j]);
2010 fprintf(fp,",%s",titles[j]);
2015 write_failure_alert_box(g_dest, errno);
2019 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
2020 store = GTK_LIST_STORE(model);
2021 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2026 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2037 fprintf(fp, "%u",packet);
2038 fprintf(fp, ",%u", sequence);
2039 fprintf(fp, ",%.2f", delta);
2040 fprintf(fp, ",%.2f", jitter);
2041 fprintf(fp, ",%.2f", ipbw);
2042 fprintf(fp, ",%s", marker? "SET" : "");
2043 fprintf(fp, ",%s", status_str);
2044 fprintf(fp, ",%s", date_str);
2045 fprintf(fp, ",%u", length);
2048 write_failure_alert_box(g_dest, errno);
2053 more_items = gtk_tree_model_iter_next (model,&iter);
2056 if (fclose(fp) == EOF) {
2057 write_failure_alert_box(g_dest, errno);
2062 window_destroy(GTK_WIDGET(user_data->dlg.save_csv_as_w));
2065 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2067 user_data->dlg.save_csv_as_w = NULL;
2070 /* when the user wants to save the csv information in a file */
2071 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data _U_)
2075 GtkWidget *label_format;
2076 GtkWidget *channels_label;
2077 GSList *channels_group = NULL;
2078 GtkWidget *forward_rb;
2079 GtkWidget *reversed_rb;
2082 if (user_data->dlg.save_csv_as_w != NULL) {
2083 /* There's already a Save CSV info dialog box; reactivate it. */
2084 reactivate_window(user_data->dlg.save_csv_as_w);
2088 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,
2089 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2090 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2093 /* Container for each row of widgets */
2094 vertb = gtk_vbox_new(FALSE, 0);
2095 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2096 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2097 gtk_widget_show (vertb);
2099 table1 = gtk_table_new (2, 4, FALSE);
2100 gtk_widget_show (table1);
2101 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2102 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2103 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2105 label_format = gtk_label_new ("Format: Comma Separated Values");
2106 gtk_widget_show (label_format);
2107 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2108 (GtkAttachOptions) (GTK_FILL),
2109 (GtkAttachOptions) (0), 0, 0);
2112 channels_label = gtk_label_new ("Channels:");
2113 gtk_widget_show (channels_label);
2114 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2115 (GtkAttachOptions) (GTK_FILL),
2116 (GtkAttachOptions) (0), 0, 0);
2117 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2119 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2120 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2121 gtk_widget_show (forward_rb);
2122 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2123 (GtkAttachOptions) (GTK_FILL),
2124 (GtkAttachOptions) (0), 0, 0);
2126 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2127 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2128 gtk_widget_show (reversed_rb);
2129 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2130 (GtkAttachOptions) (GTK_FILL),
2131 (GtkAttachOptions) (0), 0, 0);
2133 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2134 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2135 gtk_widget_show (both_rb);
2136 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2137 (GtkAttachOptions) (GTK_FILL),
2138 (GtkAttachOptions) (0), 0, 0);
2140 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2142 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2143 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2144 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2145 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2147 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2148 G_CALLBACK(window_delete_event_cb), NULL);
2149 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2150 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2152 gtk_widget_show(user_data->dlg.save_csv_as_w);
2153 window_present(user_data->dlg.save_csv_as_w);
2155 if (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT){
2156 save_csv_as_ok_cb(user_data->dlg.save_csv_as_w, user_data->dlg.save_csv_as_w);
2158 window_destroy(user_data->dlg.save_csv_as_w);
2164 /****************************************************************************/
2165 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data _U_)
2167 /* Note that we no longer have a Save voice info dialog box. */
2168 user_data->dlg.save_voice_as_w = NULL;
2171 /****************************************************************************/
2172 /* here we save it into a file that user specified */
2173 /* XXX what about endians here? could go something wrong? */
2174 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2176 FILE *to_stream, *forw_stream, *rev_stream;
2177 size_t fwritten, rwritten;
2178 int f_rawvalue, r_rawvalue, rawvalue;
2181 guint32 f_write_silence = 0;
2182 guint32 r_write_silence = 0;
2184 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2185 gboolean stop_flag = FALSE;
2188 forw_stream = ws_fopen(user_data->f_tempname, "rb");
2189 if (forw_stream == NULL)
2191 rev_stream = ws_fopen(user_data->r_tempname, "rb");
2192 if (rev_stream == NULL) {
2193 fclose(forw_stream);
2197 /* open file for saving */
2198 to_stream = ws_fopen(dest, "wb");
2199 if (to_stream == NULL) {
2200 fclose(forw_stream);
2205 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2207 if (format == SAVE_AU_FORMAT) /* au format */
2209 /* First we write the .au header. XXX Hope this is endian independant */
2210 /* the magic word 0x2e736e64 == .snd */
2211 phtonl(pd, 0x2e736e64);
2212 nchars=fwrite(pd, 1, 4, to_stream);
2213 /* header offset == 24 bytes */
2215 nchars=fwrite(pd, 1, 4, to_stream);
2216 /* total length; it is permitted to set this to 0xffffffff */
2218 nchars=fwrite(pd, 1, 4, to_stream);
2219 /* encoding format == 16-bit linear PCM */
2221 nchars=fwrite(pd, 1, 4, to_stream);
2222 /* sample rate == 8000 Hz */
2224 nchars=fwrite(pd, 1, 4, to_stream);
2227 nchars=fwrite(pd, 1, 4, to_stream);
2231 /* only forward direction */
2232 case SAVE_FORWARD_DIRECTION_MASK: {
2233 progbar_count = user_data->forward.saveinfo.count;
2234 progbar_quantum = user_data->forward.saveinfo.count/100;
2235 while ((f_rawvalue = getc(forw_stream)) != EOF) {
2238 if((count > progbar_nextstep) && (count <= progbar_count)) {
2239 update_progress_dlg(progbar,
2240 (gfloat) count/progbar_count, "Saving");
2241 progbar_nextstep = progbar_nextstep + progbar_quantum;
2245 if (user_data->forward.statinfo.pt == PT_PCMU){
2246 sample = ulaw2linear((unsigned char)f_rawvalue);
2249 else if(user_data->forward.statinfo.pt == PT_PCMA){
2250 sample = alaw2linear((unsigned char)f_rawvalue);
2254 fclose(forw_stream);
2257 destroy_progress_dlg(progbar);
2261 fwritten = fwrite(pd, 1, 2, to_stream);
2263 fclose(forw_stream);
2266 destroy_progress_dlg(progbar);
2272 /* only reversed direction */
2273 case SAVE_REVERSE_DIRECTION_MASK: {
2274 progbar_count = user_data->reversed.saveinfo.count;
2275 progbar_quantum = user_data->reversed.saveinfo.count/100;
2276 while ((r_rawvalue = getc(rev_stream)) != EOF) {
2279 if((count > progbar_nextstep) && (count <= progbar_count)) {
2280 update_progress_dlg(progbar,
2281 (gfloat) count/progbar_count, "Saving");
2282 progbar_nextstep = progbar_nextstep + progbar_quantum;
2286 if (user_data->reversed.statinfo.pt == PT_PCMU){
2287 sample = ulaw2linear((unsigned char)r_rawvalue);
2290 else if(user_data->reversed.statinfo.pt == PT_PCMA){
2291 sample = alaw2linear((unsigned char)r_rawvalue);
2295 fclose(forw_stream);
2298 destroy_progress_dlg(progbar);
2302 rwritten = fwrite(pd, 1, 2, to_stream);
2304 fclose(forw_stream);
2307 destroy_progress_dlg(progbar);
2313 /* both directions */
2314 case SAVE_BOTH_DIRECTION_MASK: {
2315 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2316 (progbar_count = user_data->forward.saveinfo.count) :
2317 (progbar_count = user_data->reversed.saveinfo.count);
2318 progbar_quantum = progbar_count/100;
2319 /* since conversation in one way can start later than in the other one,
2320 * we have to write some silence information for one channel */
2321 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2322 f_write_silence = (guint32)
2323 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2325 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2326 r_write_silence = (guint32)
2327 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2332 if((count > progbar_nextstep) && (count <= progbar_count)) {
2333 update_progress_dlg(progbar,
2334 (gfloat) count/progbar_count, "Saving");
2335 progbar_nextstep = progbar_nextstep + progbar_quantum;
2338 if(f_write_silence > 0) {
2339 r_rawvalue = getc(rev_stream);
2340 switch (user_data->forward.statinfo.reg_pt) {
2342 f_rawvalue = SILENCE_PCMU;
2345 f_rawvalue = SILENCE_PCMA;
2353 else if(r_write_silence > 0) {
2354 f_rawvalue = getc(forw_stream);
2355 switch (user_data->reversed.statinfo.reg_pt) {
2357 r_rawvalue = SILENCE_PCMU;
2360 r_rawvalue = SILENCE_PCMA;
2369 f_rawvalue = getc(forw_stream);
2370 r_rawvalue = getc(rev_stream);
2372 if ((r_rawvalue == EOF) && (f_rawvalue == EOF))
2374 if ((user_data->forward.statinfo.pt == PT_PCMU) && (user_data->reversed.statinfo.pt == PT_PCMU)){
2375 sample = (ulaw2linear((unsigned char)r_rawvalue) + ulaw2linear((unsigned char)f_rawvalue)) / 2;
2378 else if((user_data->forward.statinfo.pt == PT_PCMA) && (user_data->reversed.statinfo.pt == PT_PCMA)){
2379 sample = (alaw2linear((unsigned char)r_rawvalue) + alaw2linear((unsigned char)f_rawvalue)) / 2;
2384 fclose(forw_stream);
2387 destroy_progress_dlg(progbar);
2392 rwritten = fwrite(pd, 1, 2, to_stream);
2394 fclose(forw_stream);
2397 destroy_progress_dlg(progbar);
2404 else if (format == SAVE_RAW_FORMAT) /* raw format */
2408 /* only forward direction */
2409 case SAVE_FORWARD_DIRECTION_MASK: {
2410 progbar_count = user_data->forward.saveinfo.count;
2411 progbar_quantum = user_data->forward.saveinfo.count/100;
2412 stream = forw_stream;
2415 /* only reversed direction */
2416 case SAVE_REVERSE_DIRECTION_MASK: {
2417 progbar_count = user_data->reversed.saveinfo.count;
2418 progbar_quantum = user_data->reversed.saveinfo.count/100;
2419 stream = rev_stream;
2423 fclose(forw_stream);
2426 destroy_progress_dlg(progbar);
2433 /* XXX how do you just copy the file? */
2434 while ((rawvalue = getc(stream)) != EOF) {
2437 if((count > progbar_nextstep) && (count <= progbar_count)) {
2438 update_progress_dlg(progbar,
2439 (gfloat) count/progbar_count, "Saving");
2440 progbar_nextstep = progbar_nextstep + progbar_quantum;
2444 if (putc(rawvalue, to_stream) == EOF) {
2445 fclose(forw_stream);
2448 destroy_progress_dlg(progbar);
2454 destroy_progress_dlg(progbar);
2455 fclose(forw_stream);
2462 /****************************************************************************/
2463 /* the user wants to save in a file */
2464 /* XXX support for different formats is currently commented out */
2465 static void save_voice_as_ok_cb(GtkWidget *ok_bt _U_, gpointer fs _U_)
2468 /*GtkWidget *wav, *sw;*/
2469 GtkWidget *au, *raw;
2470 GtkWidget *rev, *forw, *both;
2471 user_data_t *user_data;
2472 gint channels , format;
2474 g_dest = g_strdup(gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs)));
2476 /* Perhaps the user specified a directory instead of a file.
2477 Check whether they did. */
2478 if (test_for_directory(g_dest) == EISDIR) {
2479 /* It's a directory - set the file selection box to display it. */
2480 set_last_open_dir(g_dest);
2482 file_selection_set_current_folder(fs, get_last_open_dir());
2486 /*wav = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "wav_rb");
2487 sw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "sw_rb");*/
2488 au = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "au_rb");
2489 raw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "raw_rb");
2490 rev = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "reversed_rb");
2491 forw = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "forward_rb");
2492 both = (GtkWidget *)g_object_get_data(G_OBJECT(ok_bt), "both_rb");
2493 user_data = (user_data_t *)g_object_get_data(G_OBJECT(ok_bt), "user_data");
2495 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2496 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2497 * disable the ok button or disable the buttons for direction if only one is not ok. The
2498 * problem is if we open the save voice dialog and then click the refresh button and maybe
2499 * the state changes, so we can't save anymore. In this case we should be able to update
2500 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2503 /* we can not save in both directions */
2504 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2505 /* there are many combinations here, we just exit when first matches */
2506 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2507 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2508 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2509 "Can't save in a file: Unsupported codec!");
2510 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2511 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2512 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2513 "Can't save in a file: Wrong length of captured packets!");
2514 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2515 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2516 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2517 "Can't save in a file: RTP data with padding!");
2518 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2519 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2520 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2521 "Can't save in a file: Not all data in all packets was captured!");
2523 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2524 "Can't save in a file: File I/O problem!");
2527 /* we can not save forward direction */
2528 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2529 (GTK_TOGGLE_BUTTON (both)->active))) {
2530 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2531 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2532 "Can't save forward direction in a file: Unsupported codec!");
2533 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2534 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2535 "Can't save forward direction in a file: Wrong length of captured packets!");
2536 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2537 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2538 "Can't save forward direction in a file: RTP data with padding!");
2539 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2540 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2541 "Can't save forward direction in a file: Not all data in all packets was captured!");
2543 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2544 "Can't save forward direction in a file: File I/O problem!");
2547 /* we can not save reversed direction */
2548 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2549 (GTK_TOGGLE_BUTTON (both)->active))) {
2550 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2551 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2552 "Can't save reversed direction in a file: Unsupported codec!");
2553 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2554 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2555 "Can't save reversed direction in a file: Wrong length of captured packets!");
2556 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2557 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2558 "Can't save reversed direction in a file: RTP data with padding!");
2559 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2560 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2561 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2562 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2563 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2564 "Can't save reversed direction in a file: No RTP data!");
2566 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2567 "Can't save reversed direction in a file: File I/O problem!");
2571 /*if (GTK_TOGGLE_BUTTON (wav)->active)
2572 format = SAVE_WAV_FORMAT;
2573 else */if (GTK_TOGGLE_BUTTON (au)->active)
2574 format = SAVE_AU_FORMAT;
2575 /*else if (GTK_TOGGLE_BUTTON (sw)->active)
2576 format = SAVE_SW_FORMAT;*/
2577 else if (GTK_TOGGLE_BUTTON (raw)->active)
2578 format = SAVE_RAW_FORMAT;
2580 format = SAVE_NONE_FORMAT;
2582 if (GTK_TOGGLE_BUTTON (rev)->active)
2583 channels = SAVE_REVERSE_DIRECTION_MASK;
2584 else if (GTK_TOGGLE_BUTTON (both)->active)
2585 channels = SAVE_BOTH_DIRECTION_MASK;
2587 channels = SAVE_FORWARD_DIRECTION_MASK;
2589 /* direction/format validity*/
2590 if (format == SAVE_AU_FORMAT)
2592 /* make sure streams are alaw/ulaw */
2593 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != PT_PCMA) && (user_data->forward.statinfo.pt != PT_PCMU)){
2594 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2595 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2598 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != PT_PCMA) && (user_data->reversed.statinfo.pt != PT_PCMU)){
2599 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2600 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2603 /* make sure pt's don't differ */
2604 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2605 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2606 "Can't save in a file: Forward and reverse direction differ in type");
2610 else if (format == SAVE_RAW_FORMAT)
2612 /* can't save raw in both directions */
2613 if (channels == SAVE_BOTH_DIRECTION_MASK){
2614 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2615 "Can't save in a file: Unable to save raw data in both directions");
2621 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2622 "Can't save in a file: Invalid save format");
2626 if(!copy_file(g_dest, channels, format, user_data)) {
2627 /* XXX - report the error type! */
2628 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2629 "An error occurred while saving voice in a file!");
2633 window_destroy(GTK_WIDGET(user_data->dlg.save_voice_as_w));
2636 /****************************************************************************/
2637 /* when the user wants to save the voice information in a file */
2638 /* XXX support for different formats is currently commented out */
2639 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
2643 GtkWidget *label_format;
2644 GtkWidget *channels_label;
2645 GSList *format_group = NULL;
2646 GSList *channels_group = NULL;
2647 GtkWidget *forward_rb;
2648 GtkWidget *reversed_rb;
2650 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2654 /* if we can't save in a file: wrong codec, cut packets or other errors */
2655 /* shold the error arise here or later when you click ok button ?
2656 * if we do it here, then we must disable the refresh button, so we don't do it here */
2658 if (user_data->dlg.save_voice_as_w != NULL) {
2659 /* There's already a Save voice info dialog box; reactivate it. */
2660 reactivate_window(user_data->dlg.save_voice_as_w);
2664 /* XXX - use file_selection from dlg_utils instead! */
2665 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,
2666 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2667 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2671 /* Container for each row of widgets */
2672 vertb = gtk_vbox_new(FALSE, 0);
2673 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2674 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2675 gtk_widget_show (vertb);
2677 table1 = gtk_table_new (2, 4, FALSE);
2678 gtk_widget_show (table1);
2679 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2680 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2681 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2683 /*label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
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 label_format = gtk_label_new ("Format: ");
2690 gtk_widget_show (label_format);
2691 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2692 (GtkAttachOptions) (GTK_FILL),
2693 (GtkAttachOptions) (0), 0, 0);
2695 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2697 raw_rb = gtk_radio_button_new_with_label (format_group, ".raw");
2698 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (raw_rb));
2699 gtk_widget_show (raw_rb);
2700 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2701 (GtkAttachOptions) (GTK_FILL),
2702 (GtkAttachOptions) (0), 0, 0);
2705 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2706 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2707 gtk_widget_show (au_rb);
2708 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2709 (GtkAttachOptions) (GTK_FILL),
2710 (GtkAttachOptions) (0), 0, 0);
2712 /* we support .au - ulaw*/
2713 /* wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2714 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (wav_rb));
2715 gtk_widget_show (wav_rb);
2716 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2717 (GtkAttachOptions) (GTK_FILL),
2718 (GtkAttachOptions) (0), 0, 0);
2720 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2721 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (sw_rb));
2722 gtk_widget_show (sw_rb);
2723 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2724 (GtkAttachOptions) (GTK_FILL),
2725 (GtkAttachOptions) (0), 0, 0);
2726 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2727 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2728 gtk_widget_show (au_rb);
2729 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2730 (GtkAttachOptions) (GTK_FILL),
2731 (GtkAttachOptions) (0), 0, 0);
2735 channels_label = gtk_label_new ("Channels:");
2736 gtk_widget_show (channels_label);
2737 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2738 (GtkAttachOptions) (GTK_FILL),
2739 (GtkAttachOptions) (0), 0, 0);
2740 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2742 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2743 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2744 gtk_widget_show (forward_rb);
2745 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2746 (GtkAttachOptions) (GTK_FILL),
2747 (GtkAttachOptions) (0), 0, 0);
2749 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed");
2750 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2751 gtk_widget_show (reversed_rb);
2752 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2753 (GtkAttachOptions) (GTK_FILL),
2754 (GtkAttachOptions) (0), 0, 0);
2756 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2757 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2758 gtk_widget_show (both_rb);
2759 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2760 (GtkAttachOptions) (GTK_FILL),
2761 (GtkAttachOptions) (0), 0, 0);
2763 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2765 /* if one direction is nok we don't allow saving
2766 XXX this is not ok since the user can click the refresh button and cause changes
2767 but we can not update this window. So we move all the decision on the time the ok
2769 if (user_data->forward.saved == FALSE) {
2770 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2771 gtk_widget_set_sensitive(forward_rb, FALSE);
2772 gtk_widget_set_sensitive(both_rb, FALSE);
2774 else if (user_data->reversed.saved == FALSE) {
2775 gtk_widget_set_sensitive(reversed_rb, FALSE);
2776 gtk_widget_set_sensitive(both_rb, FALSE);
2780 /*g_object_set_data(G_OBJECT(ok_bt), "wav_rb", wav_rb);*/
2781 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2782 /*g_object_set_data(G_OBJECT(ok_bt), "sw_rb", sw_rb);*/
2783 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2784 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2785 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2786 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2787 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2789 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2790 G_CALLBACK(window_delete_event_cb), NULL);
2791 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2792 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2794 gtk_widget_show(user_data->dlg.save_voice_as_w);
2795 window_present(user_data->dlg.save_voice_as_w);
2797 if (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT){
2798 save_voice_as_ok_cb(user_data->dlg.save_voice_as_w, user_data->dlg.save_voice_as_w);
2800 window_destroy(user_data->dlg.save_voice_as_w);
2806 /****************************************************************************/
2807 /* when we are finished with redisection, we add the label for the statistic */
2808 static void draw_stat(user_data_t *user_data)
2810 gchar label_max[200];
2811 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
2812 - user_data->forward.statinfo.start_seq_nr + 1;
2813 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
2814 - user_data->reversed.statinfo.start_seq_nr + 1;
2815 gint32 f_lost = f_expected - user_data->forward.statinfo.total_nr;
2816 gint32 r_lost = r_expected - user_data->reversed.statinfo.total_nr;
2817 double f_perc, r_perc;
2819 f_perc = (double)(f_lost*100)/(double)f_expected;
2824 r_perc = (double)(r_lost*100)/(double)r_expected;
2829 g_snprintf(label_max, sizeof(label_max), "Max delta = %f ms at packet no. %u \n"
2830 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2831 " Sequence errors = %u",
2832 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
2833 user_data->forward.statinfo.total_nr, f_expected, f_lost, f_perc,
2834 user_data->forward.statinfo.sequence);
2836 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2838 g_snprintf(label_max, sizeof(label_max), "Max delta = %f ms at packet no. %u \n"
2839 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2840 " Sequence errors = %u",
2841 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
2842 user_data->reversed.statinfo.total_nr, r_expected, r_lost, r_perc,
2843 user_data->reversed.statinfo.sequence);
2845 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2852 /****************************************************************************/
2853 /* append a line to list */
2854 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num,
2855 double delta, double jitter, double bandwidth, gchar *status, gboolean marker,
2856 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
2858 GtkListStore *list_store;
2860 if (strcmp(status, OK_TEXT) != 0) {
2861 user_data->dlg.number_of_nok++;
2864 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
2866 /* Creates a new row at position. iter will be changed to point to this new row.
2867 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
2868 * The row will be filled with the values given to this function.
2870 * should generally be preferred when inserting rows in a sorted list store.
2872 #if GTK_CHECK_VERSION(2,6,0)
2873 gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
2875 gtk_list_store_append (list_store, &user_data->dlg.iter);
2876 gtk_list_store_set (list_store, &user_data->dlg.iter,
2878 PACKET_COLUMN, number,
2879 SEQUENCE_COLUMN, seq_num,
2880 DELTA_COLUMN, delta,
2881 JITTER_COLUMN, jitter,
2882 IPBW_COLUMN, bandwidth,
2883 MARKER_COLUMN, marker,
2884 STATUS_COLUMN, (char *)status,
2885 DATE_COLUMN, (char *)timeStr,
2886 LENGTH_COLUMN, pkt_len,
2887 FOREGROUND_COLOR_COL, NULL,
2888 BACKGROUND_COLOR_COL, (char *)color_str,
2891 if(flags & STAT_FLAG_FIRST){
2892 /* Set first row as active */
2893 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
2897 /****************************************************************************
2898 * Functions needed to present values from the list
2902 /* Present boolean value */
2904 rtp_boolean_data_func (GtkTreeViewColumn *column _U_,
2905 GtkCellRenderer *renderer,
2906 GtkTreeModel *model,
2912 /* the col to get data from is in userdata */
2913 gint bool_col = GPOINTER_TO_INT(user_data);
2915 gtk_tree_model_get(model, iter, bool_col, &bool_val, -1);
2919 g_strlcpy(buf, bool_val ? "SET" : "", sizeof(buf));
2922 g_assert_not_reached();
2925 g_object_set(renderer, "text", buf, NULL);
2930 GtkWidget* create_list(user_data_t* user_data)
2933 GtkListStore *list_store;
2935 GtkTreeViewColumn *column;
2936 GtkCellRenderer *renderer;
2937 GtkTreeSortable *sortable;
2938 GtkTreeView *list_view;
2939 GtkTreeSelection *selection;
2941 /* Create the store */
2942 list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX*/
2943 G_TYPE_UINT, /* Packet */
2944 G_TYPE_UINT, /* Sequence */
2945 G_TYPE_FLOAT, /* Delta(ms) */
2946 G_TYPE_FLOAT, /* Jitter(ms) */
2947 G_TYPE_FLOAT, /* IP BW(kbps) */
2948 G_TYPE_BOOLEAN, /* Marker */
2949 G_TYPE_STRING, /* Status */
2950 G_TYPE_STRING, /* Date */
2951 G_TYPE_UINT, /* Length */
2952 G_TYPE_STRING, /* Foreground color */
2953 G_TYPE_STRING); /* Background color */
2956 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
2958 list_view = GTK_TREE_VIEW(list);
2959 sortable = GTK_TREE_SORTABLE(list_store);
2961 #if GTK_CHECK_VERSION(2,6,0)
2962 /* Speed up the list display */
2963 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
2966 /* Setup the sortable columns */
2967 gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
2968 gtk_tree_view_set_headers_clickable(list_view, FALSE);
2970 /* The view now holds a reference. We can get rid of our own reference */
2971 g_object_unref (G_OBJECT (list_store));
2974 * Create the first column packet, associating the "text" attribute of the
2975 * cell_renderer to the first column of the model
2977 renderer = gtk_cell_renderer_text_new ();
2978 column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
2979 "text", PACKET_COLUMN,
2980 "foreground", FOREGROUND_COLOR_COL,
2981 "background", BACKGROUND_COLOR_COL,
2983 gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
2984 gtk_tree_view_column_set_resizable(column, TRUE);
2985 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
2986 gtk_tree_view_column_set_min_width(column, 100);
2988 /* Add the column to the view. */
2989 gtk_tree_view_append_column (list_view, column);
2991 /* Second column.. Sequence. */
2992 renderer = gtk_cell_renderer_text_new ();
2993 column = gtk_tree_view_column_new_with_attributes ("Sequence", renderer,
2994 "text", SEQUENCE_COLUMN,
2995 "foreground", FOREGROUND_COLOR_COL,
2996 "background", BACKGROUND_COLOR_COL,
2998 gtk_tree_view_column_set_sort_column_id(column, SEQUENCE_COLUMN);
2999 gtk_tree_view_column_set_resizable(column, TRUE);
3000 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3001 gtk_tree_view_column_set_min_width(column, 100);
3002 gtk_tree_view_append_column (list_view, column);
3004 /* Third column.. Delta(ms). */
3005 renderer = gtk_cell_renderer_text_new ();
3006 column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
3007 "text", DELTA_COLUMN,
3008 "foreground", FOREGROUND_COLOR_COL,
3009 "background", BACKGROUND_COLOR_COL,
3012 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3013 GINT_TO_POINTER(DELTA_COLUMN), NULL);
3015 gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
3016 gtk_tree_view_column_set_resizable(column, TRUE);
3017 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3018 gtk_tree_view_column_set_min_width(column, 100);
3019 gtk_tree_view_append_column (list_view, column);
3021 /* Forth column.. Jitter(ms). */
3022 renderer = gtk_cell_renderer_text_new ();
3023 column = gtk_tree_view_column_new_with_attributes ("Jitter(ms)", renderer,
3024 "text", JITTER_COLUMN,
3025 "foreground", FOREGROUND_COLOR_COL,
3026 "background", BACKGROUND_COLOR_COL,
3029 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3030 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3032 gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3033 gtk_tree_view_column_set_resizable(column, TRUE);
3034 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3035 gtk_tree_view_column_set_min_width(column, 100);
3036 gtk_tree_view_append_column (list_view, column);
3038 /* Fifth column.. IP BW(kbps). */
3039 renderer = gtk_cell_renderer_text_new ();
3040 column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
3041 "text", IPBW_COLUMN,
3042 "foreground", FOREGROUND_COLOR_COL,
3043 "background", BACKGROUND_COLOR_COL,
3046 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3047 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3049 gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3050 gtk_tree_view_column_set_resizable(column, TRUE);
3051 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3052 gtk_tree_view_column_set_min_width(column, 100);
3053 gtk_tree_view_append_column (list_view, column);
3055 /* Sixth column.. Marker. */
3056 renderer = gtk_cell_renderer_text_new ();
3057 column = gtk_tree_view_column_new_with_attributes ("Marker", renderer,
3058 "text", MARKER_COLUMN,
3059 "foreground", FOREGROUND_COLOR_COL,
3060 "background", BACKGROUND_COLOR_COL,
3063 gtk_tree_view_column_set_cell_data_func(column, renderer, rtp_boolean_data_func,
3064 GINT_TO_POINTER(MARKER_COLUMN), NULL);
3066 gtk_tree_view_column_set_sort_column_id(column, MARKER_COLUMN);
3067 gtk_tree_view_column_set_resizable(column, TRUE);
3068 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3069 gtk_tree_view_column_set_min_width(column, 75);
3070 gtk_tree_view_append_column (list_view, column);
3072 /* Seventh column.. Status. */
3073 renderer = gtk_cell_renderer_text_new ();
3074 column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3075 "text", STATUS_COLUMN,
3076 "foreground", FOREGROUND_COLOR_COL,
3077 "background", BACKGROUND_COLOR_COL,
3079 gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3080 gtk_tree_view_column_set_resizable(column, TRUE);
3081 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3082 gtk_tree_view_column_set_min_width(column, 100);
3083 gtk_tree_view_append_column (list_view, column);
3085 /* Now enable the sorting of each column */
3086 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3087 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3089 /* Setup the selection handler */
3090 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3091 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3093 g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3094 G_CALLBACK (on_list_select_row),
3099 /****************************************************************************/
3100 /* Create the dialog box with all widgets */
3101 static void create_rtp_dialog(user_data_t* user_data)
3103 GtkWidget *window = NULL;
3104 GtkWidget *list_fwd;
3105 GtkWidget *list_rev;
3106 GtkWidget *label_stats_fwd;
3107 GtkWidget *label_stats_rev;
3108 GtkWidget *notebook;
3110 GtkWidget *main_vb, *page, *page_r;
3112 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3113 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3114 #ifdef USE_CONVERSATION_GRAPH
3115 GtkWidget *graph_bt;
3117 GtkWidget *graph_bt;
3118 gchar label_forward[150];
3119 gchar label_forward_tree[150];
3120 gchar label_reverse[150];
3122 gchar str_ip_src[16];
3123 gchar str_ip_dst[16];
3125 window = window_new(GTK_WINDOW_TOPLEVEL, "Wireshark: RTP Stream Analysis");
3126 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3128 /* Container for each row of widgets */
3129 main_vb = gtk_vbox_new(FALSE, 2);
3130 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3131 gtk_container_add(GTK_CONTAINER(window), main_vb);
3132 gtk_widget_show(main_vb);
3135 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), sizeof(str_ip_src));
3136 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), sizeof(str_ip_dst));
3138 g_snprintf(label_forward, sizeof(label_forward),
3139 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3140 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3142 g_snprintf(label_forward_tree, sizeof(label_forward_tree),
3143 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3144 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3147 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), sizeof(str_ip_src));
3148 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), sizeof(str_ip_dst));
3150 g_snprintf(label_reverse, sizeof(label_reverse),
3151 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3152 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3154 /* Start a notebook for flipping between sets of changes */
3155 notebook = gtk_notebook_new();
3156 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3157 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3159 user_data->dlg.notebook_signal_id =
3160 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3162 /* page for forward connection */
3163 page = gtk_vbox_new(FALSE, 8);
3164 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3166 /* direction label */
3167 label = gtk_label_new(label_forward);
3168 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3170 /* place for some statistics */
3171 label_stats_fwd = gtk_label_new("\n");
3172 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3174 /* scrolled window */
3175 scrolled_window = scrolled_window_new(NULL, NULL);
3178 list_fwd = create_list(user_data);
3179 gtk_widget_show(list_fwd);
3180 gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3181 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3182 gtk_widget_show(scrolled_window);
3185 label = gtk_label_new(" Forward Direction ");
3186 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3188 /* same page for reversed connection */
3189 page_r = gtk_vbox_new(FALSE, 8);
3190 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3191 label = gtk_label_new(label_reverse);
3192 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3193 label_stats_rev = gtk_label_new("\n");
3194 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3196 scrolled_window_r = scrolled_window_new(NULL, NULL);
3198 list_rev = create_list(user_data);
3199 gtk_widget_show(list_rev);
3200 gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3201 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3202 gtk_widget_show(scrolled_window_r);
3204 label = gtk_label_new(" Reversed Direction ");
3205 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3207 /* page for help&about or future
3208 page_help = gtk_hbox_new(FALSE, 5);
3209 label = gtk_label_new(" Future ");
3210 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3211 frame = gtk_frame_new("");
3212 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3213 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3214 gtk_container_add(GTK_CONTAINER(frame), text);
3215 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3216 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3219 /* show all notebooks */
3220 gtk_widget_show_all(notebook);
3223 box4 = gtk_hbutton_box_new();
3224 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3225 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3226 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3227 gtk_box_set_spacing(GTK_BOX (box4), 0);
3228 gtk_widget_show(box4);
3230 voice_bt = gtk_button_new_with_label("Save payload...");
3231 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3232 gtk_widget_show(voice_bt);
3233 g_signal_connect(voice_bt, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
3235 csv_bt = gtk_button_new_with_label("Save as CSV...");
3236 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3237 gtk_widget_show(csv_bt);
3238 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3240 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3241 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3242 gtk_widget_show(refresh_bt);
3243 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3245 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3246 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3247 gtk_widget_show(goto_bt);
3248 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked_lst), user_data);
3250 graph_bt = gtk_button_new_with_label("Graph");
3251 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3252 gtk_widget_show(graph_bt);
3253 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3256 #ifdef USE_CONVERSATION_GRAPH
3257 graph_bt = gtk_button_new_with_label("Graph");
3258 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3259 gtk_widget_show(graph_bt);
3260 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3263 next_bt = gtk_button_new_with_label("Next non-Ok");
3264 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3265 gtk_widget_show(next_bt);
3266 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked_list), user_data);
3268 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3269 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3270 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3271 gtk_widget_show(close_bt);
3272 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3274 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3275 g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3277 gtk_widget_show(window);
3278 window_present(window);
3281 /* some widget references need to be saved for outside use */
3282 user_data->dlg.window = window;
3283 user_data->dlg.list_fwd = list_fwd;
3284 user_data->dlg.list_rev = list_rev;
3285 user_data->dlg.label_stats_fwd = label_stats_fwd;
3286 user_data->dlg.label_stats_rev = label_stats_rev;
3287 user_data->dlg.notebook = notebook;
3288 user_data->dlg.selected_list = list_fwd;
3289 user_data->dlg.number_of_nok = 0;
3292 * select the initial row
3294 gtk_widget_grab_focus(list_fwd);
3299 /****************************************************************************/
3300 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3301 const gchar* proto_field, guint32* p_result)
3304 proto_node *proto_sibling_node;
3305 header_field_info *hfssrc;
3308 finfo = PITEM_FINFO(ptree_node);
3310 if (hfinformation==(finfo->hfinfo)) {
3311 hfssrc = proto_registrar_get_byname(proto_field);
3314 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3315 ptree_node=ptree_node->next) {
3316 finfo=PITEM_FINFO(ptree_node);
3317 if (hfssrc==finfo->hfinfo) {
3318 if (hfinformation->type==FT_IPv4) {
3319 ipv4 = fvalue_get(&finfo->value);
3320 *p_result = ipv4_get_net_order_addr(ipv4);
3323 *p_result = fvalue_get_uinteger(&finfo->value);
3332 proto_sibling_node = ptree_node->next;
3334 if (proto_sibling_node) {
3335 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3341 /****************************************************************************/
3342 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3343 const gchar* proto_name,
3344 const gchar* proto_field,
3347 proto_node *ptree_node;
3348 header_field_info *hfinformation;
3350 hfinformation = proto_registrar_get_byname(proto_name);
3351 if (hfinformation == NULL)
3354 ptree_node = ((proto_node *)protocol_tree)->first_child;
3358 return process_node(ptree_node, hfinformation, proto_field, p_result);
3362 /****************************************************************************/
3364 address *ip_src_fwd,
3365 guint16 port_src_fwd,
3366 address *ip_dst_fwd,
3367 guint16 port_dst_fwd,
3369 address *ip_src_rev,
3370 guint16 port_src_rev,
3371 address *ip_dst_rev,
3372 guint16 port_dst_rev,
3376 user_data_t *user_data;
3379 static color_t col[MAX_GRAPHS] = {
3380 {0, 0x0000, 0x0000, 0x0000},
3381 {0, 0xffff, 0x0000, 0x0000},
3382 {0, 0x0000, 0xffff, 0x0000},
3383 {0, 0x0000, 0x0000, 0xffff}
3387 user_data = g_malloc(sizeof(user_data_t));
3389 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3390 user_data->port_src_fwd = port_src_fwd;
3391 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3392 user_data->port_dst_fwd = port_dst_fwd;
3393 user_data->ssrc_fwd = ssrc_fwd;
3394 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3395 user_data->port_src_rev = port_src_rev;
3396 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3397 user_data->port_dst_rev = port_dst_rev;
3398 user_data->ssrc_rev = ssrc_rev;
3401 /* file names for storing sound data */
3402 /*XXX: check for errors*/
3403 fd = create_tempfile(user_data->f_tempname, sizeof(user_data->f_tempname),
3406 fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_tempname),
3409 user_data->forward.saveinfo.fp = NULL;
3410 user_data->reversed.saveinfo.fp = NULL;
3411 user_data->dlg.save_voice_as_w = NULL;
3412 user_data->dlg.save_csv_as_w = NULL;
3413 user_data->dlg.dialog_graph.window = NULL;
3415 #ifdef USE_CONVERSATION_GRAPH
3416 user_data->dlg.graph_window = NULL;
3417 user_data->series_fwd.value_pairs = NULL;
3418 user_data->series_rev.value_pairs = NULL;
3421 /* init dialog_graph */
3422 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3423 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3424 user_data->dlg.dialog_graph.draw_area=NULL;
3425 user_data->dlg.dialog_graph.pixmap=NULL;
3426 user_data->dlg.dialog_graph.scrollbar=NULL;
3427 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3428 user_data->dlg.dialog_graph.pixmap_width=500;
3429 user_data->dlg.dialog_graph.pixmap_height=200;
3430 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3431 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3432 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3433 user_data->dlg.dialog_graph.max_interval=0;
3434 user_data->dlg.dialog_graph.num_items=0;
3435 user_data->dlg.dialog_graph.start_time = -1;
3437 for(i=0;i<MAX_GRAPHS;i++){
3438 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3439 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3440 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3441 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3442 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3443 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3444 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3445 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3448 /* create the dialog box */
3449 create_rtp_dialog(user_data);
3451 /* proceed as if the Refresh button would have been pressed */
3452 on_refresh_bt_clicked(NULL, user_data);
3455 /****************************************************************************/
3456 /* entry point from main menu */
3457 static void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3460 guint16 port_src_fwd;
3462 guint16 port_dst_fwd;
3463 guint32 ssrc_fwd = 0;
3465 guint16 port_src_rev;
3467 guint16 port_dst_rev;
3468 guint32 ssrc_rev = 0;
3469 unsigned int version_fwd;
3471 gchar filter_text[256];
3474 epan_dissect_t *edt;
3477 gboolean frame_matched;
3479 GList *strinfo_list;
3480 GList *filtered_list = NULL;
3481 rtp_stream_info_t *strinfo;
3484 /* Try to compile the filter. */
3485 g_strlcpy(filter_text,"rtp && rtp.version && rtp.ssrc && (ip || ipv6)",sizeof(filter_text));
3486 if (!dfilter_compile(filter_text, &sfcode)) {
3487 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3490 /* we load the current file into cf variable */
3492 fdata = cf->current_frame;
3494 /* we are on the selected frame now */
3496 return; /* if we exit here it's an error */
3498 /* dissect the current frame */
3499 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3500 cf->pd, fdata->cap_len, &err, &err_info)) {
3501 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3502 cf_read_error_message(err, err_info), cf->filename);
3505 edt = epan_dissect_new(TRUE, FALSE);
3506 epan_dissect_prime_dfilter(edt, sfcode);
3507 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3508 frame_matched = dfilter_apply_edt(sfcode, edt);
3510 /* if it is not an rtp frame, show the rtpstream dialog */
3511 frame_matched = dfilter_apply_edt(sfcode, edt);
3512 if (frame_matched != 1) {
3513 epan_dissect_free(edt);
3514 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3515 "You didn't choose a RTP packet!");
3519 /* ok, it is a RTP frame, so let's get the ip and port values */
3520 COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
3521 COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
3522 port_src_fwd = edt->pi.srcport;
3523 port_dst_fwd = edt->pi.destport;
3525 /* assume the inverse ip/port combination for the reverse direction */
3526 COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
3527 COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
3528 port_src_rev = edt->pi.destport;
3529 port_dst_rev = edt->pi.srcport;
3531 /* check if it is RTP Version 2 */
3532 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3533 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3534 "RTP Version != 2 isn't supported!");
3538 /* now we need the SSRC value of the current frame */
3539 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3540 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3541 "SSRC value couldn't be found!");
3545 /* Scan for rtpstream */
3547 /* search for reversed direction in the global rtp streams list */
3549 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3550 while (strinfo_list)
3552 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3553 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3554 && strinfo->src_port==port_src_fwd
3555 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3556 && strinfo->dest_port==port_dst_fwd)
3558 filtered_list = g_list_prepend(filtered_list, strinfo);
3561 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3562 && strinfo->src_port==port_src_rev
3563 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3564 && strinfo->dest_port==port_dst_rev)
3567 filtered_list = g_list_append(filtered_list, strinfo);
3569 ssrc_rev = strinfo->ssrc;
3572 strinfo_list = g_list_next(strinfo_list);
3575 /* if more than one reverse streams found, we let the user choose the right one */
3577 rtpstream_dlg_show(filtered_list);
3596 /****************************************************************************/
3598 rtp_analysis_init(const char *dummy _U_,void* userdata _U_)
3600 rtp_analysis_cb(NULL, NULL);
3603 /****************************************************************************/
3605 register_tap_listener_rtp_analysis(void)
3607 register_stat_cmd_arg("rtp", rtp_analysis_init,NULL);
3609 register_stat_menu_item("RTP/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3610 rtp_analysis_cb, NULL, NULL, NULL);