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.
55 #include <epan/epan_dissect.h>
56 #include <epan/filesystem.h>
57 #include <epan/pint.h>
59 #include <epan/dissectors/packet-rtp.h>
60 #include <epan/rtp_pt.h>
61 #include <epan/addr_resolv.h>
62 #include <epan/stat_cmd_args.h>
63 #include <epan/strutil.h>
66 #include "../register.h"
68 #include "../alert_box.h"
69 #include "../simple_dialog.h"
70 #include "../stat_menu.h"
71 #include "../progress_dlg.h"
73 #include "../tempfile.h"
74 #include <wsutil/file_util.h>
76 #include "gtk/gtkglobals.h"
77 #include "gtk/dlg_utils.h"
78 #include "gtk/file_dlg.h"
79 #include "gtk/gui_utils.h"
80 #include "gtk/gui_stat_menu.h"
82 #include "gtk/rtp_analysis.h"
83 #include "gtk/rtp_stream.h"
84 #include "gtk/rtp_stream_dlg.h"
100 BACKGROUND_COLOR_COL,
101 N_COLUMN /* The number of columns */
103 /****************************************************************************/
106 #define NUM_GRAPH_ITEMS 100000
107 #define MAX_YSCALE 16
108 #define AUTO_MAX_YSCALE 0
110 #define GRAPH_FWD_JITTER 0
111 #define GRAPH_FWD_DIFF 1
112 #define GRAPH_REV_JITTER 2
113 #define GRAPH_REV_DIFF 3
114 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
116 #define MAX_PIXELS_PER_TICK 4
117 #define DEFAULT_PIXELS_PER_TICK 1
118 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
119 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
121 #define MAX_TICK_VALUES 5
122 #define DEFAULT_TICK_VALUE 1
123 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
124 typedef struct _dialog_graph_graph_item_t {
127 } dialog_graph_graph_item_t;
129 typedef struct _dialog_graph_graph_t {
130 struct _user_data_t *ud;
131 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
134 GtkWidget *display_button;
139 } dialog_graph_graph_t;
142 typedef struct _dialog_graph_t {
143 gboolean needs_redraw;
144 gint32 interval; /* measurement interval in ms */
145 guint32 last_interval;
146 guint32 max_interval; /* XXX max_interval and num_items are redundant */
148 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
150 GtkWidget *draw_area;
152 GtkAdjustment *scrollbar_adjustment;
153 GtkWidget *scrollbar;
161 typedef struct _dialog_data_t {
166 GtkWidget *label_stats_fwd;
167 GtkWidget *label_stats_rev;
168 GtkWidget *selected_list;
170 GtkTreeSelection *selected_list_sel;
171 gint selected_list_row;
173 GtkWidget *save_voice_as_w;
174 GtkWidget *save_csv_as_w;
175 gint notebook_signal_id;
176 dialog_graph_t dialog_graph;
179 #define OK_TEXT "[ Ok ]"
181 /* type of error when saving voice in a file didn't succeed */
184 TAP_RTP_WRONG_LENGTH,
185 TAP_RTP_PADDING_ERROR,
187 TAP_RTP_FILE_OPEN_ERROR,
191 typedef struct _tap_rtp_save_info_t {
194 error_type_t error_type;
196 } tap_rtp_save_info_t;
199 /* structure that holds the information about the forward and reversed direction */
200 struct _info_direction {
201 tap_rtp_stat_t statinfo;
202 tap_rtp_save_info_t saveinfo;
205 #define SILENCE_PCMU (guint8)0xFF
206 #define SILENCE_PCMA (guint8)0x55
208 /* structure that holds general information about the connection
209 * and structures for both directions */
210 typedef struct _user_data_t {
211 /* tap associated data*/
213 guint16 port_src_fwd;
215 guint16 port_dst_fwd;
218 guint16 port_src_rev;
220 guint16 port_dst_rev;
223 struct _info_direction forward;
224 struct _info_direction reversed;
229 /* dialog associated data */
236 static const gchar *titles[11] = {
250 #define SAVE_FORWARD_DIRECTION_MASK 0x01
251 #define SAVE_REVERSE_DIRECTION_MASK 0x02
252 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
254 #define SAVE_NONE_FORMAT 0
255 #define SAVE_WAV_FORMAT 1
256 #define SAVE_AU_FORMAT 2
257 #define SAVE_SW_FORMAT 3
258 #define SAVE_RAW_FORMAT 4
261 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data);
262 /****************************************************************************/
263 static void enable_graph(dialog_graph_graph_t *dgg)
270 static void dialog_graph_reset(user_data_t* user_data);
274 /****************************************************************************/
277 /****************************************************************************/
278 /* when there is a [re]reading of packet's */
280 rtp_reset(void *user_data_arg)
282 user_data_t *user_data = user_data_arg;
283 user_data->forward.statinfo.first_packet = TRUE;
284 user_data->reversed.statinfo.first_packet = TRUE;
285 user_data->forward.statinfo.max_delta = 0;
286 user_data->reversed.statinfo.max_delta = 0;
287 user_data->forward.statinfo.max_jitter = 0;
288 user_data->reversed.statinfo.max_jitter = 0;
289 user_data->forward.statinfo.max_skew = 0;
290 user_data->reversed.statinfo.max_skew = 0;
291 user_data->forward.statinfo.mean_jitter = 0;
292 user_data->reversed.statinfo.mean_jitter = 0;
293 user_data->forward.statinfo.delta = 0;
294 user_data->reversed.statinfo.delta = 0;
295 user_data->forward.statinfo.diff = 0;
296 user_data->reversed.statinfo.diff = 0;
297 user_data->forward.statinfo.jitter = 0;
298 user_data->reversed.statinfo.jitter = 0;
299 user_data->forward.statinfo.skew = 0;
300 user_data->reversed.statinfo.skew = 0;
301 user_data->forward.statinfo.sumt = 0;
302 user_data->reversed.statinfo.sumt = 0;
303 user_data->forward.statinfo.sumTS = 0;
304 user_data->reversed.statinfo.sumTS = 0;
305 user_data->forward.statinfo.sumt2 = 0;
306 user_data->reversed.statinfo.sumt2 = 0;
307 user_data->forward.statinfo.sumtTS = 0;
308 user_data->reversed.statinfo.sumtTS = 0;
309 user_data->forward.statinfo.bandwidth = 0;
310 user_data->reversed.statinfo.bandwidth = 0;
311 user_data->forward.statinfo.total_bytes = 0;
312 user_data->reversed.statinfo.total_bytes = 0;
313 user_data->forward.statinfo.bw_start_index = 0;
314 user_data->reversed.statinfo.bw_start_index = 0;
315 user_data->forward.statinfo.bw_index = 0;
316 user_data->reversed.statinfo.bw_index = 0;
317 user_data->forward.statinfo.timestamp = 0;
318 user_data->reversed.statinfo.timestamp = 0;
319 user_data->forward.statinfo.max_nr = 0;
320 user_data->reversed.statinfo.max_nr = 0;
321 user_data->forward.statinfo.total_nr = 0;
322 user_data->reversed.statinfo.total_nr = 0;
323 user_data->forward.statinfo.sequence = 0;
324 user_data->reversed.statinfo.sequence = 0;
325 user_data->forward.statinfo.start_seq_nr = 0;
326 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
327 user_data->forward.statinfo.stop_seq_nr = 0;
328 user_data->reversed.statinfo.stop_seq_nr = 0;
329 user_data->forward.statinfo.cycles = 0;
330 user_data->reversed.statinfo.cycles = 0;
331 user_data->forward.statinfo.under = FALSE;
332 user_data->reversed.statinfo.under = FALSE;
333 user_data->forward.statinfo.start_time = 0;
334 user_data->reversed.statinfo.start_time = 0;
335 user_data->forward.statinfo.time = 0;
336 user_data->reversed.statinfo.time = 0;
337 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
338 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
340 user_data->forward.saveinfo.count = 0;
341 user_data->reversed.saveinfo.count = 0;
342 user_data->forward.saveinfo.saved = FALSE;
343 user_data->reversed.saveinfo.saved = FALSE;
345 /* clear the dialog box lists */
346 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
347 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
349 /* reset graph info */
350 dialog_graph_reset(user_data);
352 /* XXX check for error at fclose? */
353 if (user_data->forward.saveinfo.fp != NULL)
354 fclose(user_data->forward.saveinfo.fp);
355 if (user_data->reversed.saveinfo.fp != NULL)
356 fclose(user_data->reversed.saveinfo.fp);
357 user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
358 if (user_data->forward.saveinfo.fp == NULL)
359 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
360 user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
361 if (user_data->reversed.saveinfo.fp == NULL)
362 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
366 /****************************************************************************/
367 static int rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
369 dialog_graph_graph_item_t *it;
373 /* we sometimes get called when dgg is disabled.
374 this is a bug since the tap listener should be removed first */
379 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
382 * Find which interval this is supposed to to in and store the
383 * interval index as idx
385 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
386 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
388 rtp_time = nstime_to_sec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
392 idx = (guint32)(rtp_time*1000)/dgg->ud->dlg.dialog_graph.interval;
394 /* some sanity checks */
395 if((idx<0)||(idx>=NUM_GRAPH_ITEMS)){
399 /* update num_items */
400 if((guint32)idx > dgg->ud->dlg.dialog_graph.num_items){
401 dgg->ud->dlg.dialog_graph.num_items=idx;
402 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
406 * Find the appropriate dialog_graph_graph_item_t structure
411 * Use the max value to highlight RTP problems
413 if (value > it->value) {
416 it->flags = it->flags | statinfo->flags;
421 /****************************************************************************/
422 /* here we can redraw the output */
424 static void rtp_draw(void *prs _U_)
429 /* forward declarations */
430 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
431 double delta, double jitter, double skew ,double bandwidth, gchar *status, gboolean marker,
432 gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
434 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
435 tap_rtp_stat_t *statinfo, packet_info *pinfo,
436 const struct _rtp_info *rtpinfo);
438 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
439 tap_rtp_stat_t *statinfo,
441 const struct _rtp_info *rtpinfo);
444 /****************************************************************************/
445 /* whenever a RTP packet is seen by the tap listener */
446 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
448 user_data_t *user_data = user_data_arg;
449 const struct _rtp_info *rtpinfo = rtpinfo_arg;
451 /* we ignore packets that are not displayed */
452 if (pinfo->fd->flags.passed_dfilter == 0)
454 /* also ignore RTP Version != 2 */
455 else if (rtpinfo->info_version !=2)
457 /* is it the forward direction? */
458 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
459 && CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
460 && user_data->port_src_fwd == pinfo->srcport
461 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
462 && user_data->port_dst_fwd == pinfo->destport) {
463 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
464 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));
465 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));
466 rtp_packet_add_info(user_data->dlg.list_fwd, user_data,
467 &(user_data->forward.statinfo), pinfo, rtpinfo);
468 rtp_packet_save_payload(&(user_data->forward.saveinfo),
469 &(user_data->forward.statinfo), pinfo, rtpinfo);
471 /* is it the reversed direction? */
472 else if (user_data->ssrc_rev == rtpinfo->info_sync_src
473 && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
474 && user_data->port_src_rev == pinfo->srcport
475 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
476 && user_data->port_dst_rev == pinfo->destport) {
477 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
478 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));
479 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));
480 rtp_packet_add_info(user_data->dlg.list_rev, user_data,
481 &(user_data->reversed.statinfo), pinfo, rtpinfo);
482 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
483 &(user_data->reversed.statinfo), pinfo, rtpinfo);
490 Replaced by using the strings instead.
491 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
492 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
493 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
494 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
495 COLOR_T_EVENT g_snprintf(color_str,sizeof(color_str),"#ef8c bfff ffff");
496 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
498 /****************************************************************************/
499 /* adds statistics information from the packet to the list */
500 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
501 tap_rtp_stat_t *statinfo, packet_info *pinfo,
502 const struct _rtp_info *rtpinfo)
510 then = pinfo->fd->abs_ts.secs;
511 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
512 tm_tmp = localtime(&then);
513 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
516 tm_tmp->tm_year + 1900,
522 /* Default to using black on white text if nothing below overrides it */
523 g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
525 if (statinfo->pt == PT_CN) {
526 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
527 /* color = COLOR_CN; */
528 g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
530 else if (statinfo->pt == PT_CN_OLD) {
531 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
532 /* color = COLOR_CN; */
533 g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
535 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
536 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
537 /* color = COLOR_ERROR; */
538 g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
540 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
541 if (statinfo->flags & STAT_FLAG_PT_T_EVENT){
542 g_snprintf(status,sizeof(status),"Payload changed to PT=%u telephone/event", statinfo->pt);
544 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
546 /* color = COLOR_WARNING; */
547 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
549 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
550 g_snprintf(status,sizeof(status),"Incorrect timestamp");
551 /* color = COLOR_WARNING; */
552 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
554 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
555 && !(statinfo->flags & STAT_FLAG_FIRST)
556 && !(statinfo->flags & STAT_FLAG_PT_CN)
557 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
558 && !(statinfo->flags & STAT_FLAG_MARKER)) {
559 g_snprintf(status,sizeof(status),"Marker missing?");
560 /* color = COLOR_WARNING; */
561 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
562 }else if (statinfo->flags & STAT_FLAG_PT_T_EVENT){
563 g_snprintf(status,sizeof(status),"PT=%u telephone/event", statinfo->pt);
565 /* color = COLOR_T_EVENT; */
566 g_snprintf(color_str,sizeof(color_str),"#ef8cbfffffff");
568 if (statinfo->flags & STAT_FLAG_MARKER) {
569 /* color = COLOR_WARNING; */
570 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
572 g_snprintf(status,sizeof(status),OK_TEXT);
574 /* is this the first packet we got in this direction? */
575 if (statinfo->flags & STAT_FLAG_FIRST) {
576 add_to_list(list, user_data,
577 pinfo->fd->num, rtpinfo->info_seq_num,
584 rtpinfo->info_marker_set,
585 timeStr, pinfo->fd->pkt_len,
590 add_to_list(list, user_data,
591 pinfo->fd->num, rtpinfo->info_seq_num,
598 rtpinfo->info_marker_set,
599 timeStr, pinfo->fd->pkt_len,
606 #define MAX_SILENCE_TICKS 1000000
607 /****************************************************************************/
608 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
609 tap_rtp_stat_t *statinfo,
611 const struct _rtp_info *rtpinfo)
618 /* is this the first packet we got in this direction? */
619 if (statinfo->flags & STAT_FLAG_FIRST) {
620 if (saveinfo->fp == NULL) {
621 saveinfo->saved = FALSE;
622 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
625 saveinfo->saved = TRUE;
628 /* save the voice information */
629 /* if there was already an error, we quit */
630 if (saveinfo->saved == FALSE)
633 /* if the captured length and packet length aren't equal, we quit
634 * if also the RTP dissector thinks there is some information missing */
635 if ((pinfo->fd->pkt_len != pinfo->fd->cap_len) &&
636 (!rtpinfo->info_all_data_present)) {
637 saveinfo->saved = FALSE;
638 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
642 /* if padding bit is set, but the padding count is bigger
643 * then the whole RTP data - error with padding count */
644 if ( (rtpinfo->info_padding_set != FALSE) &&
645 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
646 saveinfo->saved = FALSE;
647 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
651 /* do we need to insert some silence? */
652 if ((rtpinfo->info_marker_set) &&
653 !(statinfo->flags & STAT_FLAG_FIRST) &&
654 !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) &&
655 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
656 /* the amount of silence should be the difference between
657 * the last timestamp and the current one minus x
658 * x should equal the amount of information in the last frame
659 * XXX not done yet */
660 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
661 rtpinfo->info_padding_count) && i < MAX_SILENCE_TICKS; i++) {
662 switch (statinfo->reg_pt) {
673 nchars=fwrite(&tmp, 1, 1, saveinfo->fp);
676 fflush(saveinfo->fp);
680 if (rtpinfo->info_payload_type == PT_CN
681 || rtpinfo->info_payload_type == PT_CN_OLD) {
683 /*all other payloads*/
685 if (!rtpinfo->info_all_data_present) {
686 /* Not all the data was captured. */
687 saveinfo->saved = FALSE;
688 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
692 /* we put the pointer at the beginning of the RTP
693 * payload, that is, at the beginning of the RTP data
694 * plus the offset of the payload from the beginning
696 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
697 nchars=fwrite(data, sizeof(unsigned char), (rtpinfo->info_payload_len - rtpinfo->info_padding_count), saveinfo->fp);
698 saveinfo->count+=(rtpinfo->info_payload_len - rtpinfo->info_padding_count);
700 fflush(saveinfo->fp);
701 saveinfo->saved = TRUE;
709 /****************************************************************************/
712 /****************************************************************************/
714 /****************************************************************************/
715 /* close the dialog window and remove the tap listener */
716 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data)
718 /* remove tap listener */
719 protect_thread_critical_region();
720 remove_tap_listener(user_data);
721 unprotect_thread_critical_region();
723 /* close and remove temporary files */
724 if (user_data->forward.saveinfo.fp != NULL)
725 fclose(user_data->forward.saveinfo.fp);
726 if (user_data->reversed.saveinfo.fp != NULL)
727 fclose(user_data->reversed.saveinfo.fp);
728 /*XXX: test for error **/
729 ws_remove(user_data->f_tempname);
730 ws_remove(user_data->r_tempname);
732 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
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 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
742 g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
744 g_free(user_data->f_tempname);
745 g_free(user_data->r_tempname);
750 /****************************************************************************/
751 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
752 GtkNotebookPage *page _U_,
754 user_data_t *user_data _U_)
756 user_data->dlg.selected_list =
757 (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
759 user_data->dlg.selected_list_row = 0;
762 /****************************************************************************/
764 static void on_list_select_row(GtkTreeSelection *selection,
765 user_data_t *user_data/*gpointer data */)
767 user_data->dlg.selected_list_sel = selection;
771 /****************************************************************************/
772 static void dialog_graph_set_title(user_data_t* user_data)
775 if (!user_data->dlg.dialog_graph.window){
778 title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
779 get_addr_name(&(user_data->ip_src_fwd)),
780 user_data->port_src_fwd,
781 get_addr_name(&(user_data->ip_dst_fwd)),
782 user_data->port_dst_fwd,
783 get_addr_name(&(user_data->ip_src_rev)),
784 user_data->port_src_rev,
785 get_addr_name(&(user_data->ip_dst_rev)),
786 user_data->port_dst_rev);
788 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
794 /****************************************************************************/
795 static void dialog_graph_reset(user_data_t* user_data)
799 user_data->dlg.dialog_graph.needs_redraw=TRUE;
800 for(i=0;i<MAX_GRAPHS;i++){
801 for(j=0;j<NUM_GRAPH_ITEMS;j++){
802 dialog_graph_graph_item_t *dggi;
803 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
808 user_data->dlg.dialog_graph.last_interval=0xffffffff;
809 user_data->dlg.dialog_graph.max_interval=0;
810 user_data->dlg.dialog_graph.num_items=0;
812 /* create the color titles near the filter buttons */
813 for(i=0;i<MAX_GRAPHS;i++){
816 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
817 sizeof(user_data->dlg.dialog_graph.graph[0].title),
818 "%s: %s:%u to %s:%u (SSRC=0x%X)",
820 get_addr_name(&(user_data->ip_src_fwd)),
821 user_data->port_src_fwd,
822 get_addr_name(&(user_data->ip_dst_fwd)),
823 user_data->port_dst_fwd,
824 user_data->ssrc_fwd);
827 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
828 sizeof(user_data->dlg.dialog_graph.graph[0].title),
829 "%s: %s:%u to %s:%u (SSRC=0x%X)",
831 get_addr_name(&(user_data->ip_src_rev)),
832 user_data->port_src_rev,
833 get_addr_name(&(user_data->ip_dst_rev)),
834 user_data->port_dst_rev,
835 user_data->ssrc_rev);
839 dialog_graph_set_title(user_data);
842 /****************************************************************************/
843 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
845 dialog_graph_graph_item_t *it;
852 /****************************************************************************/
853 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
856 g_snprintf(buf, buf_len, "%ds",t/1000000);
857 } else if(t>=1000000){
858 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
860 g_snprintf(buf, buf_len, "%dms",t/1000);
862 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
864 g_snprintf(buf, buf_len, "%dus",t);
868 /****************************************************************************/
869 static void dialog_graph_draw(user_data_t* user_data)
872 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
873 gint32 current_interval;
874 guint32 left_x_border;
875 guint32 right_x_border;
876 guint32 top_y_border;
877 guint32 bottom_y_border;
879 int label_width, label_height;
880 guint32 draw_width, draw_height;
881 char label_string[15];
884 guint32 num_time_intervals;
885 guint32 max_value; /* max value of seen data */
886 guint32 max_y; /* max value of the Y scale */
888 if(!user_data->dlg.dialog_graph.needs_redraw){
891 user_data->dlg.dialog_graph.needs_redraw=FALSE;
894 * Find the length of the intervals we have data for
895 * so we know how large arrays we need to malloc()
897 num_time_intervals=user_data->dlg.dialog_graph.num_items;
898 /* if there isnt anything to do, just return */
899 if(num_time_intervals==0){
902 num_time_intervals+=1;
903 /* XXX move this check to _packet() */
904 if(num_time_intervals>NUM_GRAPH_ITEMS){
905 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
910 * find the max value so we can autoscale the y axis
913 for(i=0;i<MAX_GRAPHS;i++){
916 if(!user_data->dlg.dialog_graph.graph[i].display){
919 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
922 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
924 /* keep track of the max value we have encountered */
934 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
935 user_data->dlg.dialog_graph.draw_area->style->white_gc,
938 user_data->dlg.dialog_graph.draw_area->allocation.width,
939 user_data->dlg.dialog_graph.draw_area->allocation.height);
943 * Calculate the y scale we should use
945 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
946 max_y=yscale_max[MAX_YSCALE-1];
947 for(i=MAX_YSCALE-1;i>0;i--){
948 if(max_value<yscale_max[i]){
953 /* the user had specified an explicit y scale to use */
954 max_y=user_data->dlg.dialog_graph.max_y_units;
958 * Calculate size of borders surrounding the plot
959 * The border on the right side needs to be adjusted depending
960 * on the width of the text labels. For simplicity we assume that the
961 * top y scale label will be the widest one
963 print_time_scale_string(label_string, sizeof(label_string), max_y);
964 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
965 pango_layout_get_pixel_size(layout, &label_width, &label_height);
967 right_x_border=label_width+20;
969 bottom_y_border=label_height+20;
973 * Calculate the size of the drawing area for the actual plot
975 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
976 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
980 * Draw the y axis and labels
981 * (we always draw the y scale with 11 ticks along the axis)
983 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
984 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
986 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
987 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
993 /* first, middle and last tick are slightly longer */
997 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
998 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
999 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1000 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1001 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1002 /* draw the labels */
1004 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1005 pango_layout_set_text(layout, label_string, -1);
1006 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1007 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1008 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1009 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1010 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1014 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1015 pango_layout_set_text(layout, label_string, -1);
1016 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1017 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1018 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1019 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1020 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1024 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1025 pango_layout_set_text(layout, label_string, -1);
1026 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1027 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1028 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1029 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1030 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1038 * if we have not specified the last_interval via the gui,
1039 * then just pick the current end of the capture so that is scrolls
1040 * nicely when doing live captures
1042 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1043 last_interval=user_data->dlg.dialog_graph.max_interval;
1045 last_interval=user_data->dlg.dialog_graph.last_interval;
1052 /* plot the x-scale */
1053 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);
1055 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1056 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1057 first_interval*=user_data->dlg.dialog_graph.interval;
1064 while(interval_delta<((last_interval-first_interval)/10)){
1065 interval_delta*=delta_multiplier;
1066 if(delta_multiplier==5){
1073 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1076 /* if pixels_per_tick is <5, only draw every 10 ticks */
1077 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1081 if(current_interval%interval_delta){
1087 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1088 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1089 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1090 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1091 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1092 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1096 if(user_data->dlg.dialog_graph.interval>=1000){
1097 g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
1098 } else if(user_data->dlg.dialog_graph.interval>=100){
1099 g_snprintf(label_string, sizeof(label_string), "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1100 } else if(user_data->dlg.dialog_graph.interval>=10){
1101 g_snprintf(label_string, sizeof(label_string), "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1103 g_snprintf(label_string, sizeof(label_string), "%d.%3ds", current_interval/1000,current_interval%1000);
1105 pango_layout_set_text(layout, label_string, -1);
1106 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1107 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1108 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1109 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1110 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1122 * Draw "x" for Sequence Errors and "m" for Marks
1124 /* Draw the labels Fwd and Rev */
1125 g_strlcpy(label_string,"<-Fwd",sizeof(label_string));
1126 pango_layout_set_text(layout, label_string, -1);
1127 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1128 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1129 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1130 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1131 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1133 g_strlcpy(label_string,"<-Rev",sizeof(label_string));
1134 pango_layout_set_text(layout, label_string, -1);
1135 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1136 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1137 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1138 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1139 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1142 /* Draw the marks */
1143 for(i=MAX_GRAPHS-1;i>=0;i--){
1145 guint32 x_pos, prev_x_pos;
1147 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1148 if (!user_data->dlg.dialog_graph.graph[i].display){
1151 /* initialize prev x/y to the low left corner of the graph */
1152 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;
1154 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1155 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;
1157 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1159 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1160 g_strlcpy(label_string,"x",sizeof(label_string));
1162 g_strlcpy(label_string,"m",sizeof(label_string));
1165 pango_layout_set_text(layout, label_string, -1);
1166 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1167 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1168 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1170 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1178 g_object_unref(G_OBJECT(layout));
1181 * Loop over all graphs and draw them
1183 for(i=MAX_GRAPHS-1;i>=0;i--){
1185 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1186 if (!user_data->dlg.dialog_graph.graph[i].display){
1189 /* initialize prev x/y to the low left corner of the graph */
1190 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;
1191 prev_y_pos=draw_height-1+top_y_border;
1193 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1195 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;
1196 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1200 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1203 /* dont need to draw anything if the segment
1204 * is entirely above the top of the graph
1206 if( (prev_y_pos==0) && (y_pos==0) ){
1213 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1214 x_pos, draw_height-1+top_y_border,
1224 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1225 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1226 user_data->dlg.dialog_graph.pixmap,
1229 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1232 /* update the scrollbar */
1233 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1234 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1235 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1236 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1237 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1239 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1241 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1242 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1243 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1247 /****************************************************************************/
1248 static void dialog_graph_redraw(user_data_t* user_data)
1250 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1251 dialog_graph_draw(user_data);
1254 /****************************************************************************/
1255 static void quit(GtkWidget *widget _U_, user_data_t *user_data)
1257 user_data->dlg.dialog_graph.window = NULL;
1260 /****************************************************************************/
1261 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1263 user_data_t *user_data;
1265 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1271 gdk_draw_pixmap(widget->window,
1272 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1273 user_data->dlg.dialog_graph.pixmap,
1274 event->area.x, event->area.y,
1275 event->area.x, event->area.y,
1276 event->area.width, event->area.height);
1281 /****************************************************************************/
1282 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1284 user_data_t *user_data;
1287 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1293 if(user_data->dlg.dialog_graph.pixmap){
1294 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1295 user_data->dlg.dialog_graph.pixmap=NULL;
1298 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1299 widget->allocation.width,
1300 widget->allocation.height,
1302 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1303 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1305 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1306 widget->style->white_gc,
1309 widget->allocation.width,
1310 widget->allocation.height);
1312 /* set up the colors and the GC structs for this pixmap */
1313 for(i=0;i<MAX_GRAPHS;i++){
1314 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1315 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1318 dialog_graph_redraw(user_data);
1322 /****************************************************************************/
1323 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1325 user_data_t *user_data=(user_data_t *)data;
1328 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1329 if(user_data->dlg.dialog_graph.last_interval==mi){
1332 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1333 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1337 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1339 dialog_graph_redraw(user_data);
1343 /****************************************************************************/
1344 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1346 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1347 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1348 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.draw_area), "user_data_t", user_data);
1350 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);
1352 /* signals needed to handle backing pixmap */
1353 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1354 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1356 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1357 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1359 /* create the associated scrollbar */
1360 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1361 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1362 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1363 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1364 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1367 /****************************************************************************/
1368 static void disable_graph(dialog_graph_graph_t *dgg)
1372 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1377 /****************************************************************************/
1378 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1380 /* this graph is not active, just update display and redraw */
1381 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1383 dialog_graph_redraw(dgg->ud);
1388 cf_retap_packets(&cfile);
1389 dialog_graph_redraw(dgg->ud);
1394 /****************************************************************************/
1395 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1401 hbox=gtk_hbox_new(FALSE, 3);
1402 gtk_container_add(GTK_CONTAINER(box), hbox);
1403 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1404 gtk_widget_show(hbox);
1406 g_snprintf(str, sizeof(str), "Graph %d", num);
1407 dgg->display_button=gtk_toggle_button_new_with_label(str);
1408 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1409 gtk_widget_show(dgg->display_button);
1410 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1411 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1413 label=gtk_label_new(dgg->title);
1414 gtk_widget_show(label);
1415 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1417 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1418 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1419 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1420 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1421 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1426 /****************************************************************************/
1427 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1434 frame=gtk_frame_new("Graphs");
1435 gtk_container_add(GTK_CONTAINER(box), frame);
1436 gtk_widget_show(frame);
1438 vbox=gtk_vbox_new(FALSE, 1);
1439 gtk_container_add(GTK_CONTAINER(frame), vbox);
1440 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1441 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1442 gtk_widget_show(vbox);
1444 for(i=0;i<MAX_GRAPHS;i++){
1445 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1448 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1449 gtk_widget_show(label);
1450 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1455 /****************************************************************************/
1456 static void yscale_select(GtkWidget *item, gpointer key)
1459 user_data_t *user_data;
1461 user_data=(user_data_t *)key;
1462 val=(long)g_object_get_data(G_OBJECT(item), "yscale_max");
1464 user_data->dlg.dialog_graph.max_y_units=val;
1465 dialog_graph_redraw(user_data);
1468 /****************************************************************************/
1469 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1472 user_data_t *user_data;
1474 user_data=(user_data_t *)key;
1475 val=(long)g_object_get_data(G_OBJECT(item), "pixels_per_tick");
1476 user_data->dlg.dialog_graph.pixels_per_tick=val;
1477 dialog_graph_redraw(user_data);
1480 /****************************************************************************/
1481 static void tick_interval_select(GtkWidget *item, gpointer key)
1484 user_data_t *user_data;
1486 user_data=(user_data_t *)key;
1487 val=(long)g_object_get_data(G_OBJECT(item), "tick_interval");
1489 user_data->dlg.dialog_graph.interval=val;
1490 cf_retap_packets(&cfile);
1491 dialog_graph_redraw(user_data);
1494 /****************************************************************************/
1495 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1498 GtkWidget *menu_item;
1501 for(i=0;i<MAX_YSCALE;i++){
1502 if(yscale_max[i]==AUTO_MAX_YSCALE){
1503 g_strlcpy(str,"Auto",sizeof(str));
1505 g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1507 menu_item=gtk_menu_item_new_with_label(str);
1508 g_object_set_data(G_OBJECT(menu_item), "yscale_max",
1509 GUINT_TO_POINTER(yscale_max[i]));
1510 g_signal_connect(menu_item, "activate", G_CALLBACK(yscale_select), user_data);
1511 gtk_widget_show(menu_item);
1512 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1517 /****************************************************************************/
1518 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
1521 GtkWidget *menu_item;
1524 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1525 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1526 menu_item=gtk_menu_item_new_with_label(str);
1528 g_object_set_data(G_OBJECT(menu_item), "pixels_per_tick",
1529 GUINT_TO_POINTER(pixels_per_tick[i]));
1530 g_signal_connect(menu_item, "activate", G_CALLBACK(pixels_per_tick_select), user_data);
1531 gtk_widget_show(menu_item);
1532 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1534 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1539 /****************************************************************************/
1540 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
1543 GtkWidget *menu_item;
1546 for(i=0;i<MAX_TICK_VALUES;i++){
1547 if(tick_interval_values[i]>=1000){
1548 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1549 } else if(tick_interval_values[i]>=100){
1550 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1551 } else if(tick_interval_values[i]>=10){
1552 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1554 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1557 menu_item=gtk_menu_item_new_with_label(str);
1558 g_object_set_data(G_OBJECT(menu_item), "tick_interval",
1559 GUINT_TO_POINTER(tick_interval_values[i]));
1560 g_signal_connect(menu_item, "activate", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1561 gtk_widget_show(menu_item);
1562 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1564 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1568 /****************************************************************************/
1569 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
1573 GtkWidget *option_menu;
1576 hbox=gtk_hbox_new(FALSE, 0);
1577 gtk_container_add(GTK_CONTAINER(box), hbox);
1578 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1579 gtk_widget_show(hbox);
1581 label=gtk_label_new(name);
1582 gtk_widget_show(label);
1583 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1585 option_menu=gtk_option_menu_new();
1586 menu=gtk_menu_new();
1587 (*func)(user_data, menu);
1588 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1589 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1590 gtk_widget_show(option_menu);
1593 /****************************************************************************/
1594 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1596 GtkWidget *frame_vbox;
1600 frame_vbox=gtk_vbox_new(FALSE, 0);
1601 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1602 gtk_widget_show(frame_vbox);
1604 frame = gtk_frame_new("X Axis");
1605 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1606 gtk_widget_show(frame);
1608 vbox=gtk_vbox_new(FALSE, 0);
1609 gtk_container_add(GTK_CONTAINER(frame), vbox);
1610 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1611 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1612 gtk_widget_show(vbox);
1614 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1615 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1617 frame = gtk_frame_new("Y Axis");
1618 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1619 gtk_widget_show(frame);
1621 vbox=gtk_vbox_new(FALSE, 0);
1622 gtk_container_add(GTK_CONTAINER(frame), vbox);
1623 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1624 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1625 gtk_widget_show(vbox);
1627 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1632 /****************************************************************************/
1633 static void dialog_graph_init_window(user_data_t* user_data)
1637 GtkWidget *bt_close;
1639 /* create the main window */
1640 user_data->dlg.dialog_graph.window=dlg_window_new("I/O Graphs"); /* transient_for top_level */
1642 vbox=gtk_vbox_new(FALSE, 0);
1643 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1644 gtk_widget_show(vbox);
1646 create_draw_area(user_data, vbox);
1648 hbox=gtk_hbox_new(FALSE, 3);
1649 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1650 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1651 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1652 gtk_widget_show(hbox);
1654 create_filter_area(user_data, hbox);
1655 create_ctrl_area(user_data, hbox);
1657 dialog_graph_set_title(user_data);
1659 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1660 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1661 gtk_widget_show(hbox);
1663 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1664 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1666 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1668 gtk_widget_show(user_data->dlg.dialog_graph.window);
1669 window_present(user_data->dlg.dialog_graph.window);
1674 /****************************************************************************/
1675 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1677 if (user_data->dlg.dialog_graph.window != NULL) {
1678 /* There's already a graph window; reactivate it. */
1679 reactivate_window(user_data->dlg.dialog_graph.window);
1683 dialog_graph_init_window(user_data);
1687 /****************************************************************************/
1689 static void on_goto_bt_clicked_lst(GtkWidget *bt _U_, user_data_t *user_data _U_)
1692 GtkTreeModel *model;
1693 GtkTreeSelection *selection;
1696 selection = user_data->dlg.selected_list_sel;
1698 if (selection==NULL)
1701 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1702 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1703 cf_goto_frame(&cfile, fnumber);
1708 static void draw_stat(user_data_t *user_data);
1710 /****************************************************************************/
1711 /* re-dissects all packets */
1712 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1714 GString *error_string;
1716 /* remove tap listener */
1717 protect_thread_critical_region();
1718 remove_tap_listener(user_data);
1719 unprotect_thread_critical_region();
1721 /* register tap listener */
1722 error_string = register_tap_listener("rtp", user_data, NULL, 0,
1723 rtp_reset, rtp_packet, rtp_draw);
1724 if (error_string != NULL) {
1725 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1726 g_string_free(error_string, TRUE);
1730 /* retap all packets */
1731 cf_retap_packets(&cfile);
1733 /* draw statistics info */
1734 draw_stat(user_data);
1738 static void on_next_bt_clicked_list(GtkWidget *bt _U_, user_data_t *user_data _U_)
1741 GtkTreeModel *model;
1743 GtkTreeSelection *selection;
1746 selection = user_data->dlg.selected_list_sel;
1748 if (selection==NULL)
1752 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1753 while (gtk_tree_model_iter_next (model,&iter)) {
1754 gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1755 if (strcmp(text, OK_TEXT) != 0) {
1756 gtk_tree_selection_select_iter (selection, &iter);
1757 path = gtk_tree_model_get_path(model, &iter);
1758 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1761 gtk_tree_path_free(path);
1768 if (user_data->dlg.number_of_nok>1){
1769 /* Get the first iter and select it before starting over */
1770 gtk_tree_model_get_iter_first(model, &iter);
1771 gtk_tree_selection_select_iter (selection, &iter);
1778 /****************************************************************************/
1779 /* when we want to save the information */
1780 static gboolean save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
1783 GtkWidget *rev, *forw, *both;
1784 user_data_t *user_data;
1786 GtkListStore *store;
1788 GtkTreeModel *model;
1789 gboolean more_items = TRUE;
1791 /* To Hold data from the list row */
1792 guint32 packet; /* Packet */
1793 guint16 sequence; /* Sequence */
1794 guint32 timestamp; /* timestamp */
1795 gfloat delta; /* Delta(ms) */
1796 gfloat jitter; /* Jitter(ms) */
1797 gfloat skew; /* Skew(ms) */
1798 gfloat ipbw; /* IP BW(kbps) */
1799 gboolean marker; /* Marker */
1800 char * status_str; /* Status */
1801 char * date_str; /* Date */
1802 guint length; /* Length */
1808 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
1810 /* Perhaps the user specified a directory instead of a file.
1811 * Check whether they did.
1813 if (test_for_directory(g_dest) == EISDIR) {
1814 /* It's a directory - set the file selection box to display it. */
1815 set_last_open_dir(g_dest);
1817 file_selection_set_current_folder(fc, get_last_open_dir());
1818 gtk_file_chooser_set_current_name(fc, "");
1819 return FALSE; /* run the dialog again */
1822 rev = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
1823 forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
1824 both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
1825 user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
1827 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
1828 fp = ws_fopen(g_dest, "w");
1830 open_failure_alert_box(g_dest, errno, TRUE);
1832 return TRUE; /* we're done */
1835 if (GTK_TOGGLE_BUTTON(both)->active) {
1836 fprintf(fp, "Forward\n");
1838 write_failure_alert_box(g_dest, errno);
1841 return TRUE; /* we're done */
1845 for(j = 0; j < NUM_COLS; j++) {
1847 fprintf(fp,"%s",titles[j]);
1849 fprintf(fp,",%s",titles[j]);
1854 write_failure_alert_box(g_dest, errno);
1859 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
1860 store = GTK_LIST_STORE(model);
1861 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
1864 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1865 PACKET_COLUMN, &packet,
1866 SEQUENCE_COLUMN, &sequence,
1867 TIMESTAMP_COLUMN, ×tamp,
1868 DELTA_COLUMN, &delta,
1869 JITTER_COLUMN, &jitter,
1872 MARKER_COLUMN, &marker,
1873 STATUS_COLUMN, &status_str,
1874 DATE_COLUMN, &date_str,
1875 LENGTH_COLUMN, &length,
1877 fprintf(fp, "%u",packet);
1878 fprintf(fp, ",%u", sequence);
1879 fprintf(fp, ",%u", timestamp);
1880 fprintf(fp, ",%.2f", delta);
1881 fprintf(fp, ",%.2f", jitter);
1882 fprintf(fp, ",%.2f", skew);
1883 fprintf(fp, ",%.2f", ipbw);
1884 fprintf(fp, ",%s", marker? "SET" : "");
1885 fprintf(fp, ",%s", status_str);
1886 fprintf(fp, ",%s", date_str);
1887 fprintf(fp, ",%u", length);
1892 write_failure_alert_box(g_dest, errno);
1895 return TRUE; /* we're done */
1898 more_items = gtk_tree_model_iter_next (model,&iter);
1902 if (fclose(fp) == EOF) {
1903 write_failure_alert_box(g_dest, errno);
1905 return TRUE; /* we're done */
1909 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
1911 if (GTK_TOGGLE_BUTTON(both)->active) {
1912 fp = ws_fopen(g_dest, "a");
1914 open_failure_alert_box(g_dest, errno, TRUE);
1916 return TRUE; /* we're done */
1918 fprintf(fp, "\nReverse\n");
1920 write_failure_alert_box(g_dest, errno);
1923 return TRUE; /* we're done */
1926 fp = ws_fopen(g_dest, "w");
1928 open_failure_alert_box(g_dest, errno, TRUE);
1930 return TRUE; /* we're done */
1933 for(j = 0; j < NUM_COLS; j++) {
1935 fprintf(fp,"%s",titles[j]);
1937 fprintf(fp,",%s",titles[j]);
1942 write_failure_alert_box(g_dest, errno);
1945 return TRUE; /* we're done */
1947 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
1948 store = GTK_LIST_STORE(model);
1949 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
1954 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1955 PACKET_COLUMN, &packet,
1956 SEQUENCE_COLUMN, &sequence,
1957 TIMESTAMP_COLUMN, ×tamp,
1958 DELTA_COLUMN, &delta,
1959 JITTER_COLUMN, &jitter,
1962 MARKER_COLUMN, &marker,
1963 STATUS_COLUMN, &status_str,
1964 DATE_COLUMN, &date_str,
1965 LENGTH_COLUMN, &length,
1967 fprintf(fp, "%u",packet);
1968 fprintf(fp, ",%u", sequence);
1969 fprintf(fp, ",%u", timestamp);
1970 fprintf(fp, ",%.2f", delta);
1971 fprintf(fp, ",%.2f", jitter);
1972 fprintf(fp, ",%.2f", skew);
1973 fprintf(fp, ",%.2f", ipbw);
1974 fprintf(fp, ",%s", marker? "SET" : "");
1975 fprintf(fp, ",%s", status_str);
1976 fprintf(fp, ",%s", date_str);
1977 fprintf(fp, ",%u", length);
1982 write_failure_alert_box(g_dest, errno);
1985 return TRUE; /* we're done */
1988 more_items = gtk_tree_model_iter_next (model,&iter);
1991 if (fclose(fp) == EOF) {
1992 write_failure_alert_box(g_dest, errno);
1994 return TRUE; /* we're done */
1999 return TRUE; /* we're done */
2002 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2004 user_data->dlg.save_csv_as_w = NULL;
2007 /* when the user wants to save the csv information in a file */
2008 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
2012 GtkWidget *label_format;
2013 GtkWidget *channels_label;
2014 GSList *channels_group = NULL;
2015 GtkWidget *forward_rb;
2016 GtkWidget *reversed_rb;
2019 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2020 if (user_data->dlg.save_csv_as_w != NULL) {
2021 /* There's already a Save CSV info dialog box; reactivate it. */
2022 reactivate_window(user_data->dlg.save_csv_as_w);
2026 user_data->dlg.save_csv_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2027 GTK_WINDOW(user_data->dlg.notebook),
2028 GTK_FILE_CHOOSER_ACTION_SAVE,
2029 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2030 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2032 #if GTK_CHECK_VERSION(2,8,0)
2033 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
2035 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_csv_as_w),GTK_WINDOW(user_data->dlg.window));
2037 /* Container for each row of widgets */
2038 vertb = gtk_vbox_new(FALSE, 0);
2039 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2040 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2041 gtk_widget_show (vertb);
2043 table1 = gtk_table_new (2, 4, FALSE);
2044 gtk_widget_show (table1);
2045 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2046 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2047 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2049 label_format = gtk_label_new ("Format: Comma Separated Values");
2050 gtk_widget_show (label_format);
2051 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2052 (GtkAttachOptions) (GTK_FILL),
2053 (GtkAttachOptions) (0), 0, 0);
2055 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2058 channels_label = gtk_label_new ("Channels: ");
2059 gtk_widget_show (channels_label);
2060 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2061 (GtkAttachOptions) (GTK_FILL),
2062 (GtkAttachOptions) (0), 0, 0);
2063 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2065 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2066 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2067 gtk_widget_show (forward_rb);
2068 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2069 (GtkAttachOptions) (GTK_FILL),
2070 (GtkAttachOptions) (0), 0, 0);
2072 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed ");
2073 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2074 gtk_widget_show (reversed_rb);
2075 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2076 (GtkAttachOptions) (GTK_FILL),
2077 (GtkAttachOptions) (0), 0, 0);
2079 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2080 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2081 gtk_widget_show (both_rb);
2082 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2083 (GtkAttachOptions) (GTK_FILL),
2084 (GtkAttachOptions) (0), 0, 0);
2086 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2088 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2089 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2090 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2091 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2093 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2094 G_CALLBACK(window_delete_event_cb), NULL);
2095 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2096 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2098 gtk_widget_show(user_data->dlg.save_csv_as_w);
2099 window_present(user_data->dlg.save_csv_as_w);
2101 /* "Run" the GtkFileChooserDialog. */
2102 /* Upon exit: If "Accept" run the OK callback. */
2103 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2104 /* Destroy the window. */
2105 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2106 /* return with a TRUE status so that the dialog window will be destroyed. */
2107 /* Trying to re-run the dialog after popping up an alert box will not work */
2108 /* since the user will not be able to dismiss the alert box. */
2109 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2110 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2112 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2113 /* GtkFileChooserDialog. */
2114 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
2115 if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
2116 break; /* we're done */
2119 window_destroy(user_data->dlg.save_csv_as_w);
2123 /****************************************************************************/
2124 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2126 /* Note that we no longer have a Save voice info dialog box. */
2127 user_data->dlg.save_voice_as_w = NULL;
2130 /****************************************************************************/
2131 /* here we save it into a file that user specified */
2132 /* XXX what about endians here? could go something wrong? */
2133 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2135 FILE *to_stream, *forw_stream, *rev_stream;
2136 size_t fwritten, rwritten;
2137 int f_rawvalue, r_rawvalue, rawvalue;
2140 guint32 f_write_silence = 0;
2141 guint32 r_write_silence = 0;
2143 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2144 gboolean stop_flag = FALSE;
2147 forw_stream = ws_fopen(user_data->f_tempname, "rb");
2148 if (forw_stream == NULL)
2150 rev_stream = ws_fopen(user_data->r_tempname, "rb");
2151 if (rev_stream == NULL) {
2152 fclose(forw_stream);
2156 /* open file for saving */
2157 to_stream = ws_fopen(dest, "wb");
2158 if (to_stream == NULL) {
2159 fclose(forw_stream);
2164 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2166 if (format == SAVE_AU_FORMAT) /* au format */
2168 /* First we write the .au header. XXX Hope this is endian independant */
2169 /* the magic word 0x2e736e64 == .snd */
2170 phtonl(pd, 0x2e736e64);
2171 nchars=fwrite(pd, 1, 4, to_stream);
2172 /* header offset == 24 bytes */
2174 nchars=fwrite(pd, 1, 4, to_stream);
2175 /* total length; it is permitted to set this to 0xffffffff */
2177 nchars=fwrite(pd, 1, 4, to_stream);
2178 /* encoding format == 16-bit linear PCM */
2180 nchars=fwrite(pd, 1, 4, to_stream);
2181 /* sample rate == 8000 Hz */
2183 nchars=fwrite(pd, 1, 4, to_stream);
2186 nchars=fwrite(pd, 1, 4, to_stream);
2190 /* only forward direction */
2191 case SAVE_FORWARD_DIRECTION_MASK: {
2192 progbar_count = user_data->forward.saveinfo.count;
2193 progbar_quantum = user_data->forward.saveinfo.count/100;
2194 while ((f_rawvalue = getc(forw_stream)) != EOF) {
2197 if((count > progbar_nextstep) && (count <= progbar_count)) {
2198 update_progress_dlg(progbar,
2199 (gfloat) count/progbar_count, "Saving");
2200 progbar_nextstep = progbar_nextstep + progbar_quantum;
2204 if (user_data->forward.statinfo.pt == PT_PCMU){
2205 sample = ulaw2linear((unsigned char)f_rawvalue);
2208 else if(user_data->forward.statinfo.pt == PT_PCMA){
2209 sample = alaw2linear((unsigned char)f_rawvalue);
2213 fclose(forw_stream);
2216 destroy_progress_dlg(progbar);
2220 fwritten = fwrite(pd, 1, 2, to_stream);
2222 fclose(forw_stream);
2225 destroy_progress_dlg(progbar);
2231 /* only reversed direction */
2232 case SAVE_REVERSE_DIRECTION_MASK: {
2233 progbar_count = user_data->reversed.saveinfo.count;
2234 progbar_quantum = user_data->reversed.saveinfo.count/100;
2235 while ((r_rawvalue = getc(rev_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->reversed.statinfo.pt == PT_PCMU){
2246 sample = ulaw2linear((unsigned char)r_rawvalue);
2249 else if(user_data->reversed.statinfo.pt == PT_PCMA){
2250 sample = alaw2linear((unsigned char)r_rawvalue);
2254 fclose(forw_stream);
2257 destroy_progress_dlg(progbar);
2261 rwritten = fwrite(pd, 1, 2, to_stream);
2263 fclose(forw_stream);
2266 destroy_progress_dlg(progbar);
2272 /* both directions */
2273 case SAVE_BOTH_DIRECTION_MASK: {
2274 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2275 (progbar_count = user_data->forward.saveinfo.count) :
2276 (progbar_count = user_data->reversed.saveinfo.count);
2277 progbar_quantum = progbar_count/100;
2278 /* since conversation in one way can start later than in the other one,
2279 * we have to write some silence information for one channel */
2280 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2281 f_write_silence = (guint32)
2282 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2284 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2285 r_write_silence = (guint32)
2286 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2291 if((count > progbar_nextstep) && (count <= progbar_count)) {
2292 update_progress_dlg(progbar,
2293 (gfloat) count/progbar_count, "Saving");
2294 progbar_nextstep = progbar_nextstep + progbar_quantum;
2297 if(f_write_silence > 0) {
2298 r_rawvalue = getc(rev_stream);
2299 switch (user_data->forward.statinfo.reg_pt) {
2301 f_rawvalue = SILENCE_PCMU;
2304 f_rawvalue = SILENCE_PCMA;
2312 else if(r_write_silence > 0) {
2313 f_rawvalue = getc(forw_stream);
2314 switch (user_data->reversed.statinfo.reg_pt) {
2316 r_rawvalue = SILENCE_PCMU;
2319 r_rawvalue = SILENCE_PCMA;
2328 f_rawvalue = getc(forw_stream);
2329 r_rawvalue = getc(rev_stream);
2331 if ((r_rawvalue == EOF) && (f_rawvalue == EOF))
2333 if ((user_data->forward.statinfo.pt == PT_PCMU) && (user_data->reversed.statinfo.pt == PT_PCMU)){
2334 sample = (ulaw2linear((unsigned char)r_rawvalue) + ulaw2linear((unsigned char)f_rawvalue)) / 2;
2337 else if((user_data->forward.statinfo.pt == PT_PCMA) && (user_data->reversed.statinfo.pt == PT_PCMA)){
2338 sample = (alaw2linear((unsigned char)r_rawvalue) + alaw2linear((unsigned char)f_rawvalue)) / 2;
2343 fclose(forw_stream);
2346 destroy_progress_dlg(progbar);
2351 rwritten = fwrite(pd, 1, 2, to_stream);
2353 fclose(forw_stream);
2356 destroy_progress_dlg(progbar);
2363 else if (format == SAVE_RAW_FORMAT) /* raw format */
2367 /* only forward direction */
2368 case SAVE_FORWARD_DIRECTION_MASK: {
2369 progbar_count = user_data->forward.saveinfo.count;
2370 progbar_quantum = user_data->forward.saveinfo.count/100;
2371 stream = forw_stream;
2374 /* only reversed direction */
2375 case SAVE_REVERSE_DIRECTION_MASK: {
2376 progbar_count = user_data->reversed.saveinfo.count;
2377 progbar_quantum = user_data->reversed.saveinfo.count/100;
2378 stream = rev_stream;
2382 fclose(forw_stream);
2385 destroy_progress_dlg(progbar);
2392 /* XXX how do you just copy the file? */
2393 while ((rawvalue = getc(stream)) != EOF) {
2396 if((count > progbar_nextstep) && (count <= progbar_count)) {
2397 update_progress_dlg(progbar,
2398 (gfloat) count/progbar_count, "Saving");
2399 progbar_nextstep = progbar_nextstep + progbar_quantum;
2403 if (putc(rawvalue, to_stream) == EOF) {
2404 fclose(forw_stream);
2407 destroy_progress_dlg(progbar);
2413 destroy_progress_dlg(progbar);
2414 fclose(forw_stream);
2421 /****************************************************************************/
2422 /* the user wants to save in a file */
2423 /* XXX support for different formats is currently commented out */
2424 static gboolean save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2427 /*GtkWidget *wav, *sw;*/
2428 GtkWidget *au, *raw;
2429 GtkWidget *rev, *forw, *both;
2430 user_data_t *user_data;
2431 gint channels, format;
2433 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2435 /* Perhaps the user specified a directory instead of a file.
2436 * Check whether they did.
2438 if (test_for_directory(g_dest) == EISDIR) {
2439 /* It's a directory - set the file selection box to display it. */
2440 set_last_open_dir(g_dest);
2442 file_selection_set_current_folder(fc, get_last_open_dir());
2443 gtk_file_chooser_set_current_name(fc, "");
2444 return FALSE; /* run the dialog again */
2448 wav = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2449 sw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2451 au = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
2452 raw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
2453 rev = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2454 forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
2455 both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
2456 user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
2458 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2459 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2460 * disable the ok button or disable the buttons for direction if only one is not ok. The
2461 * problem is if we open the save voice dialog and then click the refresh button and maybe
2462 * the state changes, so we can't save anymore. In this case we should be able to update
2463 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2466 /* we can not save in both directions */
2467 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2468 /* there are many combinations here, we just exit when first matches */
2469 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2470 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2471 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2472 "Can't save in a file: Unsupported codec!");
2473 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2474 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2475 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2476 "Can't save in a file: Wrong length of captured packets!");
2477 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2478 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2479 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2480 "Can't save in a file: RTP data with padding!");
2481 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2482 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2483 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2484 "Can't save in a file: Not all data in all packets was captured!");
2486 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2487 "Can't save in a file: File I/O problem!");
2489 return TRUE; /* we're done */
2491 /* we can not save forward direction */
2492 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2493 (GTK_TOGGLE_BUTTON (both)->active))) {
2494 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2495 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2496 "Can't save forward direction in a file: Unsupported codec!");
2497 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2498 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2499 "Can't save forward direction in a file: Wrong length of captured packets!");
2500 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2501 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2502 "Can't save forward direction in a file: RTP data with padding!");
2503 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2504 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2505 "Can't save forward direction in a file: Not all data in all packets was captured!");
2507 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2508 "Can't save forward direction in a file: File I/O problem!");
2510 return TRUE; /* we're done */
2512 /* we can not save reversed direction */
2513 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2514 (GTK_TOGGLE_BUTTON (both)->active))) {
2515 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2516 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2517 "Can't save reversed direction in a file: Unsupported codec!");
2518 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2519 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2520 "Can't save reversed direction in a file: Wrong length of captured packets!");
2521 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2522 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2523 "Can't save reversed direction in a file: RTP data with padding!");
2524 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2525 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2526 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2527 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2528 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2529 "Can't save reversed direction in a file: No RTP data!");
2531 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2532 "Can't save reversed direction in a file: File I/O problem!");
2534 return TRUE; /* we're done */
2538 if (GTK_TOGGLE_BUTTON (wav)->active)
2539 format = SAVE_WAV_FORMAT;
2542 if (GTK_TOGGLE_BUTTON (au)->active)
2543 format = SAVE_AU_FORMAT;
2545 else if (GTK_TOGGLE_BUTTON (sw)->active)
2546 format = SAVE_SW_FORMAT;
2548 else if (GTK_TOGGLE_BUTTON (raw)->active)
2549 format = SAVE_RAW_FORMAT;
2551 format = SAVE_NONE_FORMAT;
2553 if (GTK_TOGGLE_BUTTON (rev)->active)
2554 channels = SAVE_REVERSE_DIRECTION_MASK;
2555 else if (GTK_TOGGLE_BUTTON (both)->active)
2556 channels = SAVE_BOTH_DIRECTION_MASK;
2558 channels = SAVE_FORWARD_DIRECTION_MASK;
2560 /* direction/format validity*/
2561 if (format == SAVE_AU_FORMAT)
2563 /* make sure streams are alaw/ulaw */
2564 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != PT_PCMA) && (user_data->forward.statinfo.pt != PT_PCMU)){
2565 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2566 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2568 return TRUE; /* we're done */
2570 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != PT_PCMA) && (user_data->reversed.statinfo.pt != PT_PCMU)){
2571 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2572 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2574 return TRUE; /* we're done */
2576 /* make sure pt's don't differ */
2577 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2578 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2579 "Can't save in a file: Forward and reverse direction differ in type");
2581 return TRUE; /* we're done */
2584 else if (format == SAVE_RAW_FORMAT)
2586 /* can't save raw in both directions */
2587 if (channels == SAVE_BOTH_DIRECTION_MASK){
2588 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2589 "Can't save in a file: Unable to save raw data in both directions");
2591 return TRUE; /* we're done */
2596 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2597 "Can't save in a file: Invalid save format");
2599 return TRUE; /* we're done */
2602 if(!copy_file(g_dest, channels, format, user_data)) {
2603 /* XXX - report the error type! */
2604 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2605 "An error occurred while saving voice in a file!");
2607 return TRUE; /* we're done */
2611 return TRUE; /* we're done */
2614 /****************************************************************************/
2615 /* when the user wants to save the voice information in a file */
2616 /* XXX support for different formats is currently commented out */
2617 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
2621 GtkWidget *label_format;
2622 GtkWidget *channels_label;
2623 GSList *format_group = NULL;
2624 GSList *channels_group = NULL;
2625 GtkWidget *forward_rb;
2626 GtkWidget *reversed_rb;
2628 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2632 /* if we can't save in a file: wrong codec, cut packets or other errors */
2633 /* Should the error arise here or later when you click ok button ?
2634 * if we do it here, then we must disable the refresh button, so we don't do it here
2637 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2638 if (user_data->dlg.save_voice_as_w != NULL) {
2639 /* There's already a Save voice info dialog box; reactivate it. */
2640 reactivate_window(user_data->dlg.save_voice_as_w);
2644 /* XXX - use file_selection from dlg_utils instead! */
2645 user_data->dlg.save_voice_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2646 GTK_WINDOW(user_data->dlg.notebook),
2647 GTK_FILE_CHOOSER_ACTION_SAVE,
2648 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2649 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2651 #if GTK_CHECK_VERSION(2,8,0)
2652 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
2654 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_voice_as_w),GTK_WINDOW(user_data->dlg.window));
2656 /* Container for each row of widgets */
2657 vertb = gtk_vbox_new(FALSE, 0);
2658 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2659 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2660 gtk_widget_show (vertb);
2662 table1 = gtk_table_new (2, 4, FALSE);
2663 gtk_widget_show (table1);
2664 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2665 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2666 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2668 /*label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2669 gtk_widget_show (label_format);
2670 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2671 (GtkAttachOptions) (GTK_FILL),
2672 (GtkAttachOptions) (0), 0, 0);*/
2674 label_format = gtk_label_new ("Format: ");
2675 gtk_widget_show (label_format);
2676 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2677 (GtkAttachOptions) (GTK_FILL),
2678 (GtkAttachOptions) (0), 0, 0);
2680 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2682 raw_rb = gtk_radio_button_new_with_label (format_group, ".raw");
2683 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (raw_rb));
2684 gtk_widget_show (raw_rb);
2685 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2686 (GtkAttachOptions) (GTK_FILL),
2687 (GtkAttachOptions) (0), 0, 0);
2690 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2691 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2692 gtk_widget_show (au_rb);
2693 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2694 (GtkAttachOptions) (GTK_FILL),
2695 (GtkAttachOptions) (0), 0, 0);
2698 /* we support .au - ulaw*/
2699 wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2700 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (wav_rb));
2701 gtk_widget_show (wav_rb);
2702 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2703 (GtkAttachOptions) (GTK_FILL),
2704 (GtkAttachOptions) (0), 0, 0);
2706 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2707 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (sw_rb));
2708 gtk_widget_show (sw_rb);
2709 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2710 (GtkAttachOptions) (GTK_FILL),
2711 (GtkAttachOptions) (0), 0, 0);
2712 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2713 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2714 gtk_widget_show (au_rb);
2715 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2716 (GtkAttachOptions) (GTK_FILL),
2717 (GtkAttachOptions) (0), 0, 0);
2720 channels_label = gtk_label_new ("Channels: ");
2721 gtk_widget_show (channels_label);
2722 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2723 (GtkAttachOptions) (GTK_FILL),
2724 (GtkAttachOptions) (0), 0, 0);
2725 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2727 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2728 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2729 gtk_widget_show (forward_rb);
2730 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2731 (GtkAttachOptions) (GTK_FILL),
2732 (GtkAttachOptions) (0), 0, 0);
2734 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed ");
2735 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2736 gtk_widget_show (reversed_rb);
2737 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2738 (GtkAttachOptions) (GTK_FILL),
2739 (GtkAttachOptions) (0), 0, 0);
2741 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2742 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2743 gtk_widget_show (both_rb);
2744 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2745 (GtkAttachOptions) (GTK_FILL),
2746 (GtkAttachOptions) (0), 0, 0);
2749 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
2752 /* if one direction is nok we don't allow saving
2753 XXX this is not ok since the user can click the refresh button and cause changes
2754 but we can not update this window. So we move all the decision on the time the ok
2757 if (user_data->forward.saved == FALSE) {
2758 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2759 gtk_widget_set_sensitive(forward_rb, FALSE);
2760 gtk_widget_set_sensitive(both_rb, FALSE);
2762 else if (user_data->reversed.saved == FALSE) {
2763 gtk_widget_set_sensitive(reversed_rb, FALSE);
2764 gtk_widget_set_sensitive(both_rb, FALSE);
2768 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
2769 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2770 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
2771 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2772 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2773 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2774 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2775 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2777 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2778 G_CALLBACK(window_delete_event_cb), NULL);
2779 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2780 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2782 gtk_widget_show(user_data->dlg.save_voice_as_w);
2783 window_present(user_data->dlg.save_voice_as_w);
2785 /* "Run" the GtkFileChooserDialog. */
2786 /* Upon exit: If "Accept" run the OK callback. */
2787 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2788 /* Destroy the window. */
2789 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2790 /* return with a TRUE status so that the dialog window will be destroyed. */
2791 /* Trying to re-run the dialog after popping up an alert box will not work */
2792 /* since the user will not be able to dismiss the alert box. */
2793 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2794 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2796 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2797 /* GtkFileChooserDialog. */
2798 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
2799 if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
2800 break; /* we're done */
2803 window_destroy(user_data->dlg.save_voice_as_w);
2808 /****************************************************************************/
2809 /* when we are finished with redisection, we add the label for the statistic */
2810 static void draw_stat(user_data_t *user_data)
2812 gchar label_max[300];
2813 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
2814 - user_data->forward.statinfo.start_seq_nr + 1;
2815 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
2816 - user_data->reversed.statinfo.start_seq_nr + 1;
2817 guint32 f_total_nr = user_data->forward.statinfo.total_nr;
2818 guint32 r_total_nr = user_data->reversed.statinfo.total_nr;
2819 gint32 f_lost = f_expected - f_total_nr;
2820 gint32 r_lost = r_expected - r_total_nr;
2821 double f_sumt = user_data->forward.statinfo.sumt;
2822 double f_sumTS = user_data->forward.statinfo.sumTS;
2823 double f_sumt2 = user_data->forward.statinfo.sumt2;
2824 double f_sumtTS = user_data->forward.statinfo.sumtTS;
2826 double r_sumt = user_data->reversed.statinfo.sumt;
2827 double r_sumTS = user_data->reversed.statinfo.sumTS;
2828 double r_sumt2 = user_data->reversed.statinfo.sumt2;
2829 double r_sumtTS = user_data->reversed.statinfo.sumtTS;
2830 double f_perc, r_perc;
2831 double f_clock_drift = 1.0;
2832 double r_clock_drift = 1.0;
2833 double f_duration = user_data->forward.statinfo.time - user_data->forward.statinfo.start_time;
2834 double r_duration = user_data->reversed.statinfo.time - user_data->reversed.statinfo.start_time;
2835 guint32 f_clock_rate = user_data->forward.statinfo.clock_rate;
2836 guint32 r_clock_rate = user_data->reversed.statinfo.clock_rate;
2838 if (f_clock_rate == 0){
2842 if (r_clock_rate == 0){
2847 f_perc = (double)(f_lost*100)/(double)f_expected;
2852 r_perc = (double)(r_lost*100)/(double)r_expected;
2857 if ((f_total_nr >0)&&(f_sumt2 > 0)){
2858 f_clock_drift = (f_total_nr * f_sumtTS - f_sumt * f_sumTS) / (f_total_nr * f_sumt2 - f_sumt * f_sumt);
2860 if ((r_total_nr >0)&&(r_sumt2 > 0)){
2861 r_clock_drift = (r_total_nr * r_sumtTS - r_sumt * r_sumTS) / (r_total_nr * r_sumt2 - r_sumt * r_sumt);
2863 g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
2864 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
2865 "Max skew = %.2f ms.\n"
2866 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2867 " Sequence errors = %u \n"
2868 "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
2869 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
2870 user_data->forward.statinfo.max_jitter,user_data->forward.statinfo.mean_jitter,
2871 user_data->forward.statinfo.max_skew,
2872 f_expected, f_expected, f_lost, f_perc,
2873 user_data->forward.statinfo.sequence,
2874 f_duration/1000,f_duration*(f_clock_drift-1.0),f_clock_drift*f_clock_rate,100.0*(f_clock_drift-1.0));
2876 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2877 gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_fwd),TRUE);
2879 g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
2880 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
2881 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2882 " Sequence errors = %u \n"
2883 "Duration %.0f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
2884 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
2885 user_data->reversed.statinfo.max_jitter,user_data->reversed.statinfo.mean_jitter,
2886 r_expected, r_expected, r_lost, r_perc,
2887 user_data->reversed.statinfo.sequence,
2888 r_duration/1000,r_duration*(r_clock_drift-1.0),r_clock_drift*r_clock_rate,100.0*(r_clock_drift-1.0));
2890 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2891 gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_rev),TRUE);
2898 /****************************************************************************/
2899 /* append a line to list */
2900 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
2901 double delta, double jitter,double skew, double bandwidth, gchar *status, gboolean marker,
2902 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
2904 GtkListStore *list_store;
2906 if (strcmp(status, OK_TEXT) != 0) {
2907 user_data->dlg.number_of_nok++;
2910 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
2912 /* Creates a new row at position. iter will be changed to point to this new row.
2913 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
2914 * The row will be filled with the values given to this function.
2916 * should generally be preferred when inserting rows in a sorted list store.
2918 #if GTK_CHECK_VERSION(2,6,0)
2919 gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
2921 gtk_list_store_append (list_store, &user_data->dlg.iter);
2922 gtk_list_store_set (list_store, &user_data->dlg.iter,
2924 PACKET_COLUMN, number,
2925 SEQUENCE_COLUMN, seq_num,
2926 TIMESTAMP_COLUMN, timestamp,
2927 DELTA_COLUMN, delta,
2928 JITTER_COLUMN, jitter,
2930 IPBW_COLUMN, bandwidth,
2931 MARKER_COLUMN, marker,
2932 STATUS_COLUMN, (char *)status,
2933 DATE_COLUMN, (char *)timeStr,
2934 LENGTH_COLUMN, pkt_len,
2935 FOREGROUND_COLOR_COL, NULL,
2936 BACKGROUND_COLOR_COL, (char *)color_str,
2939 if(flags & STAT_FLAG_FIRST){
2940 /* Set first row as active */
2941 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
2945 /****************************************************************************
2946 * Functions needed to present values from the list
2950 /* Present boolean value */
2952 rtp_boolean_data_func (GtkTreeViewColumn *column _U_,
2953 GtkCellRenderer *renderer,
2954 GtkTreeModel *model,
2960 /* the col to get data from is in userdata */
2961 gint bool_col = GPOINTER_TO_INT(user_data);
2963 gtk_tree_model_get(model, iter, bool_col, &bool_val, -1);
2967 g_strlcpy(buf, bool_val ? "SET" : "", sizeof(buf));
2970 g_assert_not_reached();
2973 g_object_set(renderer, "text", buf, NULL);
2978 GtkWidget* create_list(user_data_t* user_data)
2981 GtkListStore *list_store;
2983 GtkTreeViewColumn *column;
2984 GtkCellRenderer *renderer;
2985 GtkTreeSortable *sortable;
2986 GtkTreeView *list_view;
2987 GtkTreeSelection *selection;
2989 /* Create the store */
2990 list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX*/
2991 G_TYPE_UINT, /* Packet */
2992 G_TYPE_UINT, /* Sequence */
2993 G_TYPE_UINT, /* Time stamp */
2994 G_TYPE_FLOAT, /* Delta(ms) */
2995 G_TYPE_FLOAT, /* Filtered Jitter(ms) */
2996 G_TYPE_FLOAT, /* Skew(ms) */
2997 G_TYPE_FLOAT, /* IP BW(kbps) */
2998 G_TYPE_BOOLEAN, /* Marker */
2999 G_TYPE_STRING, /* Status */
3000 G_TYPE_STRING, /* Date */
3001 G_TYPE_UINT, /* Length */
3002 G_TYPE_STRING, /* Foreground color */
3003 G_TYPE_STRING); /* Background color */
3006 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
3008 list_view = GTK_TREE_VIEW(list);
3009 sortable = GTK_TREE_SORTABLE(list_store);
3011 #if GTK_CHECK_VERSION(2,6,0)
3012 /* Speed up the list display */
3013 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
3016 /* Setup the sortable columns */
3017 gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
3018 gtk_tree_view_set_headers_clickable(list_view, FALSE);
3020 /* The view now holds a reference. We can get rid of our own reference */
3021 g_object_unref (G_OBJECT (list_store));
3024 * Create the first column packet, associating the "text" attribute of the
3025 * cell_renderer to the first column of the model
3027 renderer = gtk_cell_renderer_text_new ();
3028 column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
3029 "text", PACKET_COLUMN,
3030 "foreground", FOREGROUND_COLOR_COL,
3031 "background", BACKGROUND_COLOR_COL,
3033 gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
3034 gtk_tree_view_column_set_resizable(column, TRUE);
3035 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3036 gtk_tree_view_column_set_min_width(column, 55);
3038 /* Add the column to the view. */
3039 gtk_tree_view_append_column (list_view, column);
3042 renderer = gtk_cell_renderer_text_new ();
3043 column = gtk_tree_view_column_new_with_attributes ("Sequence", renderer,
3044 "text", SEQUENCE_COLUMN,
3045 "foreground", FOREGROUND_COLOR_COL,
3046 "background", BACKGROUND_COLOR_COL,
3048 gtk_tree_view_column_set_sort_column_id(column, SEQUENCE_COLUMN);
3049 gtk_tree_view_column_set_resizable(column, TRUE);
3050 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3051 gtk_tree_view_column_set_min_width(column, 75);
3052 gtk_tree_view_append_column (list_view, column);
3055 Currently not visible
3057 renderer = gtk_cell_renderer_text_new ();
3058 column = gtk_tree_view_column_new_with_attributes ("Time stamp", renderer,
3059 "text", TIMESTAMP_COLUMN,
3060 "foreground", FOREGROUND_COLOR_COL,
3061 "background", BACKGROUND_COLOR_COL,
3063 gtk_tree_view_column_set_sort_column_id(column, TIMESTAMP_COLUMN);
3064 gtk_tree_view_column_set_resizable(column, TRUE);
3065 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3066 gtk_tree_view_column_set_min_width(column, 75);
3067 gtk_tree_view_append_column (list_view, column);
3070 renderer = gtk_cell_renderer_text_new ();
3071 column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
3072 "text", DELTA_COLUMN,
3073 "foreground", FOREGROUND_COLOR_COL,
3074 "background", BACKGROUND_COLOR_COL,
3077 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3078 GINT_TO_POINTER(DELTA_COLUMN), NULL);
3080 gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
3081 gtk_tree_view_column_set_resizable(column, TRUE);
3082 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3083 gtk_tree_view_column_set_min_width(column, 75);
3084 gtk_tree_view_append_column (list_view, column);
3087 renderer = gtk_cell_renderer_text_new ();
3088 column = gtk_tree_view_column_new_with_attributes ("Filtered Jitter(ms)", renderer,
3089 "text", JITTER_COLUMN,
3090 "foreground", FOREGROUND_COLOR_COL,
3091 "background", BACKGROUND_COLOR_COL,
3094 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3095 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3097 gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3098 gtk_tree_view_column_set_resizable(column, TRUE);
3099 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3100 gtk_tree_view_column_set_min_width(column, 110);
3101 gtk_tree_view_append_column (list_view, column);
3104 renderer = gtk_cell_renderer_text_new ();
3105 column = gtk_tree_view_column_new_with_attributes ("Skew(ms)", renderer,
3106 "text", SKEW_COLUMN,
3107 "foreground", FOREGROUND_COLOR_COL,
3108 "background", BACKGROUND_COLOR_COL,
3111 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3112 GINT_TO_POINTER(SKEW_COLUMN), NULL);
3114 gtk_tree_view_column_set_sort_column_id(column, SKEW_COLUMN);
3115 gtk_tree_view_column_set_resizable(column, TRUE);
3116 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3117 gtk_tree_view_column_set_min_width(column, 110);
3118 gtk_tree_view_append_column (list_view, column);
3121 renderer = gtk_cell_renderer_text_new ();
3122 column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
3123 "text", IPBW_COLUMN,
3124 "foreground", FOREGROUND_COLOR_COL,
3125 "background", BACKGROUND_COLOR_COL,
3128 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3129 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3131 gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3132 gtk_tree_view_column_set_resizable(column, TRUE);
3133 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3134 gtk_tree_view_column_set_min_width(column, 80);
3135 gtk_tree_view_append_column (list_view, column);
3138 renderer = gtk_cell_renderer_text_new ();
3139 column = gtk_tree_view_column_new_with_attributes ("Marker", renderer,
3140 "text", MARKER_COLUMN,
3141 "foreground", FOREGROUND_COLOR_COL,
3142 "background", BACKGROUND_COLOR_COL,
3145 gtk_tree_view_column_set_cell_data_func(column, renderer, rtp_boolean_data_func,
3146 GINT_TO_POINTER(MARKER_COLUMN), NULL);
3148 gtk_tree_view_column_set_sort_column_id(column, MARKER_COLUMN);
3149 gtk_tree_view_column_set_resizable(column, TRUE);
3150 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3151 gtk_tree_view_column_set_min_width(column, 60);
3152 gtk_tree_view_append_column (list_view, column);
3155 renderer = gtk_cell_renderer_text_new ();
3156 column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3157 "text", STATUS_COLUMN,
3158 "foreground", FOREGROUND_COLOR_COL,
3159 "background", BACKGROUND_COLOR_COL,
3161 gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3162 gtk_tree_view_column_set_resizable(column, TRUE);
3163 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3164 gtk_tree_view_column_set_min_width(column, 100);
3165 gtk_tree_view_append_column (list_view, column);
3167 /* Now enable the sorting of each column */
3168 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3169 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3171 /* Setup the selection handler */
3172 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3173 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3175 g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3176 G_CALLBACK (on_list_select_row),
3181 /****************************************************************************/
3182 /* Create the dialog box with all widgets */
3183 static void create_rtp_dialog(user_data_t* user_data)
3185 GtkWidget *window = NULL;
3186 GtkWidget *list_fwd;
3187 GtkWidget *list_rev;
3188 GtkWidget *label_stats_fwd;
3189 GtkWidget *label_stats_rev;
3190 GtkWidget *notebook;
3192 GtkWidget *main_vb, *page, *page_r;
3194 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3195 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3197 GtkWidget *graph_bt;
3198 gchar label_forward[150];
3199 gchar label_forward_tree[150];
3200 gchar label_reverse[150];
3202 gchar str_ip_src[16];
3203 gchar str_ip_dst[16];
3205 window = dlg_window_new("Wireshark: RTP Stream Analysis"); /* transient_for top_level */
3206 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3208 /* Container for each row of widgets */
3209 main_vb = gtk_vbox_new(FALSE, 2);
3210 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3211 gtk_container_add(GTK_CONTAINER(window), main_vb);
3212 gtk_widget_show(main_vb);
3215 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), sizeof(str_ip_src));
3216 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), sizeof(str_ip_dst));
3218 g_snprintf(label_forward, sizeof(label_forward),
3219 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3220 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3222 g_snprintf(label_forward_tree, sizeof(label_forward_tree),
3223 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3224 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3227 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), sizeof(str_ip_src));
3228 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), sizeof(str_ip_dst));
3230 g_snprintf(label_reverse, sizeof(label_reverse),
3231 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3232 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3234 /* Start a notebook for flipping between sets of changes */
3235 notebook = gtk_notebook_new();
3236 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3237 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3239 user_data->dlg.notebook_signal_id =
3240 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3242 /* page for forward connection */
3243 page = gtk_vbox_new(FALSE, 8);
3244 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3246 /* direction label */
3247 label = gtk_label_new(label_forward);
3248 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3250 /* place for some statistics */
3251 label_stats_fwd = gtk_label_new("\n");
3252 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3254 /* scrolled window */
3255 scrolled_window = scrolled_window_new(NULL, NULL);
3258 list_fwd = create_list(user_data);
3259 gtk_widget_show(list_fwd);
3260 gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3261 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3262 gtk_widget_show(scrolled_window);
3265 label = gtk_label_new(" Forward Direction ");
3266 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3268 /* same page for reversed connection */
3269 page_r = gtk_vbox_new(FALSE, 8);
3270 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3271 label = gtk_label_new(label_reverse);
3272 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3273 label_stats_rev = gtk_label_new("\n");
3274 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3276 scrolled_window_r = scrolled_window_new(NULL, NULL);
3278 list_rev = create_list(user_data);
3279 gtk_widget_show(list_rev);
3280 gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3281 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3282 gtk_widget_show(scrolled_window_r);
3284 label = gtk_label_new(" Reversed Direction ");
3285 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3287 /* page for help&about or future */
3289 page_help = gtk_hbox_new(FALSE, 5);
3290 label = gtk_label_new(" Future ");
3291 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3292 frame = gtk_frame_new("");
3293 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3294 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3295 gtk_container_add(GTK_CONTAINER(frame), text);
3296 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3297 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3300 /* show all notebooks */
3301 gtk_widget_show_all(notebook);
3304 box4 = gtk_hbutton_box_new();
3305 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3306 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3307 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3308 gtk_box_set_spacing(GTK_BOX (box4), 0);
3309 gtk_widget_show(box4);
3311 voice_bt = gtk_button_new_with_label("Save payload...");
3312 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3313 gtk_widget_show(voice_bt);
3314 g_signal_connect(voice_bt, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
3316 csv_bt = gtk_button_new_with_label("Save as CSV...");
3317 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3318 gtk_widget_show(csv_bt);
3319 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3321 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3322 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3323 gtk_widget_show(refresh_bt);
3324 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3326 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3327 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3328 gtk_widget_show(goto_bt);
3329 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked_lst), user_data);
3331 graph_bt = gtk_button_new_with_label("Graph");
3332 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3333 gtk_widget_show(graph_bt);
3334 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3337 next_bt = gtk_button_new_with_label("Next non-Ok");
3338 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3339 gtk_widget_show(next_bt);
3340 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked_list), user_data);
3342 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3343 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3344 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3345 gtk_widget_show(close_bt);
3346 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3348 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3349 g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3351 gtk_widget_show(window);
3352 window_present(window);
3355 /* some widget references need to be saved for outside use */
3356 user_data->dlg.window = window;
3357 user_data->dlg.list_fwd = list_fwd;
3358 user_data->dlg.list_rev = list_rev;
3359 user_data->dlg.label_stats_fwd = label_stats_fwd;
3360 user_data->dlg.label_stats_rev = label_stats_rev;
3361 user_data->dlg.notebook = notebook;
3362 user_data->dlg.selected_list = list_fwd;
3363 user_data->dlg.number_of_nok = 0;
3366 * select the initial row
3368 gtk_widget_grab_focus(list_fwd);
3373 /****************************************************************************/
3374 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3375 const gchar* proto_field, guint32* p_result)
3378 proto_node *proto_sibling_node;
3379 header_field_info *hfssrc;
3382 finfo = PNODE_FINFO(ptree_node);
3384 g_assert(finfo && "Caller passed top of the protocol tree. Expected child node");
3386 if (hfinformation==(finfo->hfinfo)) {
3387 hfssrc = proto_registrar_get_byname(proto_field);
3390 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3391 ptree_node=ptree_node->next) {
3392 finfo=PNODE_FINFO(ptree_node);
3393 if (hfssrc==finfo->hfinfo) {
3394 if (hfinformation->type==FT_IPv4) {
3395 ipv4 = fvalue_get(&finfo->value);
3396 *p_result = ipv4_get_net_order_addr(ipv4);
3399 *p_result = fvalue_get_uinteger(&finfo->value);
3408 proto_sibling_node = ptree_node->next;
3410 if (proto_sibling_node) {
3411 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3417 /****************************************************************************/
3418 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3419 const gchar* proto_name,
3420 const gchar* proto_field,
3423 proto_node *ptree_node;
3424 header_field_info *hfinformation;
3426 hfinformation = proto_registrar_get_byname(proto_name);
3427 if (hfinformation == NULL)
3430 ptree_node = ((proto_node *)protocol_tree)->first_child;
3434 return process_node(ptree_node, hfinformation, proto_field, p_result);
3438 /****************************************************************************/
3440 address *ip_src_fwd,
3441 guint16 port_src_fwd,
3442 address *ip_dst_fwd,
3443 guint16 port_dst_fwd,
3445 address *ip_src_rev,
3446 guint16 port_src_rev,
3447 address *ip_dst_rev,
3448 guint16 port_dst_rev,
3452 user_data_t *user_data;
3455 static color_t col[MAX_GRAPHS] = {
3456 {0, 0x0000, 0x0000, 0x0000},
3457 {0, 0xffff, 0x0000, 0x0000},
3458 {0, 0x0000, 0xffff, 0x0000},
3459 {0, 0x0000, 0x0000, 0xffff}
3464 user_data = g_malloc(sizeof(user_data_t));
3466 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3467 user_data->port_src_fwd = port_src_fwd;
3468 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3469 user_data->port_dst_fwd = port_dst_fwd;
3470 user_data->ssrc_fwd = ssrc_fwd;
3471 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3472 user_data->port_src_rev = port_src_rev;
3473 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3474 user_data->port_dst_rev = port_dst_rev;
3475 user_data->ssrc_rev = ssrc_rev;
3478 /* file names for storing sound data */
3479 /*XXX: check for errors*/
3480 fd = create_tempfile(&tempname, "wireshark_rtp_f");
3481 user_data->f_tempname = g_strdup(tempname);
3483 fd = create_tempfile(&tempname, "wireshark_rtp_r");
3484 user_data->r_tempname = g_strdup(tempname);
3486 user_data->forward.saveinfo.fp = NULL;
3487 user_data->reversed.saveinfo.fp = NULL;
3488 user_data->dlg.save_voice_as_w = NULL;
3489 user_data->dlg.save_csv_as_w = NULL;
3490 user_data->dlg.dialog_graph.window = NULL;
3492 /* init dialog_graph */
3493 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3494 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3495 user_data->dlg.dialog_graph.draw_area=NULL;
3496 user_data->dlg.dialog_graph.pixmap=NULL;
3497 user_data->dlg.dialog_graph.scrollbar=NULL;
3498 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3499 user_data->dlg.dialog_graph.pixmap_width=500;
3500 user_data->dlg.dialog_graph.pixmap_height=200;
3501 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3502 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3503 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3504 user_data->dlg.dialog_graph.max_interval=0;
3505 user_data->dlg.dialog_graph.num_items=0;
3506 user_data->dlg.dialog_graph.start_time = -1;
3508 for(i=0;i<MAX_GRAPHS;i++){
3509 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3510 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3511 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3512 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3513 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3514 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3515 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3516 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3519 /* create the dialog box */
3520 create_rtp_dialog(user_data);
3522 /* proceed as if the Refresh button would have been pressed */
3523 on_refresh_bt_clicked(NULL, user_data);
3526 /****************************************************************************/
3527 /* entry point from main menu */
3528 static void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3531 guint16 port_src_fwd;
3533 guint16 port_dst_fwd;
3534 guint32 ssrc_fwd = 0;
3536 guint16 port_src_rev;
3538 guint16 port_dst_rev;
3539 guint32 ssrc_rev = 0;
3540 unsigned int version_fwd;
3542 gchar filter_text[256];
3548 gboolean frame_matched;
3550 GList *strinfo_list;
3551 GList *filtered_list = NULL;
3552 rtp_stream_info_t *strinfo;
3555 /* Try to compile the filter. */
3556 g_strlcpy(filter_text,"rtp && rtp.version && rtp.ssrc && (ip || ipv6)",sizeof(filter_text));
3557 if (!dfilter_compile(filter_text, &sfcode)) {
3558 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3561 /* we load the current file into cf variable */
3563 fdata = cf->current_frame;
3565 /* we are on the selected frame now */
3567 return; /* if we exit here it's an error */
3569 /* dissect the current frame */
3570 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3571 cf->pd, fdata->cap_len, &err, &err_info)) {
3572 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3573 cf_read_error_message(err, err_info), cf->filename);
3576 epan_dissect_init(&edt, TRUE, FALSE);
3577 epan_dissect_prime_dfilter(&edt, sfcode);
3578 epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3579 frame_matched = dfilter_apply_edt(sfcode, &edt);
3581 /* if it is not an rtp frame, show the rtpstream dialog */
3582 frame_matched = dfilter_apply_edt(sfcode, &edt);
3583 if (frame_matched != 1) {
3584 epan_dissect_cleanup(&edt);
3585 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3586 "You didn't choose a RTP packet!");
3590 /* ok, it is a RTP frame, so let's get the ip and port values */
3591 COPY_ADDRESS(&(ip_src_fwd), &(edt.pi.src))
3592 COPY_ADDRESS(&(ip_dst_fwd), &(edt.pi.dst))
3593 port_src_fwd = edt.pi.srcport;
3594 port_dst_fwd = edt.pi.destport;
3596 /* assume the inverse ip/port combination for the reverse direction */
3597 COPY_ADDRESS(&(ip_src_rev), &(edt.pi.dst))
3598 COPY_ADDRESS(&(ip_dst_rev), &(edt.pi.src))
3599 port_src_rev = edt.pi.destport;
3600 port_dst_rev = edt.pi.srcport;
3602 /* check if it is RTP Version 2 */
3603 if (!get_int_value_from_proto_tree(edt.tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3604 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3605 "RTP Version != 2 isn't supported!");
3609 /* now we need the SSRC value of the current frame */
3610 if (!get_int_value_from_proto_tree(edt.tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3611 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3612 "SSRC value couldn't be found!");
3616 /* Scan for rtpstream */
3618 /* search for reversed direction in the global rtp streams list */
3620 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3621 while (strinfo_list)
3623 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3624 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3625 && strinfo->src_port==port_src_fwd
3626 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3627 && strinfo->dest_port==port_dst_fwd)
3629 filtered_list = g_list_prepend(filtered_list, strinfo);
3632 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3633 && strinfo->src_port==port_src_rev
3634 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3635 && strinfo->dest_port==port_dst_rev)
3638 filtered_list = g_list_append(filtered_list, strinfo);
3640 ssrc_rev = strinfo->ssrc;
3643 strinfo_list = g_list_next(strinfo_list);
3646 /* if more than one reverse streams found, we let the user choose the right one */
3648 rtpstream_dlg_show(filtered_list);
3667 /****************************************************************************/
3669 rtp_analysis_init(const char *dummy _U_,void* userdata _U_)
3671 rtp_analysis_cb(NULL, NULL);
3674 /****************************************************************************/
3676 register_tap_listener_rtp_analysis(void)
3678 register_stat_cmd_arg("rtp", rtp_analysis_init,NULL);
3680 register_stat_menu_item("_RTP/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3681 rtp_analysis_cb, NULL, NULL, NULL);