2 * RTP analysis addition for Wireshark
4 * Copyright 2003, Alcatel Business Systems
5 * By Lars Ruoff <lars.ruoff@gmx.net>
8 * Copyright 2003, Iskratel, Ltd, Kranj
9 * By Miha Jemec <m.jemec@iskratel.si>
11 * Graph. Copyright 2004, Verso Technology
12 * By Alejandro Vaquero <alejandro.vaquero@verso.com>
13 * Based on io_stat.c by Ronnie Sahlberg
15 * Wireshark - Network traffic analyzer
16 * By Gerald Combs <gerald@wireshark.org>
17 * Copyright 1998 Gerald Combs
19 * This program is free software; you can redistribute it and/or
20 * modify it under the terms of the GNU General Public License
21 * as published by the Free Software Foundation; either version 2
22 * of the License, or (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program; if not, write to the Free Software
31 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
50 #include <wsutil/file_util.h>
51 #include <wsutil/tempfile.h>
52 #include <wsutil/g711.h>
53 #include <wsutil/pint.h>
55 #include <epan/epan_dissect.h>
56 #include <wsutil/filesystem.h>
58 #include <epan/dissectors/packet-rtp.h>
59 #include <epan/rtp_pt.h>
60 #include <epan/addr_resolv.h>
61 #include <epan/stat_tap_ui.h>
62 #include <epan/strutil.h>
64 #include <epan/stat_groups.h>
67 #include "ui/alert_box.h"
68 #include "ui/last_open_dir.h"
69 #include "ui/progress_dlg.h"
70 #include "ui/simple_dialog.h"
71 #include "ui/utf8_entities.h"
74 #include "ui/gtk/gtkglobals.h"
75 #include "ui/gtk/dlg_utils.h"
76 #include "ui/gtk/file_dlg.h"
77 #include "ui/gtk/gui_utils.h"
78 #include "ui/gtk/gui_stat_menu.h"
79 #include "ui/gtk/pixmap_save.h"
80 #include "ui/gtk/main.h"
81 #include "ui/rtp_analysis.h"
82 #include "ui/rtp_stream.h"
83 #include "ui/gtk/rtp_stream_dlg.h"
84 #include "ui/gtk/stock_icons.h"
86 #ifdef HAVE_LIBPORTAUDIO
87 #include "ui/voip_calls.h"
88 #include "ui/gtk/graph_analysis.h"
89 #include "ui/gtk/rtp_player.h"
90 #endif /* HAVE_LIBPORTAUDIO */
92 #include "ui/gtk/old-gtk-compat.h"
94 #include "frame_tvbuff.h"
96 void register_tap_listener_rtp_analysis(void);
111 FOREGROUND_COLOR_COL,
112 BACKGROUND_COLOR_COL,
113 N_COLUMN /* The number of columns */
115 /****************************************************************************/
118 #define NUM_GRAPH_ITEMS 100000
119 #define MAX_YSCALE 16
120 #define DEFAULT_YSCALE_INDEX 0 /* AUTO_MAX_YSCALE */
121 #define AUTO_MAX_YSCALE 0
123 #define GRAPH_FWD_JITTER 0
124 #define GRAPH_FWD_DIFF 1
125 #define GRAPH_FWD_DELTA 2
126 #define GRAPH_REV_JITTER 3
127 #define GRAPH_REV_DIFF 4
128 #define GRAPH_REV_DELTA 5
130 static guint32 yscale_max[MAX_YSCALE] = {
131 AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000,
132 1000000, 2000000, 5000000, 10000000, 20000000, 50000000
135 #define MAX_PIXELS_PER_TICK 4
136 #define DEFAULT_PIXELS_PER_TICK_INDEX 2
137 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
139 static const char *graph_descr[MAX_GRAPHS] = {
140 "Fwd Jitter", "Fwd Difference", "Fwd Delta", "Rvr Jitter", "Rvr Difference", "Rvr Delta"
144 #define MAX_TICK_VALUES 5
145 #define DEFAULT_TICK_VALUE_INDEX 1
146 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
148 typedef struct _dialog_graph_graph_item_t {
151 } dialog_graph_graph_item_t;
153 typedef struct _dialog_graph_graph_t {
154 struct _user_data_t *ud;
155 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
158 GtkWidget *display_button;
163 } dialog_graph_graph_t;
166 typedef struct _dialog_graph_t {
167 gboolean needs_redraw;
168 gint32 interval; /* measurement interval in ms */
169 guint32 last_interval;
170 guint32 max_interval; /* XXX max_interval and num_items are redundant */
174 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
176 GtkWidget *draw_area;
177 #if GTK_CHECK_VERSION(2,22,0)
178 cairo_surface_t *surface;
182 GtkAdjustment *scrollbar_adjustment;
183 GtkWidget *scrollbar;
190 typedef struct _dialog_data_t {
195 GtkWidget *label_stats_fwd;
196 GtkWidget *label_stats_rev;
197 GtkWidget *selected_list;
199 GtkTreeSelection *selected_list_sel;
200 gint selected_list_row;
202 GtkWidget *save_voice_as_w;
203 GtkWidget *save_csv_as_w;
204 gulong notebook_signal_id;
205 dialog_graph_t dialog_graph;
208 #define OK_TEXT "[ Ok ]"
210 /* type of error when saving voice in a file didn't succeed */
213 TAP_RTP_WRONG_LENGTH,
214 TAP_RTP_PADDING_ERROR,
216 TAP_RTP_FILE_OPEN_ERROR,
217 TAP_RTP_FILE_WRITE_ERROR,
221 typedef struct _tap_rtp_save_info_t {
224 error_type_t error_type;
226 } tap_rtp_save_info_t;
229 /* structure that holds the information about the forward and reversed direction */
230 struct _info_direction {
231 tap_rtp_stat_t statinfo;
232 tap_rtp_save_info_t saveinfo;
235 #define SILENCE_PCMU (guint8)0xFF
236 #define SILENCE_PCMA (guint8)0x55
238 /* structure that holds general information about the connection
239 * and structures for both directions */
240 typedef struct _user_data_t {
241 /* tap associated data*/
243 guint32 port_src_fwd;
245 guint32 port_dst_fwd;
248 guint32 port_src_rev;
250 guint32 port_dst_rev;
253 struct _info_direction forward;
254 struct _info_direction reversed;
259 /* dialog associated data */
266 static const gchar *titles[11] = {
280 #define SAVE_FORWARD_DIRECTION_MASK 0x01
281 #define SAVE_REVERSE_DIRECTION_MASK 0x02
282 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
284 #define SAVE_NONE_FORMAT 0
285 #define SAVE_WAV_FORMAT 1
286 #define SAVE_AU_FORMAT 2
287 #define SAVE_SW_FORMAT 3
288 #define SAVE_RAW_FORMAT 4
291 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data);
293 /****************************************************************************/
295 enable_graph(dialog_graph_graph_t *dgg)
302 static void dialog_graph_reset(user_data_t* user_data);
306 /****************************************************************************/
309 /****************************************************************************/
310 /* when there is a [re]reading of packet's */
312 rtp_reset(void *user_data_arg)
314 user_data_t *user_data = (user_data_t *)user_data_arg;
316 user_data->forward.statinfo.first_packet = TRUE;
317 user_data->reversed.statinfo.first_packet = TRUE;
318 user_data->forward.statinfo.max_delta = 0;
319 user_data->reversed.statinfo.max_delta = 0;
320 user_data->forward.statinfo.max_jitter = 0;
321 user_data->reversed.statinfo.max_jitter = 0;
322 user_data->forward.statinfo.max_skew = 0;
323 user_data->reversed.statinfo.max_skew = 0;
324 user_data->forward.statinfo.mean_jitter = 0;
325 user_data->reversed.statinfo.mean_jitter = 0;
326 user_data->forward.statinfo.delta = 0;
327 user_data->reversed.statinfo.delta = 0;
328 user_data->forward.statinfo.diff = 0;
329 user_data->reversed.statinfo.diff = 0;
330 user_data->forward.statinfo.jitter = 0;
331 user_data->reversed.statinfo.jitter = 0;
332 user_data->forward.statinfo.skew = 0;
333 user_data->reversed.statinfo.skew = 0;
334 user_data->forward.statinfo.sumt = 0;
335 user_data->reversed.statinfo.sumt = 0;
336 user_data->forward.statinfo.sumTS = 0;
337 user_data->reversed.statinfo.sumTS = 0;
338 user_data->forward.statinfo.sumt2 = 0;
339 user_data->reversed.statinfo.sumt2 = 0;
340 user_data->forward.statinfo.sumtTS = 0;
341 user_data->reversed.statinfo.sumtTS = 0;
342 user_data->forward.statinfo.bandwidth = 0;
343 user_data->reversed.statinfo.bandwidth = 0;
344 user_data->forward.statinfo.total_bytes = 0;
345 user_data->reversed.statinfo.total_bytes = 0;
346 user_data->forward.statinfo.bw_start_index = 0;
347 user_data->reversed.statinfo.bw_start_index = 0;
348 user_data->forward.statinfo.bw_index = 0;
349 user_data->reversed.statinfo.bw_index = 0;
350 user_data->forward.statinfo.timestamp = 0;
351 user_data->reversed.statinfo.timestamp = 0;
352 user_data->forward.statinfo.max_nr = 0;
353 user_data->reversed.statinfo.max_nr = 0;
354 user_data->forward.statinfo.total_nr = 0;
355 user_data->reversed.statinfo.total_nr = 0;
356 user_data->forward.statinfo.sequence = 0;
357 user_data->reversed.statinfo.sequence = 0;
358 user_data->forward.statinfo.start_seq_nr = 0;
359 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
360 user_data->forward.statinfo.stop_seq_nr = 0;
361 user_data->reversed.statinfo.stop_seq_nr = 0;
362 user_data->forward.statinfo.cycles = 0;
363 user_data->reversed.statinfo.cycles = 0;
364 user_data->forward.statinfo.under = FALSE;
365 user_data->reversed.statinfo.under = FALSE;
366 user_data->forward.statinfo.start_time = 0;
367 user_data->reversed.statinfo.start_time = 0;
368 user_data->forward.statinfo.time = 0;
369 user_data->reversed.statinfo.time = 0;
370 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
371 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
373 user_data->forward.saveinfo.count = 0;
374 user_data->reversed.saveinfo.count = 0;
375 user_data->forward.saveinfo.saved = FALSE;
376 user_data->reversed.saveinfo.saved = FALSE;
378 /* clear the dialog box lists */
379 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
380 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
382 /* reset graph info */
383 dialog_graph_reset(user_data);
385 #ifdef HAVE_LIBPORTAUDIO
386 /* reset the RTP player */
389 /* XXX check for error at fclose? */
390 if (user_data->forward.saveinfo.fp != NULL)
391 fclose(user_data->forward.saveinfo.fp);
392 if (user_data->reversed.saveinfo.fp != NULL)
393 fclose(user_data->reversed.saveinfo.fp);
394 user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
395 if (user_data->forward.saveinfo.fp == NULL)
396 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
397 user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
398 if (user_data->reversed.saveinfo.fp == NULL)
399 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
403 /****************************************************************************/
405 rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
407 dialog_graph_graph_item_t *it;
412 * We sometimes get called when dgg is disabled.
413 * This is a bug since the tap listener should be removed first
419 dgg->ud->dlg.dialog_graph.needs_redraw = TRUE;
422 * Find which interval this is supposed to go in and store the
423 * interval index as idx
425 if (dgg->ud->dlg.dialog_graph.start_time == -1) { /* it is the first */
426 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
428 rtp_time = nstime_to_msec(&pinfo->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
432 idx = (guint32)(rtp_time)/dgg->ud->dlg.dialog_graph.interval;
434 /* some sanity checks */
435 if (idx >= NUM_GRAPH_ITEMS) {
439 /* update num_items */
440 if (idx > dgg->ud->dlg.dialog_graph.num_items) {
441 dgg->ud->dlg.dialog_graph.num_items = idx;
442 dgg->ud->dlg.dialog_graph.max_interval = idx*dgg->ud->dlg.dialog_graph.interval;
446 * Find the appropriate dialog_graph_graph_item_t structure
448 it = &dgg->items[idx];
451 * Use the max value to highlight RTP problems
453 if (value > it->value) {
456 it->flags = it->flags | statinfo->flags;
461 /****************************************************************************/
462 /* here we can redraw the output */
465 rtp_draw(void *prs _U_)
470 /* forward declarations */
471 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
472 double delta, double jitter, double skew , double bandwidth, gchar *status, gboolean marker,
473 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags);
475 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
476 tap_rtp_stat_t *statinfo, packet_info *pinfo,
477 const struct _rtp_info *rtpinfo);
479 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
480 tap_rtp_stat_t *statinfo,
482 const struct _rtp_info *rtpinfo);
485 /****************************************************************************/
486 /* whenever a RTP packet is seen by the tap listener */
488 rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
490 user_data_t *user_data = (user_data_t *)user_data_arg;
491 const struct _rtp_info *rtpinfo = (const struct _rtp_info *)rtpinfo_arg;
492 gboolean rtp_selected = FALSE;
494 /* we ignore packets that are not displayed */
495 if (pinfo->fd->flags.passed_dfilter == 0)
497 /* also ignore RTP Version != 2 */
498 else if (rtpinfo->info_version != 2)
500 /* is it the forward direction? */
501 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
502 && (CMP_ADDRESS(&(user_data->src_fwd), &(pinfo->src)) == 0)
503 && (user_data->port_src_fwd == pinfo->srcport)
504 && (CMP_ADDRESS(&(user_data->dst_fwd), &(pinfo->dst)) == 0)
505 && (user_data->port_dst_fwd == pinfo->destport)) {
506 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
507 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]),
508 &(user_data->forward.statinfo), pinfo,
509 (guint32)(user_data->forward.statinfo.jitter*1000));
510 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]),
511 &(user_data->forward.statinfo), pinfo,
512 (guint32)(user_data->forward.statinfo.diff*1000));
513 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DELTA]),
514 &(user_data->forward.statinfo), pinfo,
515 (guint32)(user_data->forward.statinfo.delta*1000));
516 rtp_packet_add_info(user_data->dlg.list_fwd, user_data,
517 &(user_data->forward.statinfo), pinfo, rtpinfo);
518 rtp_packet_save_payload(&(user_data->forward.saveinfo),
519 &(user_data->forward.statinfo), pinfo, rtpinfo);
522 /* is it the reversed direction? */
523 else if (user_data->ssrc_rev == rtpinfo->info_sync_src
524 && (CMP_ADDRESS(&(user_data->src_rev), &(pinfo->src)) == 0)
525 && (user_data->port_src_rev == pinfo->srcport)
526 && (CMP_ADDRESS(&(user_data->dst_rev), &(pinfo->dst)) == 0)
527 && (user_data->port_dst_rev == pinfo->destport)) {
528 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
529 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]),
530 &(user_data->reversed.statinfo), pinfo,
531 (guint32)(user_data->reversed.statinfo.jitter*1000));
532 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]),
533 &(user_data->reversed.statinfo), pinfo,
534 (guint32)(user_data->reversed.statinfo.diff*1000));
535 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DELTA]),
536 &(user_data->reversed.statinfo), pinfo,
537 (guint32)(user_data->reversed.statinfo.delta*1000));
538 rtp_packet_add_info(user_data->dlg.list_rev, user_data,
539 &(user_data->reversed.statinfo), pinfo, rtpinfo);
540 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
541 &(user_data->reversed.statinfo), pinfo, rtpinfo);
544 /* add this RTP for future listening using the RTP Player*/
546 #ifdef HAVE_LIBPORTAUDIO
547 add_rtp_packet(rtpinfo, pinfo);
555 Replaced by using the strings instead.
556 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
557 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
558 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
559 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
560 GdkColor yellow = {0, 0xffff, 0xffff, 0x0000};
561 COLOR_T_EVENT g_snprintf(color_str, sizeof(color_str), "#ef8c bfff ffff");
562 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
564 /****************************************************************************/
565 /* adds statistics information from the packet to the list */
567 rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
568 tap_rtp_stat_t *statinfo, packet_info *pinfo,
569 const struct _rtp_info *rtpinfo)
578 then = pinfo->fd->abs_ts.secs;
579 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
580 tm_tmp = localtime(&then);
581 g_snprintf(timeStr, sizeof(timeStr), "%02d/%02d/%04d %02d:%02d:%02d.%03d",
584 tm_tmp->tm_year + 1900,
590 /* Default to using black on white text if nothing below overrides it */
591 g_snprintf(color_str, sizeof(color_str), "#ffffffffffff");
593 if (statinfo->pt == PT_CN) {
594 g_snprintf(status, sizeof(status), "Comfort noise (PT=13, RFC 3389)");
595 /* color = COLOR_CN; */
596 g_snprintf(color_str, sizeof(color_str), "#bfffbfffffff");
598 else if (statinfo->pt == PT_CN_OLD) {
599 g_snprintf(status, sizeof(status), "Comfort noise (PT=19, reserved)");
600 /* color = COLOR_CN; */
601 g_snprintf(color_str, sizeof(color_str), "#bfffbfffffff");
603 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
604 g_snprintf(status, sizeof(status), "Wrong sequence nr.");
605 /* color = COLOR_ERROR; */
606 g_snprintf(color_str, sizeof(color_str), "#ffffbfffbfff");
608 else if (statinfo->flags & STAT_FLAG_DUP_PKT) {
609 g_snprintf(status, sizeof(status), "Suspected duplicate(MAC address) only delta time calculated");
610 /* color = Yellow; */
611 g_snprintf(color_str, sizeof(color_str), "#ffffffff0000");
613 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
614 if (statinfo->flags & STAT_FLAG_PT_T_EVENT) {
615 g_snprintf(status, sizeof(status), "Payload changed to PT=%u telephone/event", statinfo->pt);
617 g_snprintf(status, sizeof(status), "Payload changed to PT=%u", statinfo->pt);
619 /* color = COLOR_WARNING; */
620 g_snprintf(color_str, sizeof(color_str), "#ffffdfffbfff");
622 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
623 g_snprintf(status, sizeof(status), "Incorrect timestamp");
624 /* color = COLOR_WARNING; */
625 g_snprintf(color_str, sizeof(color_str), "#ffffdfffbfff");
627 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
628 && !(statinfo->flags & STAT_FLAG_FIRST)
629 && !(statinfo->flags & STAT_FLAG_PT_CN)
630 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
631 && !(statinfo->flags & STAT_FLAG_MARKER)) {
632 g_snprintf(status, sizeof(status), "Marker missing?");
633 /* color = COLOR_WARNING; */
634 g_snprintf(color_str, sizeof(color_str), "#ffffdfffbfff");
635 }else if (statinfo->flags & STAT_FLAG_PT_T_EVENT) {
636 g_snprintf(status, sizeof(status), "PT=%u telephone/event", statinfo->pt);
638 /* color = COLOR_T_EVENT; */
639 g_snprintf(color_str, sizeof(color_str), "#ef8cbfffffff");
641 if (statinfo->flags & STAT_FLAG_MARKER) {
642 /* color = COLOR_WARNING; */
643 g_snprintf(color_str, sizeof(color_str), "#ffffdfffbfff");
645 g_snprintf(status, sizeof(status), OK_TEXT);
647 /* is this the first packet we got in this direction? */
648 if (statinfo->flags & STAT_FLAG_FIRST) {
649 add_to_list(list, user_data,
650 pinfo->fd->num, rtpinfo->info_seq_num,
657 rtpinfo->info_marker_set,
658 timeStr, pinfo->fd->pkt_len,
663 add_to_list(list, user_data,
664 pinfo->fd->num, rtpinfo->info_seq_num,
671 rtpinfo->info_marker_set,
672 timeStr, pinfo->fd->pkt_len,
679 #define MAX_SILENCE_TICKS 1000000
680 /****************************************************************************/
682 rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
683 tap_rtp_stat_t *statinfo,
685 const struct _rtp_info *rtpinfo)
692 /* is this the first packet we got in this direction? */
693 if (statinfo->flags & STAT_FLAG_FIRST) {
694 if (saveinfo->fp == NULL) {
695 saveinfo->saved = FALSE;
696 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
699 saveinfo->saved = TRUE;
702 /* save the voice information */
703 /* if there was already an error, we quit */
704 if (saveinfo->saved == FALSE)
707 /* if the captured length and packet length aren't equal, we quit
708 * if also the RTP dissector thinks there is some information missing */
709 if ((pinfo->fd->pkt_len != pinfo->fd->cap_len)
710 && (!rtpinfo->info_all_data_present)) {
711 saveinfo->saved = FALSE;
712 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
716 /* if padding bit is set, but the padding count is bigger
717 * then the whole RTP data - error with padding count */
718 if ( (rtpinfo->info_padding_set != FALSE)
719 && (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
720 saveinfo->saved = FALSE;
721 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
725 /* do we need to insert some silence? */
726 if ((rtpinfo->info_marker_set)
727 && ! (statinfo->flags & STAT_FLAG_FIRST)
728 && ! (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP)
729 && (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
730 /* the amount of silence should be the difference between
731 * the last timestamp and the current one minus x
732 * x should equal the amount of information in the last frame
733 * XXX not done yet */
735 (i < (statinfo->delta_timestamp - rtpinfo->info_payload_len - rtpinfo->info_padding_count))
736 && (i < MAX_SILENCE_TICKS);
738 switch (statinfo->reg_pt) {
749 nchars = fwrite(&tmp, 1, 1, saveinfo->fp);
751 /* Write error or short write */
752 saveinfo->saved = FALSE;
753 saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
758 fflush(saveinfo->fp);
762 if ((rtpinfo->info_payload_type == PT_CN)
763 || (rtpinfo->info_payload_type == PT_CN_OLD)) {
765 /* all other payloads */
767 if (!rtpinfo->info_all_data_present) {
768 /* Not all the data was captured. */
769 saveinfo->saved = FALSE;
770 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
774 /* we put the pointer at the beginning of the RTP
775 * payload, that is, at the beginning of the RTP data
776 * plus the offset of the payload from the beginning
778 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
779 nchars = fwrite(data, sizeof(unsigned char),
780 (rtpinfo->info_payload_len - rtpinfo->info_padding_count),
782 if (nchars != (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) {
783 /* Write error or short write */
784 saveinfo->saved = FALSE;
785 saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
788 saveinfo->count += (rtpinfo->info_payload_len - rtpinfo->info_padding_count);
790 fflush(saveinfo->fp);
791 saveinfo->saved = TRUE;
799 /****************************************************************************/
802 /****************************************************************************/
804 /****************************************************************************/
805 /* close the dialog window and remove the tap listener */
807 on_destroy(GtkWidget *win _U_, user_data_t *user_data)
809 /* remove tap listener */
810 remove_tap_listener(user_data);
812 /* close and remove temporary files */
813 if (user_data->forward.saveinfo.fp != NULL)
814 fclose(user_data->forward.saveinfo.fp);
815 if (user_data->reversed.saveinfo.fp != NULL)
816 fclose(user_data->reversed.saveinfo.fp);
817 /*XXX: test for error **/
818 ws_remove(user_data->f_tempname);
819 ws_remove(user_data->r_tempname);
821 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
822 /* destroy save_voice_as window if open */
823 if (user_data->dlg.save_voice_as_w != NULL)
824 window_destroy(user_data->dlg.save_voice_as_w);
826 /* destroy graph window if open */
827 if (user_data->dlg.dialog_graph.window != NULL)
828 window_destroy(user_data->dlg.dialog_graph.window);
830 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows
831 * is destroyed and cause an exception using GTK1*/
832 g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
834 g_free(user_data->f_tempname);
835 g_free(user_data->r_tempname);
840 /****************************************************************************/
842 on_notebook_switch_page(GtkNotebook *notebook _U_,
845 user_data_t *user_data)
847 user_data->dlg.selected_list =
848 (page_num == 0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
850 user_data->dlg.selected_list_row = 0;
853 /****************************************************************************/
856 on_list_select_row(GtkTreeSelection *selection,
857 user_data_t *user_data /*gpointer data */)
859 user_data->dlg.selected_list_sel = selection;
863 /****************************************************************************/
865 dialog_graph_set_title(user_data_t* user_data)
869 if (!user_data->dlg.dialog_graph.window) {
873 title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
874 ep_address_to_display(&(user_data->src_fwd)),
875 user_data->port_src_fwd,
876 ep_address_to_display(&(user_data->dst_fwd)),
877 user_data->port_dst_fwd,
878 ep_address_to_display(&(user_data->src_rev)),
879 user_data->port_src_rev,
880 ep_address_to_display(&(user_data->dst_rev)),
881 user_data->port_dst_rev);
883 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
889 /****************************************************************************/
891 dialog_graph_reset(user_data_t* user_data)
895 user_data->dlg.dialog_graph.needs_redraw = TRUE;
896 for (i = 0; i < MAX_GRAPHS; i++) {
897 for (j = 0; j < NUM_GRAPH_ITEMS; j++) {
898 dialog_graph_graph_item_t *dggi;
899 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
904 user_data->dlg.dialog_graph.last_interval = 0xffffffff;
905 user_data->dlg.dialog_graph.max_interval = 0;
906 user_data->dlg.dialog_graph.num_items = 0;
908 /* create the color titles near the filter buttons */
909 for (i = 0; i < MAX_GRAPHS; i++) {
911 if (i < (MAX_GRAPHS/2)) {
912 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
913 sizeof(user_data->dlg.dialog_graph.graph[0].title),
914 "%s: %s:%u to %s:%u (SSRC=0x%X)",
916 ep_address_to_display(&(user_data->src_fwd)),
917 user_data->port_src_fwd,
918 ep_address_to_display(&(user_data->dst_fwd)),
919 user_data->port_dst_fwd,
920 user_data->ssrc_fwd);
923 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
924 sizeof(user_data->dlg.dialog_graph.graph[0].title),
925 "%s: %s:%u to %s:%u (SSRC=0x%X)",
927 ep_address_to_display(&(user_data->src_rev)),
928 user_data->port_src_rev,
929 ep_address_to_display(&(user_data->dst_rev)),
930 user_data->port_dst_rev,
931 user_data->ssrc_rev);
935 dialog_graph_set_title(user_data);
938 /****************************************************************************/
940 get_it_value(dialog_graph_graph_t *dgg, int idx)
942 dialog_graph_graph_item_t *it;
944 it = &dgg->items[idx];
949 /****************************************************************************/
951 print_time_scale_string(char *buf, int buf_len, guint32 t)
954 g_snprintf(buf, buf_len, "%ds", t/1000000);
955 } else if (t >= 1000000) {
956 g_snprintf(buf, buf_len, "%d.%03ds", t/1000000, (t%1000000)/1000);
957 } else if (t >= 10000) {
958 g_snprintf(buf, buf_len, "%dms", t/1000);
959 } else if (t >= 1000) {
960 g_snprintf(buf, buf_len, "%d.%03dms", t/1000, t%1000);
962 g_snprintf(buf, buf_len, "%dus", t);
966 /****************************************************************************/
968 dialog_graph_draw(user_data_t* user_data)
971 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
972 gint32 current_interval;
973 guint32 left_x_border;
974 guint32 right_x_border;
975 guint32 top_y_border;
976 guint32 bottom_y_border;
978 int label_width, label_height;
979 int label_width_mid, label_height_mid;
980 guint32 draw_width, draw_height;
981 char label_string[15];
982 GtkAllocation widget_alloc;
986 guint32 num_time_intervals;
987 guint32 max_value; /* max value of seen data */
988 guint32 max_y; /* max value of the Y scale */
990 if (!user_data->dlg.dialog_graph.needs_redraw) {
993 user_data->dlg.dialog_graph.needs_redraw = FALSE;
996 * Find the length of the intervals we have data for
997 * so we know how large arrays we need to malloc()
999 num_time_intervals = user_data->dlg.dialog_graph.num_items;
1000 /* if there isn't anything to do, just return */
1001 if (num_time_intervals == 0) {
1004 num_time_intervals+=1;
1005 /* XXX move this check to _packet() */
1006 if (num_time_intervals>NUM_GRAPH_ITEMS) {
1007 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
1012 * find the max value so we can autoscale the y axis
1015 for (i = 0; i < MAX_GRAPHS; i++) {
1018 if (!user_data->dlg.dialog_graph.graph[i].display) {
1021 for (idx = 0; (guint32)(idx) < num_time_intervals; idx++) {
1024 val = get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1026 /* keep track of the max value we have encountered */
1027 if (val>max_value) {
1034 * Clear out old plot
1036 #if GTK_CHECK_VERSION(2,22,0)
1037 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1039 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1041 cairo_set_source_rgb (cr, 1, 1, 1);
1042 gtk_widget_get_allocation(user_data->dlg.dialog_graph.draw_area, &widget_alloc);
1043 cairo_rectangle (cr,
1047 widget_alloc.height);
1052 * Calculate the y scale we should use
1054 if (user_data->dlg.dialog_graph.max_y_units == AUTO_MAX_YSCALE) {
1055 max_y = yscale_max[MAX_YSCALE-1];
1056 for (i = MAX_YSCALE-1; i > 0; i--) {
1057 if (max_value<yscale_max[i]) {
1058 max_y = yscale_max[i];
1062 /* the user had specified an explicit y scale to use */
1063 max_y = user_data->dlg.dialog_graph.max_y_units;
1067 * Calculate size of borders surrounding the plot
1068 * The border on the right side needs to be adjusted depending
1069 * on the width of the text labels.
1071 print_time_scale_string(label_string, sizeof(label_string), max_y);
1072 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1073 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1074 print_time_scale_string(label_string, sizeof(label_string), max_y*5/10);
1075 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1076 pango_layout_get_pixel_size(layout, &label_width_mid, &label_height_mid);
1077 if (label_width_mid > label_width) {
1078 label_width = label_width_mid;
1079 label_height = label_height_mid;
1083 right_x_border = label_width + 20;
1085 bottom_y_border = label_height + 20;
1089 * Calculate the size of the drawing area for the actual plot
1091 draw_width = user_data->dlg.dialog_graph.surface_width - right_x_border - left_x_border;
1092 draw_height = user_data->dlg.dialog_graph.surface_height - top_y_border - bottom_y_border;
1096 * Draw the y axis and labels
1097 * (we always draw the y scale with 11 ticks along the axis)
1099 #if GTK_CHECK_VERSION(2,22,0)
1100 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1102 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1104 cairo_set_line_width (cr, 1.0);
1106 user_data->dlg.dialog_graph.surface_width - right_x_border + 1.5,
1107 top_y_border + 0.5);
1109 user_data->dlg.dialog_graph.surface_width - right_x_border + 1.5,
1110 user_data->dlg.dialog_graph.surface_height - bottom_y_border + 0.5);
1114 for (i = 0; i <= 10; i++) {
1119 /* first, middle and last tick are slightly longer */
1123 #if GTK_CHECK_VERSION(2,22,0)
1124 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1126 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1128 cairo_set_line_width (cr, 1.0);
1130 user_data->dlg.dialog_graph.surface_width - right_x_border + 1.5,
1131 user_data->dlg.dialog_graph.surface_height - bottom_y_border - draw_height*i/10 + 0.5);
1134 user_data->dlg.dialog_graph.surface_width - right_x_border + 1.5 + xwidth,
1135 user_data->dlg.dialog_graph.surface_height - bottom_y_border - draw_height*i/10 + 0.5);
1138 /* draw the labels */
1140 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1141 pango_layout_set_text(layout, label_string, -1);
1142 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1143 #if GTK_CHECK_VERSION(2,22,0)
1144 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1146 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1149 user_data->dlg.dialog_graph.surface_width - right_x_border + 15 + label_width - lwidth,
1150 user_data->dlg.dialog_graph.surface_height - bottom_y_border - draw_height*i/10 - label_height/2);
1151 pango_cairo_show_layout (cr, layout);
1156 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1157 pango_layout_set_text(layout, label_string, -1);
1158 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1159 #if GTK_CHECK_VERSION(2,22,0)
1160 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1162 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1165 user_data->dlg.dialog_graph.surface_width - right_x_border + 15 + label_width - lwidth,
1166 user_data->dlg.dialog_graph.surface_height - bottom_y_border - draw_height*i/10 - label_height/2);
1167 pango_cairo_show_layout (cr, layout);
1172 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1173 pango_layout_set_text(layout, label_string, -1);
1174 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1175 #if GTK_CHECK_VERSION(2,22,0)
1176 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1178 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1181 user_data->dlg.dialog_graph.surface_width - right_x_border + 15 + label_width - lwidth,
1182 user_data->dlg.dialog_graph.surface_height - bottom_y_border - draw_height*i/10 - label_height/2);
1183 pango_cairo_show_layout (cr, layout);
1192 * if we have not specified the last_interval via the gui,
1193 * then just pick the current end of the capture so that is scrolls
1194 * nicely when doing live captures
1196 if (user_data->dlg.dialog_graph.last_interval == 0xffffffff) {
1197 last_interval = user_data->dlg.dialog_graph.max_interval;
1199 last_interval = user_data->dlg.dialog_graph.last_interval;
1206 /* plot the x-scale */
1207 #if GTK_CHECK_VERSION(2,22,0)
1208 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1210 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1212 cairo_set_line_width (cr, 1.0);
1214 left_x_border + 0.5,
1215 user_data->dlg.dialog_graph.surface_height-bottom_y_border + 1.5);
1217 user_data->dlg.dialog_graph.surface_width - right_x_border + 1.5,
1218 user_data->dlg.dialog_graph.surface_height - bottom_y_border + 1.5);
1222 if ((last_interval/user_data->dlg.dialog_graph.interval) > draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1) {
1223 first_interval = (last_interval/user_data->dlg.dialog_graph.interval)
1224 - draw_width/user_data->dlg.dialog_graph.pixels_per_tick + 1;
1225 first_interval *= user_data->dlg.dialog_graph.interval;
1231 delta_multiplier = 5;
1232 while (interval_delta<((last_interval-first_interval)/10)) {
1233 interval_delta*=delta_multiplier;
1234 if (delta_multiplier == 5) {
1235 delta_multiplier = 2;
1237 delta_multiplier = 5;
1241 for (current_interval = last_interval;
1242 current_interval > (gint32)first_interval;
1243 current_interval = current_interval-user_data->dlg.dialog_graph.interval) {
1246 /* if pixels_per_tick is <5, only draw every 10 ticks */
1247 if ((user_data->dlg.dialog_graph.pixels_per_tick<10)
1248 && (current_interval%(10*user_data->dlg.dialog_graph.interval))) {
1252 if (current_interval%interval_delta) {
1258 x = draw_width+left_x_border
1259 - (((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)
1260 * user_data->dlg.dialog_graph.pixels_per_tick);
1261 #if GTK_CHECK_VERSION(2,22,0)
1262 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1264 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1266 cairo_set_line_width (cr, 1.0);
1268 x - 1 - user_data->dlg.dialog_graph.pixels_per_tick/2 + 0.5,
1269 user_data->dlg.dialog_graph.surface_height-bottom_y_border + 1.5);
1270 cairo_line_to(cr, x-1-user_data->dlg.dialog_graph.pixels_per_tick/2+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+xlen+1.5);
1275 if (user_data->dlg.dialog_graph.interval >= 1000) {
1276 g_snprintf(label_string, sizeof(label_string),
1277 "%ds", current_interval/1000);
1278 } else if (user_data->dlg.dialog_graph.interval >= 100) {
1279 g_snprintf(label_string, sizeof(label_string),
1280 "%d.%1ds", current_interval/1000, (current_interval/100)%10);
1281 } else if (user_data->dlg.dialog_graph.interval >= 10) {
1282 g_snprintf(label_string, sizeof(label_string),
1283 "%d.%2ds", current_interval/1000, (current_interval/10)%100);
1285 g_snprintf(label_string, sizeof(label_string),
1286 "%d.%3ds", current_interval/1000, current_interval%1000);
1288 pango_layout_set_text(layout, label_string, -1);
1289 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1290 #if GTK_CHECK_VERSION(2,22,0)
1291 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1293 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1296 x - 1 - user_data->dlg.dialog_graph.pixels_per_tick/2 - lwidth/2,
1297 user_data->dlg.dialog_graph.surface_height-bottom_y_border + 20);
1298 pango_cairo_show_layout (cr, layout);
1311 * Draw "x" for Sequence Errors and "m" for Marks
1313 /* Draw the labels Fwd and Rev */
1314 g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Fwd", sizeof(label_string));
1315 pango_layout_set_text(layout, label_string, -1);
1316 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1317 #if GTK_CHECK_VERSION(2,22,0)
1318 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1320 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1323 user_data->dlg.dialog_graph.surface_width - right_x_border + 33 - lwidth,
1324 user_data->dlg.dialog_graph.surface_height - bottom_y_border + 3);
1325 pango_cairo_show_layout (cr, layout);
1329 g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Rev", sizeof(label_string));
1330 pango_layout_set_text(layout, label_string, -1);
1331 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1332 #if GTK_CHECK_VERSION(2,22,0)
1333 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1335 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1338 user_data->dlg.dialog_graph.surface_width - right_x_border + 33 - lwidth,
1339 user_data->dlg.dialog_graph.surface_height - bottom_y_border + 3 + 9);
1340 pango_cairo_show_layout (cr, layout);
1344 /* Draw the marks */
1345 for (i = MAX_GRAPHS-1; i >= 0; i--) {
1347 guint32 x_pos/*, prev_x_pos*/;
1349 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1350 if (!user_data->dlg.dialog_graph.graph[i].display) {
1353 /* initialize prev x/y to the low left corner of the graph */
1354 /*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;*/
1356 for (interval = first_interval+user_data->dlg.dialog_graph.interval;
1357 interval <= last_interval;
1358 interval += user_data->dlg.dialog_graph.interval) {
1359 x_pos = draw_width - 1
1360 - (user_data->dlg.dialog_graph.pixels_per_tick
1361 * ((last_interval-interval)/user_data->dlg.dialog_graph.interval + 1))
1364 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags &
1365 (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER|STAT_FLAG_DUP_PKT)) {
1366 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags &
1367 (STAT_FLAG_WRONG_SEQ|STAT_FLAG_DUP_PKT)) {
1368 g_strlcpy(label_string, "x", sizeof(label_string));
1370 g_strlcpy(label_string, "m", sizeof(label_string));
1373 pango_layout_set_text(layout, label_string, -1);
1374 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1375 #if GTK_CHECK_VERSION(2,22,0)
1376 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1378 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1381 x_pos - 1 - lwidth/2,
1382 user_data->dlg.dialog_graph.surface_height - bottom_y_border + 3 + 7*(i/2));
1383 pango_cairo_show_layout (cr, layout);
1389 /*prev_x_pos = x_pos;*/
1393 g_object_unref(G_OBJECT(layout));
1396 * Loop over all graphs and draw them
1398 for (i = MAX_GRAPHS-1; i >= 0; i--) {
1400 guint32 x_pos, y_pos, /*prev_x_pos,*/ prev_y_pos;
1401 if (! user_data->dlg.dialog_graph.graph[i].display) {
1404 /* initialize prev x/y to the low left corner of the graph */
1405 /*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;*/
1406 prev_y_pos = draw_height-1+top_y_border;
1408 for (interval = first_interval+user_data->dlg.dialog_graph.interval;
1409 interval <= last_interval;
1410 interval+=user_data->dlg.dialog_graph.interval) {
1412 x_pos = draw_width - 1
1413 - (user_data->dlg.dialog_graph.pixels_per_tick
1414 * ((last_interval-interval)/user_data->dlg.dialog_graph.interval+1))
1416 val = get_it_value(&user_data->dlg.dialog_graph.graph[i],
1417 interval/user_data->dlg.dialog_graph.interval);
1421 y_pos = draw_height - 1 - (val*draw_height)/max_y + top_y_border;
1424 /* don't need to draw anything if the segment
1425 * is entirely above the top of the graph
1427 if ( (prev_y_pos == 0) && (y_pos == 0) ) {
1429 /*prev_x_pos = x_pos;*/
1434 #if GTK_CHECK_VERSION(2,22,0)
1435 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1437 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1439 gdk_cairo_set_source_rgba (cr, &user_data->dlg.dialog_graph.graph[i].rgba_color);
1440 cairo_set_line_width (cr, 1.0);
1441 cairo_move_to(cr, x_pos + 0.5, draw_height - 1 + top_y_border + 0.5);
1442 cairo_line_to(cr, x_pos + 0.5, y_pos + 0.5);
1448 /*prev_x_pos = x_pos;*/
1452 cr = gdk_cairo_create (gtk_widget_get_window(user_data->dlg.dialog_graph.draw_area));
1454 #if GTK_CHECK_VERSION(2,22,0)
1455 cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1457 gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1459 cairo_rectangle (cr, 0, 0,
1460 user_data->dlg.dialog_graph.surface_width,
1461 user_data->dlg.dialog_graph.surface_height);
1466 /* update the scrollbar */
1467 gtk_adjustment_set_upper(user_data->dlg.dialog_graph.scrollbar_adjustment,
1468 (gfloat) user_data->dlg.dialog_graph.max_interval);
1469 gtk_adjustment_set_step_increment(user_data->dlg.dialog_graph.scrollbar_adjustment,
1470 (gfloat) ((last_interval - first_interval)/10));
1471 gtk_adjustment_set_page_increment(user_data->dlg.dialog_graph.scrollbar_adjustment,
1472 (gfloat) (last_interval - first_interval));
1473 if (((last_interval-first_interval)*100) < user_data->dlg.dialog_graph.max_interval) {
1474 gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment,
1475 (gfloat) (user_data->dlg.dialog_graph.max_interval/100));
1477 gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment,
1478 (gfloat) (last_interval - first_interval));
1480 gtk_adjustment_set_value(user_data->dlg.dialog_graph.scrollbar_adjustment,
1482 - gtk_adjustment_get_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment));
1483 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1484 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1488 /****************************************************************************/
1490 dialog_graph_redraw(user_data_t* user_data)
1492 user_data->dlg.dialog_graph.needs_redraw = TRUE;
1493 dialog_graph_draw(user_data);
1496 /****************************************************************************/
1498 quit(GtkWidget *widget _U_, user_data_t *user_data)
1500 GtkWidget *bt_save = (GtkWidget *)g_object_get_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save");
1501 surface_info_t *surface_info = (surface_info_t *)g_object_get_data(G_OBJECT(bt_save), "surface-info");
1503 g_free(surface_info);
1504 user_data->dlg.dialog_graph.window = NULL;
1507 /****************************************************************************/
1508 #if GTK_CHECK_VERSION(3,0,0)
1510 draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
1512 user_data_t *user_data = (user_data_t *)data;
1513 GtkAllocation allocation;
1515 gtk_widget_get_allocation (widget, &allocation);
1517 cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1518 cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
1525 expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
1527 user_data_t *user_data = (user_data_t *)data;
1528 cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
1531 #if GTK_CHECK_VERSION(2,22,0)
1532 cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1534 gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1536 cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
1545 /****************************************************************************/
1547 configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer data)
1549 user_data_t *user_data = (user_data_t *)data;
1551 GtkAllocation widget_alloc;
1553 #if GTK_CHECK_VERSION(2,22,0)
1554 surface_info_t *surface_info = g_new(surface_info_t, 1);
1561 #if GTK_CHECK_VERSION(2,22,0)
1562 if (user_data->dlg.dialog_graph.surface) {
1563 g_object_unref(user_data->dlg.dialog_graph.surface);
1564 user_data->dlg.dialog_graph.surface = NULL;
1566 gtk_widget_get_allocation(widget, &widget_alloc);
1567 user_data->dlg.dialog_graph.surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
1568 CAIRO_CONTENT_COLOR,
1570 widget_alloc.height);
1572 if (user_data->dlg.dialog_graph.pixmap) {
1573 g_object_unref(user_data->dlg.dialog_graph.pixmap);
1574 user_data->dlg.dialog_graph.pixmap = NULL;
1577 gtk_widget_get_allocation(widget, &widget_alloc);
1578 user_data->dlg.dialog_graph.pixmap = gdk_pixmap_new(gtk_widget_get_window(widget),
1580 widget_alloc.height,
1583 user_data->dlg.dialog_graph.surface_width = widget_alloc.width;
1584 user_data->dlg.dialog_graph.surface_height = widget_alloc.height;
1586 bt_save = (GtkWidget *)g_object_get_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save");
1587 #if GTK_CHECK_VERSION(2,22,0)
1588 surface_info->surface = user_data->dlg.dialog_graph.surface;
1589 surface_info->width = widget_alloc.width;
1590 surface_info->height = widget_alloc.height;
1591 g_object_set_data(G_OBJECT(bt_save), "surface-info", surface_info);
1592 gtk_widget_set_sensitive(bt_save, TRUE);
1594 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1596 g_object_set_data(G_OBJECT(bt_save), "pixmap", user_data->dlg.dialog_graph.pixmap);
1597 gtk_widget_set_sensitive(bt_save, TRUE);
1599 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1601 cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
1602 cairo_set_source_rgb (cr, 1, 1, 1);
1606 dialog_graph_redraw(user_data);
1610 /****************************************************************************/
1612 scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1614 user_data_t *user_data = (user_data_t *)data;
1617 mi = (guint32) (gtk_adjustment_get_value(user_data->dlg.dialog_graph.scrollbar_adjustment)
1618 + gtk_adjustment_get_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment));
1619 if (user_data->dlg.dialog_graph.last_interval == mi) {
1622 if ( (user_data->dlg.dialog_graph.last_interval == 0xffffffff)
1623 && (mi == user_data->dlg.dialog_graph.max_interval) ) {
1627 user_data->dlg.dialog_graph.last_interval =
1628 (mi / user_data->dlg.dialog_graph.interval) * user_data->dlg.dialog_graph.interval;
1630 dialog_graph_redraw(user_data);
1634 /****************************************************************************/
1636 create_draw_area(user_data_t* user_data, GtkWidget *box)
1638 user_data->dlg.dialog_graph.draw_area = gtk_drawing_area_new();
1639 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1641 gtk_widget_set_size_request(user_data->dlg.dialog_graph.draw_area,
1642 user_data->dlg.dialog_graph.surface_width,
1643 user_data->dlg.dialog_graph.surface_height);
1645 /* signals needed to handle backing pixmap */
1646 #if GTK_CHECK_VERSION(3,0,0)
1647 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "draw", G_CALLBACK(draw_area_draw), user_data);
1649 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), user_data);
1651 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1653 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1654 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1656 /* create the associated scrollbar */
1657 user_data->dlg.dialog_graph.scrollbar_adjustment = (GtkAdjustment *)gtk_adjustment_new(0, 0, 0, 0, 0, 0);
1658 user_data->dlg.dialog_graph.scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL,
1659 user_data->dlg.dialog_graph.scrollbar_adjustment);
1660 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1661 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1662 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment,
1664 G_CALLBACK(scrollbar_changed),
1668 /****************************************************************************/
1670 disable_graph(dialog_graph_graph_t *dgg)
1673 dgg->display = FALSE;
1674 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1679 /****************************************************************************/
1681 filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1683 /* this graph is not active, just update display and redraw */
1684 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))) {
1686 dialog_graph_redraw(dgg->ud);
1691 cf_retap_packets(&cfile);
1692 dialog_graph_redraw(dgg->ud);
1697 /****************************************************************************/
1699 create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *vbox, int num)
1705 hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
1706 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1707 gtk_widget_show(hbox);
1709 g_snprintf(str, sizeof(str), "Graph %d", num);
1710 dgg->display_button = gtk_toggle_button_new_with_label(str);
1711 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1712 gtk_widget_show(dgg->display_button);
1713 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1714 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1716 label = gtk_label_new(dgg->title);
1717 gtk_widget_show(label);
1718 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1720 #if GTK_CHECK_VERSION(3,0,0)
1721 gtk_widget_override_color(label, GTK_STATE_FLAG_NORMAL, &dgg->rgba_color);
1722 gtk_widget_override_color(label, GTK_STATE_FLAG_ACTIVE, &dgg->rgba_color);
1723 gtk_widget_override_color(label, GTK_STATE_FLAG_PRELIGHT, &dgg->rgba_color);
1724 gtk_widget_override_color(label, GTK_STATE_FLAG_SELECTED, &dgg->rgba_color);
1725 gtk_widget_override_color(label, GTK_STATE_FLAG_INSENSITIVE, &dgg->rgba_color);
1727 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1728 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1729 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1730 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1731 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1736 /****************************************************************************/
1738 create_filter_area(user_data_t* user_data, GtkWidget *box)
1745 frame = gtk_frame_new("Graphs");
1746 gtk_box_pack_start(GTK_BOX(box), frame, TRUE, TRUE, 0);
1747 gtk_widget_show(frame);
1749 vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 1, FALSE);
1750 gtk_container_add(GTK_CONTAINER(frame), vbox);
1751 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1752 gtk_widget_show(vbox);
1754 for (i = 0; i < MAX_GRAPHS; i++) {
1755 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1758 label = gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1759 gtk_widget_show(label);
1760 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1765 /****************************************************************************/
1767 yscale_select(GtkWidget *item, gpointer key)
1770 user_data_t *user_data;
1772 user_data = (user_data_t *)key;
1773 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1775 user_data->dlg.dialog_graph.max_y_units = yscale_max[i];
1776 dialog_graph_redraw(user_data);
1779 /****************************************************************************/
1781 pixels_per_tick_select(GtkWidget *item, gpointer key)
1784 user_data_t *user_data;
1786 user_data = (user_data_t *)key;
1787 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1789 user_data->dlg.dialog_graph.pixels_per_tick = pixels_per_tick[i];
1790 dialog_graph_redraw(user_data);
1793 /****************************************************************************/
1795 tick_interval_select(GtkWidget *item, gpointer key)
1798 user_data_t *user_data;
1800 user_data = (user_data_t *)key;
1801 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1803 user_data->dlg.dialog_graph.interval = tick_interval_values[i];
1804 cf_retap_packets(&cfile);
1805 dialog_graph_redraw(user_data);
1808 /****************************************************************************/
1810 create_yscale_max_menu_items(user_data_t* user_data)
1813 GtkWidget *combo_box;
1816 combo_box = gtk_combo_box_text_new();
1818 for (i = 0; i < MAX_YSCALE; i++) {
1819 if (yscale_max[i] == AUTO_MAX_YSCALE) {
1820 g_strlcpy(str, "Auto", sizeof(str));
1821 } else if (yscale_max[i] < 1000000) {
1822 g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1824 g_snprintf(str, sizeof(str), "%u s", yscale_max[i]/1000000);
1826 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1828 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_YSCALE_INDEX);
1829 g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), (gpointer)user_data);
1834 /****************************************************************************/
1836 create_pixels_per_tick_menu_items(user_data_t *user_data)
1839 GtkWidget *combo_box;
1842 combo_box = gtk_combo_box_text_new();
1844 for (i = 0; i < MAX_PIXELS_PER_TICK; i++) {
1845 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1846 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1848 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PIXELS_PER_TICK_INDEX);
1850 g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), (gpointer)user_data);
1855 /****************************************************************************/
1857 create_tick_interval_menu_items(user_data_t *user_data)
1859 GtkWidget *combo_box;
1863 combo_box = gtk_combo_box_text_new();
1865 for (i = 0; i < MAX_TICK_VALUES; i++) {
1866 if (tick_interval_values[i] >= 1000) {
1867 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1868 } else if (tick_interval_values[i] >= 100) {
1869 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1870 } else if (tick_interval_values[i] >= 10) {
1871 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1873 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1875 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1877 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_TICK_VALUE_INDEX);
1878 g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1883 /****************************************************************************/
1885 create_ctrl_menu(user_data_t* user_data, GtkWidget *vbox, const char *name, GtkWidget *(*func)(user_data_t* user_data))
1889 GtkWidget *combo_box;
1891 hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0, FALSE);
1892 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1893 gtk_widget_show(hbox);
1895 label = gtk_label_new(name);
1896 gtk_widget_show(label);
1897 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1899 combo_box = (*func)(user_data);
1900 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1901 gtk_widget_show(combo_box);
1904 /****************************************************************************/
1906 create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1908 GtkWidget *frame_vbox;
1912 frame_vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1913 gtk_box_pack_start(GTK_BOX(box), frame_vbox, TRUE, TRUE, 0);
1914 gtk_widget_show(frame_vbox);
1916 frame = gtk_frame_new("X Axis");
1917 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1918 gtk_widget_show(frame);
1920 vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1921 gtk_container_add(GTK_CONTAINER(frame), vbox);
1922 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1923 gtk_widget_show(vbox);
1925 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1926 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1928 frame = gtk_frame_new("Y Axis");
1929 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1930 gtk_widget_show(frame);
1932 vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1933 gtk_container_add(GTK_CONTAINER(frame), vbox);
1934 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1935 gtk_widget_show(vbox);
1937 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1942 /****************************************************************************/
1944 dialog_graph_init_window(user_data_t* user_data)
1948 GtkWidget *bt_close;
1951 /* create the main window */
1952 user_data->dlg.dialog_graph.window = dlg_window_new("I/O Graphs"); /* transient_for top_level */
1954 vbox = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
1955 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1956 gtk_widget_show(vbox);
1958 create_draw_area(user_data, vbox);
1960 hbox = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3, FALSE);
1961 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1962 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1963 gtk_widget_show(hbox);
1965 create_filter_area(user_data, hbox);
1966 create_ctrl_area(user_data, hbox);
1968 dialog_graph_set_title(user_data);
1970 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE, NULL);
1971 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1972 gtk_widget_show(hbox);
1974 bt_close = (GtkWidget *)g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1975 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1977 bt_save = (GtkWidget *)g_object_get_data(G_OBJECT(hbox), GTK_STOCK_SAVE);
1978 gtk_widget_set_sensitive(bt_save, FALSE);
1979 gtk_widget_set_tooltip_text(bt_save, "Save the displayed graph to a file");
1980 g_signal_connect(bt_save, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
1981 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save", bt_save);
1983 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1985 gtk_widget_show(user_data->dlg.dialog_graph.window);
1986 window_present(user_data->dlg.dialog_graph.window);
1991 /****************************************************************************/
1993 on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1995 if (user_data->dlg.dialog_graph.window != NULL) {
1996 /* There's already a graph window; reactivate it. */
1997 reactivate_window(user_data->dlg.dialog_graph.window);
2001 dialog_graph_init_window(user_data);
2005 /****************************************************************************/
2008 on_goto_bt_clicked_lst(GtkWidget *bt _U_, user_data_t *user_data _U_)
2011 GtkTreeModel *model;
2012 GtkTreeSelection *selection;
2015 selection = user_data->dlg.selected_list_sel;
2017 if (selection == NULL)
2020 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2021 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
2022 cf_goto_frame(&cfile, fnumber);
2027 static void draw_stat(user_data_t *user_data);
2029 /****************************************************************************/
2030 /* re-dissects all packets */
2032 on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
2034 GString *error_string;
2036 /* remove tap listener */
2037 remove_tap_listener(user_data);
2039 /* register tap listener */
2040 error_string = register_tap_listener("rtp", user_data, NULL, 0,
2041 rtp_reset, rtp_packet, rtp_draw);
2042 if (error_string != NULL) {
2043 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
2044 g_string_free(error_string, TRUE);
2048 /* retap all packets */
2049 cf_retap_packets(&cfile);
2051 /* draw statistics info */
2052 draw_stat(user_data);
2056 #ifdef HAVE_LIBPORTAUDIO
2057 /****************************************************************************/
2059 on_player_bt_clicked(GtkButton *button _U_, gpointer user_data _U_)
2061 rtp_player_init(NULL);
2063 #endif /* HAVE_LIBPORTAUDIO */
2066 on_next_bt_clicked_list(GtkWidget *bt _U_, user_data_t *user_data _U_)
2069 GtkTreeModel *model;
2071 GtkTreeSelection *selection;
2074 selection = user_data->dlg.selected_list_sel;
2076 if (selection == NULL)
2080 if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
2081 while (gtk_tree_model_iter_next (model, &iter)) {
2082 gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
2083 if (strcmp(text, OK_TEXT) != 0) {
2084 gtk_tree_selection_select_iter (selection, &iter);
2085 path = gtk_tree_model_get_path(model, &iter);
2086 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
2089 gtk_tree_path_free(path);
2096 if (user_data->dlg.number_of_nok>1) {
2097 /* Get the first iter and select it before starting over */
2098 gtk_tree_model_get_iter_first(model, &iter);
2099 gtk_tree_selection_select_iter (selection, &iter);
2106 /****************************************************************************/
2107 /* when we want to save the information */
2109 save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
2112 GtkWidget *rev, *forw, *both;
2113 user_data_t *user_data;
2115 GtkListStore *store;
2117 GtkTreeModel *model;
2118 gboolean more_items = TRUE;
2120 /* To Hold data from the list row */
2121 guint32 packet; /* Packet */
2122 guint16 sequence; /* Sequence */
2123 guint32 timestamp; /* timestamp */
2124 gfloat delta; /* Delta(ms) */
2125 gfloat jitter; /* Jitter(ms) */
2126 gfloat skew; /* Skew(ms) */
2127 gfloat ipbw; /* IP BW(kbps) */
2128 gboolean marker; /* Marker */
2129 char *status_str; /* Status */
2130 char *date_str; /* Date */
2131 guint length; /* Length */
2136 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2138 /* Perhaps the user specified a directory instead of a file.
2139 * Check whether they did.
2141 if (test_for_directory(g_dest) == EISDIR) {
2142 /* It's a directory - set the file selection box to display it. */
2143 set_last_open_dir(g_dest);
2145 file_selection_set_current_folder((GtkWidget *)fc, get_last_open_dir());
2146 gtk_file_chooser_set_current_name((GtkFileChooser *)fc, "");
2147 return FALSE; /* run the dialog again */
2150 rev = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2151 forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
2152 both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
2153 user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
2155 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(forw))
2156 || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2157 fp = ws_fopen(g_dest, "w");
2159 open_failure_alert_box(g_dest, errno, TRUE);
2161 return TRUE; /* we're done */
2164 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2165 fprintf(fp, "Forward\n");
2167 write_failure_alert_box(g_dest, errno);
2170 return TRUE; /* we're done */
2174 for (j = 0; j < NUM_COLS; j++) {
2176 fprintf(fp, "\"%s\"", titles[j]);
2178 fprintf(fp, ",\"%s\"", titles[j]);
2183 write_failure_alert_box(g_dest, errno);
2188 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
2189 store = GTK_LIST_STORE(model);
2190 if ( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2192 while (more_items) {
2193 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2194 PACKET_COLUMN, &packet,
2195 SEQUENCE_COLUMN, &sequence,
2196 TIMESTAMP_COLUMN, ×tamp,
2197 DELTA_COLUMN, &delta,
2198 JITTER_COLUMN, &jitter,
2201 MARKER_COLUMN, &marker,
2202 STATUS_COLUMN, &status_str,
2203 DATE_COLUMN, &date_str,
2204 LENGTH_COLUMN, &length,
2206 fprintf(fp, "\"%u\"", packet);
2207 fprintf(fp, ",\"%u\"", sequence);
2208 fprintf(fp, ",\"%u\"", timestamp);
2209 fprintf(fp, ",\"%.2f\"", delta);
2210 fprintf(fp, ",\"%.2f\"", jitter);
2211 fprintf(fp, ",\"%.2f\"", skew);
2212 fprintf(fp, ",\"%.2f\"", ipbw);
2213 fprintf(fp, ",\"%s\"", marker? "SET" : "");
2214 fprintf(fp, ",\"%s\"", status_str);
2215 fprintf(fp, ",\"%s\"", date_str);
2216 fprintf(fp, ",\"%u\"", length);
2221 write_failure_alert_box(g_dest, errno);
2224 return TRUE; /* we're done */
2227 more_items = gtk_tree_model_iter_next (model, &iter);
2231 if (fclose(fp) == EOF) {
2232 write_failure_alert_box(g_dest, errno);
2234 return TRUE; /* we're done */
2238 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rev))
2239 || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2241 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2242 fp = ws_fopen(g_dest, "a");
2244 open_failure_alert_box(g_dest, errno, TRUE);
2246 return TRUE; /* we're done */
2248 fprintf(fp, "\nReverse\n");
2250 write_failure_alert_box(g_dest, errno);
2253 return TRUE; /* we're done */
2256 fp = ws_fopen(g_dest, "w");
2258 open_failure_alert_box(g_dest, errno, TRUE);
2260 return TRUE; /* we're done */
2263 for (j = 0; j < NUM_COLS; j++) {
2265 fprintf(fp, "\"%s\"", titles[j]);
2267 fprintf(fp, ",\"%s\"", titles[j]);
2272 write_failure_alert_box(g_dest, errno);
2275 return TRUE; /* we're done */
2277 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
2278 store = GTK_LIST_STORE(model);
2279 if ( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2283 while (more_items) {
2284 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2285 PACKET_COLUMN, &packet,
2286 SEQUENCE_COLUMN, &sequence,
2287 TIMESTAMP_COLUMN, ×tamp,
2288 DELTA_COLUMN, &delta,
2289 JITTER_COLUMN, &jitter,
2292 MARKER_COLUMN, &marker,
2293 STATUS_COLUMN, &status_str,
2294 DATE_COLUMN, &date_str,
2295 LENGTH_COLUMN, &length,
2297 fprintf(fp, "\"%u\"", packet);
2298 fprintf(fp, ",\"%u\"", sequence);
2299 fprintf(fp, ",\"%u\"", timestamp);
2300 fprintf(fp, ",\"%.2f\"", delta);
2301 fprintf(fp, ",\"%.2f\"", jitter);
2302 fprintf(fp, ",\"%.2f\"", skew);
2303 fprintf(fp, ",\"%.2f\"", ipbw);
2304 fprintf(fp, ",\"%s\"", marker? "SET" : "");
2305 fprintf(fp, ",\"%s\"", status_str);
2306 fprintf(fp, ",\"%s\"", date_str);
2307 fprintf(fp, ",\"%u\"", length);
2312 write_failure_alert_box(g_dest, errno);
2315 return TRUE; /* we're done */
2318 more_items = gtk_tree_model_iter_next (model, &iter);
2321 if (fclose(fp) == EOF) {
2322 write_failure_alert_box(g_dest, errno);
2324 return TRUE; /* we're done */
2329 return TRUE; /* we're done */
2333 save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2335 user_data->dlg.save_csv_as_w = NULL;
2338 /* when the user wants to save the csv information in a file */
2340 save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
2344 GtkWidget *label_format;
2345 GtkWidget *channels_label;
2346 GtkWidget *forward_rb;
2347 GtkWidget *reversed_rb;
2350 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2351 if (user_data->dlg.save_csv_as_w != NULL) {
2352 /* There's already a Save CSV info dialog box; reactivate it. */
2353 reactivate_window(user_data->dlg.save_csv_as_w);
2357 user_data->dlg.save_csv_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2358 GTK_WINDOW(user_data->dlg.notebook),
2359 GTK_FILE_CHOOSER_ACTION_SAVE,
2360 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2361 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2363 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
2365 /* Container for each row of widgets */
2366 vertb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
2367 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2368 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2369 gtk_widget_show (vertb);
2371 grid1 = ws_gtk_grid_new ();
2372 gtk_widget_show (grid1);
2373 gtk_box_pack_start (GTK_BOX (vertb), grid1, FALSE, FALSE, 0);
2374 gtk_container_set_border_width (GTK_CONTAINER (grid1), 10);
2375 ws_gtk_grid_set_row_spacing (GTK_GRID (grid1), 20);
2377 label_format = gtk_label_new ("Format: Comma Separated Values");
2378 gtk_widget_show (label_format);
2379 ws_gtk_grid_attach_extended (GTK_GRID (grid1), label_format, 0, 0, 3, 1,
2380 (GtkAttachOptions) (GTK_FILL),
2381 (GtkAttachOptions) (0), 0, 0);
2383 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2386 channels_label = gtk_label_new ("Channels: ");
2387 gtk_widget_show (channels_label);
2388 ws_gtk_grid_attach_extended (GTK_GRID (grid1), channels_label, 0, 1, 1, 1,
2389 (GtkAttachOptions) (GTK_FILL),
2390 (GtkAttachOptions) (0), 0, 0);
2391 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2393 forward_rb = gtk_radio_button_new_with_label (NULL, "forward ");
2394 gtk_widget_show (forward_rb);
2395 ws_gtk_grid_attach_extended (GTK_GRID (grid1), forward_rb, 1, 1, 1, 1,
2396 (GtkAttachOptions) (GTK_FILL),
2397 (GtkAttachOptions) (0), 0, 0);
2399 reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed ");
2400 gtk_widget_show (reversed_rb);
2401 ws_gtk_grid_attach_extended (GTK_GRID (grid1), reversed_rb, 2, 1, 1, 1,
2402 (GtkAttachOptions) (GTK_FILL),
2403 (GtkAttachOptions) (0), 0, 0);
2405 both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
2406 gtk_widget_show (both_rb);
2407 ws_gtk_grid_attach_extended (GTK_GRID (grid1), both_rb, 3, 1, 1, 1,
2408 (GtkAttachOptions) (GTK_FILL),
2409 (GtkAttachOptions) (0), 0, 0);
2411 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2413 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2414 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2415 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2416 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2418 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2419 G_CALLBACK(window_delete_event_cb), NULL);
2420 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2421 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2423 gtk_widget_show(user_data->dlg.save_csv_as_w);
2424 window_present(user_data->dlg.save_csv_as_w);
2426 /* "Run" the GtkFileChooserDialog. */
2427 /* Upon exit: If "Accept" run the OK callback. */
2428 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2429 /* Destroy the window. */
2430 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2431 /* return with a TRUE status so that the dialog window will be destroyed. */
2432 /* Trying to re-run the dialog after popping up an alert box will not work */
2433 /* since the user will not be able to dismiss the alert box. */
2434 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2435 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2437 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2438 /* GtkFileChooserDialog. */
2439 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
2440 if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
2441 break; /* we're done */
2444 window_destroy(user_data->dlg.save_csv_as_w);
2448 /****************************************************************************/
2450 save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2452 /* Note that we no longer have a Save voice info dialog box. */
2453 user_data->dlg.save_voice_as_w = NULL;
2456 /****************************************************************************/
2457 /* here we save it into a file that user specified */
2458 /* XXX what about endians here? could go something wrong? */
2461 copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2463 FILE *to_stream, *forw_stream, *rev_stream;
2464 size_t fwritten, rwritten;
2465 int f_rawvalue, r_rawvalue, rawvalue;
2468 guint32 f_write_silence = 0;
2469 guint32 r_write_silence = 0;
2471 guint32 progbar_count, progbar_quantum;
2472 guint32 progbar_nextstep = 0;
2474 gboolean stop_flag = FALSE;
2478 forw_stream = ws_fopen(user_data->f_tempname, "rb");
2479 if (forw_stream == NULL)
2481 rev_stream = ws_fopen(user_data->r_tempname, "rb");
2482 if (rev_stream == NULL) {
2483 fclose(forw_stream);
2487 /* open file for saving */
2488 to_stream = ws_fopen(dest, "wb");
2489 if (to_stream == NULL) {
2490 fclose(forw_stream);
2495 progbar = create_progress_dlg(top_level, "Saving voice in a file", dest, TRUE, &stop_flag);
2497 if (format == SAVE_AU_FORMAT) /* au format */
2499 /* First we write the .au header. XXX Hope this is endian independent */
2500 /* the magic word 0x2e736e64 == .snd */
2501 phton32(pd, 0x2e736e64);
2502 nchars = fwrite(pd, 1, 4, to_stream);
2505 /* header offset == 24 bytes */
2507 nchars = fwrite(pd, 1, 4, to_stream);
2510 /* total length; it is permitted to set this to 0xffffffff */
2512 nchars = fwrite(pd, 1, 4, to_stream);
2515 /* encoding format == 16-bit linear PCM */
2517 nchars = fwrite(pd, 1, 4, to_stream);
2520 /* sample rate == 8000 Hz */
2522 nchars = fwrite(pd, 1, 4, to_stream);
2527 nchars = fwrite(pd, 1, 4, to_stream);
2533 /* only forward direction */
2534 case SAVE_FORWARD_DIRECTION_MASK: {
2535 progbar_count = user_data->forward.saveinfo.count;
2536 progbar_quantum = user_data->forward.saveinfo.count/100;
2537 while ((f_rawvalue = getc(forw_stream)) != EOF) {
2540 if ((count > progbar_nextstep) && (count <= progbar_count)) {
2541 update_progress_dlg(progbar,
2542 (gfloat) count/progbar_count, "Saving");
2543 progbar_nextstep = progbar_nextstep + progbar_quantum;
2547 if (user_data->forward.statinfo.pt == PT_PCMU) {
2548 sample = ulaw2linear((unsigned char)f_rawvalue);
2549 phton16(pd, sample);
2551 else if (user_data->forward.statinfo.pt == PT_PCMA) {
2552 sample = alaw2linear((unsigned char)f_rawvalue);
2553 phton16(pd, sample);
2559 fwritten = fwrite(pd, 1, 2, to_stream);
2566 /* only reversed direction */
2567 case SAVE_REVERSE_DIRECTION_MASK: {
2568 progbar_count = user_data->reversed.saveinfo.count;
2569 progbar_quantum = user_data->reversed.saveinfo.count/100;
2570 while ((r_rawvalue = getc(rev_stream)) != EOF) {
2573 if ((count > progbar_nextstep) && (count <= progbar_count)) {
2574 update_progress_dlg(progbar,
2575 (gfloat) count/progbar_count, "Saving");
2576 progbar_nextstep = progbar_nextstep + progbar_quantum;
2580 if (user_data->reversed.statinfo.pt == PT_PCMU) {
2581 sample = ulaw2linear((unsigned char)r_rawvalue);
2582 phton16(pd, sample);
2584 else if (user_data->reversed.statinfo.pt == PT_PCMA) {
2585 sample = alaw2linear((unsigned char)r_rawvalue);
2586 phton16(pd, sample);
2592 rwritten = fwrite(pd, 1, 2, to_stream);
2599 /* both directions */
2600 case SAVE_BOTH_DIRECTION_MASK: {
2601 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2602 (progbar_count = user_data->forward.saveinfo.count) :
2603 (progbar_count = user_data->reversed.saveinfo.count);
2604 progbar_quantum = progbar_count/100;
2605 /* since conversation in one way can start later than in the other one,
2606 * we have to write some silence information for one channel */
2607 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2608 f_write_silence = (guint32)
2609 ((user_data->forward.statinfo.start_time
2610 - user_data->reversed.statinfo.start_time)
2613 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2614 r_write_silence = (guint32)
2615 ((user_data->reversed.statinfo.start_time
2616 - user_data->forward.statinfo.start_time)
2622 if ((count > progbar_nextstep) && (count <= progbar_count)) {
2623 update_progress_dlg(progbar,
2624 (gfloat) count/progbar_count, "Saving");
2625 progbar_nextstep = progbar_nextstep + progbar_quantum;
2628 if (f_write_silence > 0) {
2629 r_rawvalue = getc(rev_stream);
2630 switch (user_data->forward.statinfo.reg_pt) {
2632 f_rawvalue = SILENCE_PCMU;
2635 f_rawvalue = SILENCE_PCMA;
2643 else if (r_write_silence > 0) {
2644 f_rawvalue = getc(forw_stream);
2645 switch (user_data->reversed.statinfo.reg_pt) {
2647 r_rawvalue = SILENCE_PCMU;
2650 r_rawvalue = SILENCE_PCMA;
2659 f_rawvalue = getc(forw_stream);
2660 r_rawvalue = getc(rev_stream);
2662 if ((r_rawvalue == EOF) && (f_rawvalue == EOF))
2664 if ((user_data->forward.statinfo.pt == PT_PCMU)
2665 && (user_data->reversed.statinfo.pt == PT_PCMU)) {
2666 sample = (ulaw2linear((unsigned char)r_rawvalue)
2667 + ulaw2linear((unsigned char)f_rawvalue)) / 2;
2668 phton16(pd, sample);
2670 else if ((user_data->forward.statinfo.pt == PT_PCMA)
2671 && (user_data->reversed.statinfo.pt == PT_PCMA)) {
2672 sample = (alaw2linear((unsigned char)r_rawvalue)
2673 + alaw2linear((unsigned char)f_rawvalue)) / 2;
2674 phton16(pd, sample);
2682 rwritten = fwrite(pd, 1, 2, to_stream);
2690 else if (format == SAVE_RAW_FORMAT) /* raw format */
2694 /* only forward direction */
2695 case SAVE_FORWARD_DIRECTION_MASK: {
2696 progbar_count = user_data->forward.saveinfo.count;
2697 progbar_quantum = user_data->forward.saveinfo.count/100;
2698 stream = forw_stream;
2701 /* only reversed direction */
2702 case SAVE_REVERSE_DIRECTION_MASK: {
2703 progbar_count = user_data->reversed.saveinfo.count;
2704 progbar_quantum = user_data->reversed.saveinfo.count/100;
2705 stream = rev_stream;
2715 /* XXX how do you just copy the file? */
2716 while ((rawvalue = getc(stream)) != EOF) {
2719 if ((count > progbar_nextstep) && (count <= progbar_count)) {
2720 update_progress_dlg(progbar,
2721 (gfloat) count/progbar_count, "Saving");
2722 progbar_nextstep = progbar_nextstep + progbar_quantum;
2726 if (putc(rawvalue, to_stream) == EOF) {
2740 destroy_progress_dlg(progbar);
2741 fclose(forw_stream);
2748 /****************************************************************************/
2749 /* the user wants to save in a file */
2750 /* XXX support for different formats is currently commented out */
2752 save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2755 /*GtkWidget *wav, *sw;*/
2756 GtkWidget *au, *raw;
2757 GtkWidget *rev, *forw, *both;
2758 user_data_t *user_data;
2759 gint channels, format;
2761 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2763 /* Perhaps the user specified a directory instead of a file.
2764 * Check whether they did.
2766 if (test_for_directory(g_dest) == EISDIR) {
2767 /* It's a directory - set the file selection box to display it. */
2768 set_last_open_dir(g_dest);
2770 file_selection_set_current_folder((GtkWidget *)fc, get_last_open_dir());
2771 gtk_file_chooser_set_current_name((GtkFileChooser *)fc, "");
2772 return FALSE; /* run the dialog again */
2776 wav = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2777 sw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2779 au = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
2780 raw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
2781 rev = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2782 forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
2783 both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
2784 user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
2786 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2787 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2788 * disable the ok button or disable the buttons for direction if only one is not ok. The
2789 * problem is if we open the save voice dialog and then click the refresh button and maybe
2790 * the state changes, so we can't save anymore. In this case we should be able to update
2791 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2794 /* we can not save in both directions */
2795 if ((user_data->forward.saveinfo.saved == FALSE)
2796 && (user_data->reversed.saveinfo.saved == FALSE)
2797 && (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))) {
2798 /* there are many combinations here, we just exit when first matches */
2799 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2800 || (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2801 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2802 "Can't save in a file: Unsupported codec.");
2803 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2804 || (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2805 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2806 "Can't save in a file: Wrong length of captured packets.");
2807 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2808 || (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2809 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2810 "Can't save in a file: RTP data with padding.");
2811 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2812 || (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2813 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2814 "Can't save in a file: Not all data in all packets was captured.");
2816 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2817 "Can't save in a file: File I/O problem.");
2819 return TRUE; /* we're done */
2821 /* we can not save forward direction */
2822 else if ((user_data->forward.saveinfo.saved == FALSE)
2823 && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (forw)))
2824 || (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
2825 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2826 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2827 "Can't save forward direction in a file: Unsupported codec.");
2828 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2829 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2830 "Can't save forward direction in a file: Wrong length of captured packets.");
2831 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2832 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2833 "Can't save forward direction in a file: RTP data with padding.");
2834 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2835 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2836 "Can't save forward direction in a file: Not all data in all packets was captured.");
2838 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2839 "Can't save forward direction in a file: File I/O problem.");
2841 return TRUE; /* we're done */
2843 /* we can not save reversed direction */
2844 else if ((user_data->reversed.saveinfo.saved == FALSE)
2845 && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev)))
2846 || (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
2847 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2848 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2849 "Can't save reversed direction in a file: Unsupported codec.");
2850 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2851 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2852 "Can't save reversed direction in a file: Wrong length of captured packets.");
2853 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2854 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2855 "Can't save reversed direction in a file: RTP data with padding.");
2856 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2857 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2858 "Can't save reversed direction in a file: Not all data in all packets was captured.");
2859 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2860 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2861 "Can't save reversed direction in a file: No RTP data.");
2863 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2864 "Can't save reversed direction in a file: File I/O problem.");
2866 return TRUE; /* we're done */
2870 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (wav)))
2871 format = SAVE_WAV_FORMAT;
2874 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (au)))
2875 format = SAVE_AU_FORMAT;
2877 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (sw)))
2878 format = SAVE_SW_FORMAT;
2880 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (raw)))
2881 format = SAVE_RAW_FORMAT;
2883 format = SAVE_NONE_FORMAT;
2885 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev)))
2886 channels = SAVE_REVERSE_DIRECTION_MASK;
2887 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))
2888 channels = SAVE_BOTH_DIRECTION_MASK;
2890 channels = SAVE_FORWARD_DIRECTION_MASK;
2892 /* direction/format validity*/
2893 if (format == SAVE_AU_FORMAT)
2895 /* make sure streams are alaw/ulaw */
2896 if ((channels & SAVE_FORWARD_DIRECTION_MASK)
2897 && (user_data->forward.statinfo.pt != PT_PCMA)
2898 && (user_data->forward.statinfo.pt != PT_PCMU)) {
2899 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2900 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2902 return TRUE; /* we're done */
2904 if ((channels & SAVE_REVERSE_DIRECTION_MASK)
2905 && (user_data->reversed.statinfo.pt != PT_PCMA)
2906 && (user_data->reversed.statinfo.pt != PT_PCMU)) {
2907 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2908 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2910 return TRUE; /* we're done */
2912 /* make sure pt's don't differ */
2913 if ((channels == SAVE_BOTH_DIRECTION_MASK)
2914 && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)) {
2915 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2916 "Can't save in a file: Forward and reverse direction differ in type");
2918 return TRUE; /* we're done */
2921 else if (format == SAVE_RAW_FORMAT)
2923 /* can't save raw in both directions */
2924 if (channels == SAVE_BOTH_DIRECTION_MASK) {
2925 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2926 "Can't save in a file: Unable to save raw data in both directions");
2928 return TRUE; /* we're done */
2933 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2934 "Can't save in a file: Invalid save format");
2936 return TRUE; /* we're done */
2939 if (!copy_file(g_dest, channels, format, user_data)) {
2940 /* XXX - report the error type! */
2941 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2942 "An error occurred while saving voice in a file.");
2944 return TRUE; /* we're done */
2948 return TRUE; /* we're done */
2951 /****************************************************************************/
2952 /* when the user wants to save the voice information in a file */
2953 /* XXX support for different formats is currently commented out */
2955 on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
2959 GtkWidget *label_format;
2960 GtkWidget *channels_label;
2961 GtkWidget *forward_rb;
2962 GtkWidget *reversed_rb;
2964 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2968 /* if we can't save in a file: wrong codec, cut packets or other errors */
2969 /* Should the error arise here or later when you click ok button ?
2970 * if we do it here, then we must disable the refresh button, so we don't do it here
2973 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2974 if (user_data->dlg.save_voice_as_w != NULL) {
2975 /* There's already a Save voice info dialog box; reactivate it. */
2976 reactivate_window(user_data->dlg.save_voice_as_w);
2980 /* XXX - use file_selection from dlg_utils instead! */
2981 user_data->dlg.save_voice_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2982 GTK_WINDOW(user_data->dlg.notebook),
2983 GTK_FILE_CHOOSER_ACTION_SAVE,
2984 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2985 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2987 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
2989 /* Container for each row of widgets */
2990 vertb =ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 0, FALSE);
2991 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2992 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2993 gtk_widget_show (vertb);
2995 grid1 = ws_gtk_grid_new ();
2996 gtk_widget_show (grid1);
2997 gtk_box_pack_start (GTK_BOX (vertb), grid1, FALSE, FALSE, 0);
2998 gtk_container_set_border_width (GTK_CONTAINER (grid1), 10);
2999 ws_gtk_grid_set_row_spacing (GTK_GRID (grid1), 20);
3002 label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
3003 gtk_widget_show (label_format);
3004 ws_gtk_grid_attach_extended (GTK_GRID (grid1), label_format, 0, 0, 3, 1,
3005 (GtkAttachOptions) (GTK_FILL),
3006 (GtkAttachOptions) (0), 0, 0);
3009 label_format = gtk_label_new ("Format: ");
3010 gtk_widget_show (label_format);
3011 ws_gtk_grid_attach_extended (GTK_GRID (grid1), label_format, 0, 0, 1, 1,
3012 (GtkAttachOptions) (GTK_FILL),
3013 (GtkAttachOptions) (0), 0, 0);
3015 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
3017 raw_rb = gtk_radio_button_new_with_label (NULL, ".raw");
3018 gtk_widget_show (raw_rb);
3019 ws_gtk_grid_attach_extended (GTK_GRID (grid1), raw_rb, 1, 0, 1, 1,
3020 (GtkAttachOptions) (GTK_FILL),
3021 (GtkAttachOptions) (0), 0, 0);
3024 au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
3025 gtk_widget_show (au_rb);
3026 ws_gtk_grid_attach_extended (GTK_GRID (grid1), au_rb, 3, 0, 1, 1,
3027 (GtkAttachOptions) (GTK_FILL),
3028 (GtkAttachOptions) (0), 0, 0);
3031 /* we support .au - ulaw*/
3032 wav_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".wav");
3033 gtk_widget_show (wav_rb);
3034 ws_gtk_grid_attach_extended (GTK_GRID (grid1), wav_rb, 1, 0, 1, 1,
3035 (GtkAttachOptions) (GTK_FILL),
3036 (GtkAttachOptions) (0), 0, 0);
3038 sw_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), "8 kHz, 16 bit ");
3039 gtk_widget_show (sw_rb);
3040 ws_gtk_grid_attach_extended (GTK_GRID (grid1), sw_rb, 2, 0, 1, 1,
3041 (GtkAttachOptions) (GTK_FILL),
3042 (GtkAttachOptions) (0), 0, 0);
3043 au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
3044 gtk_widget_show (au_rb);
3045 ws_gtk_grid_attach_extended (GTK_GRID (grid1), au_rb, 3, 0, 1, 1,
3046 (GtkAttachOptions) (GTK_FILL),
3047 (GtkAttachOptions) (0), 0, 0);
3050 channels_label = gtk_label_new ("Channels: ");
3051 gtk_widget_show (channels_label);
3052 ws_gtk_grid_attach_extended (GTK_GRID (grid1), channels_label, 0, 1, 1, 1,
3053 (GtkAttachOptions) (GTK_FILL),
3054 (GtkAttachOptions) (0), 0, 0);
3055 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
3057 forward_rb = gtk_radio_button_new_with_label (NULL, "forward ");
3058 gtk_widget_show (forward_rb);
3059 ws_gtk_grid_attach_extended (GTK_GRID (grid1), forward_rb, 1, 1, 1, 1,
3060 (GtkAttachOptions) (GTK_FILL),
3061 (GtkAttachOptions) (0), 0, 0);
3063 reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed ");
3064 gtk_widget_show (reversed_rb);
3065 ws_gtk_grid_attach_extended (GTK_GRID (grid1), reversed_rb, 2, 1, 1, 1,
3066 (GtkAttachOptions) (GTK_FILL),
3067 (GtkAttachOptions) (0), 0, 0);
3069 both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
3070 gtk_widget_show (both_rb);
3071 ws_gtk_grid_attach_extended (GTK_GRID (grid1), both_rb, 3, 1, 1, 1,
3072 (GtkAttachOptions) (GTK_FILL),
3073 (GtkAttachOptions) (0), 0, 0);
3076 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
3079 /* if one direction is nok we don't allow saving
3080 XXX this is not ok since the user can click the refresh button and cause changes
3081 but we can not update this window. So we move all the decision on the time the ok
3084 if (user_data->forward.saved == FALSE) {
3085 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
3086 gtk_widget_set_sensitive(forward_rb, FALSE);
3087 gtk_widget_set_sensitive(both_rb, FALSE);
3089 else if (user_data->reversed.saved == FALSE) {
3090 gtk_widget_set_sensitive(reversed_rb, FALSE);
3091 gtk_widget_set_sensitive(both_rb, FALSE);
3095 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
3096 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
3097 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
3098 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
3099 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
3100 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
3101 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
3102 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
3104 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
3105 G_CALLBACK(window_delete_event_cb), NULL);
3106 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
3107 G_CALLBACK(save_voice_as_destroy_cb), user_data);
3109 gtk_widget_show(user_data->dlg.save_voice_as_w);
3110 window_present(user_data->dlg.save_voice_as_w);
3112 /* "Run" the GtkFileChooserDialog. */
3113 /* Upon exit: If "Accept" run the OK callback. */
3114 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
3115 /* Destroy the window. */
3116 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
3117 /* return with a TRUE status so that the dialog window will be destroyed. */
3118 /* Trying to re-run the dialog after popping up an alert box will not work */
3119 /* since the user will not be able to dismiss the alert box. */
3120 /* The (somewhat unfriendly) effect: the user must re-invoke the */
3121 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
3123 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
3124 /* GtkFileChooserDialog. */
3125 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
3126 if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
3127 break; /* we're done */
3130 window_destroy(user_data->dlg.save_voice_as_w);
3135 /****************************************************************************/
3136 /* when we are finished with redisection, we add the label for the statistic */
3138 draw_stat(user_data_t *user_data)
3140 gchar label_max[300];
3141 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
3142 - user_data->forward.statinfo.start_seq_nr + 1;
3143 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
3144 - user_data->reversed.statinfo.start_seq_nr + 1;
3145 guint32 f_total_nr = user_data->forward.statinfo.total_nr;
3146 guint32 r_total_nr = user_data->reversed.statinfo.total_nr;
3147 gint32 f_lost = f_expected - f_total_nr;
3148 gint32 r_lost = r_expected - r_total_nr;
3149 double f_sumt = user_data->forward.statinfo.sumt;
3150 double f_sumTS = user_data->forward.statinfo.sumTS;
3151 double f_sumt2 = user_data->forward.statinfo.sumt2;
3152 double f_sumtTS = user_data->forward.statinfo.sumtTS;
3154 double r_sumt = user_data->reversed.statinfo.sumt;
3155 double r_sumTS = user_data->reversed.statinfo.sumTS;
3156 double r_sumt2 = user_data->reversed.statinfo.sumt2;
3157 double r_sumtTS = user_data->reversed.statinfo.sumtTS;
3158 double f_perc, r_perc;
3159 double f_clock_drift = 1.0;
3160 double r_clock_drift = 1.0;
3161 double f_duration = user_data->forward.statinfo.time - user_data->forward.statinfo.start_time;
3162 double r_duration = user_data->reversed.statinfo.time - user_data->reversed.statinfo.start_time;
3163 guint32 f_clock_rate = user_data->forward.statinfo.clock_rate;
3164 guint32 r_clock_rate = user_data->reversed.statinfo.clock_rate;
3166 if (f_clock_rate == 0) {
3170 if (r_clock_rate == 0) {
3175 f_perc = (double)(f_lost*100)/(double)f_expected;
3180 r_perc = (double)(r_lost*100)/(double)r_expected;
3185 if ((f_total_nr >0) && (f_sumt2 > 0)) {
3186 f_clock_drift = (f_total_nr * f_sumtTS - f_sumt * f_sumTS) / (f_total_nr * f_sumt2 - f_sumt * f_sumt);
3188 if ((r_total_nr >0) && (r_sumt2 > 0)) {
3189 r_clock_drift = (r_total_nr * r_sumtTS - r_sumt * r_sumTS) / (r_total_nr * r_sumt2 - r_sumt * r_sumt);
3191 g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
3192 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
3193 "Max skew = %.2f ms.\n"
3194 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
3195 " Sequence errors = %u \n"
3196 "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
3197 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
3198 user_data->forward.statinfo.max_jitter, user_data->forward.statinfo.mean_jitter,
3199 user_data->forward.statinfo.max_skew,
3200 f_expected, f_expected, f_lost, f_perc,
3201 user_data->forward.statinfo.sequence,
3203 f_duration * (f_clock_drift - 1.0),
3204 f_clock_drift * f_clock_rate,
3205 100.0 * (f_clock_drift - 1.0));
3207 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
3208 gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_fwd), TRUE);
3210 g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
3211 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
3212 "Max skew = %.2f ms.\n"
3213 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
3214 " Sequence errors = %u \n"
3215 "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
3216 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
3217 user_data->reversed.statinfo.max_jitter, user_data->reversed.statinfo.mean_jitter,
3218 user_data->reversed.statinfo.max_skew,
3219 r_expected, r_expected, r_lost, r_perc,
3220 user_data->reversed.statinfo.sequence,
3222 r_duration * (r_clock_drift - 1.0),
3223 r_clock_drift * r_clock_rate,
3224 100.0 * (r_clock_drift - 1.0));
3226 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
3227 gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_rev), TRUE);
3234 /****************************************************************************/
3235 /* append a line to list */
3237 add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
3238 double delta, double jitter, double skew, double bandwidth, gchar *status, gboolean marker,
3239 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
3241 GtkListStore *list_store;
3243 if (strcmp(status, OK_TEXT) != 0) {
3244 user_data->dlg.number_of_nok++;
3247 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
3249 /* Creates a new row at position. iter will be changed to point to this new row.
3250 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
3251 * The row will be filled with the values given to this function.
3253 * should generally be preferred when inserting rows in a sorted list store.
3255 gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
3256 PACKET_COLUMN, number,
3257 SEQUENCE_COLUMN, seq_num,
3258 TIMESTAMP_COLUMN, timestamp,
3259 DELTA_COLUMN, delta,
3260 JITTER_COLUMN, jitter,
3262 IPBW_COLUMN, bandwidth,
3263 MARKER_COLUMN, marker,
3264 STATUS_COLUMN, (char *)status,
3265 DATE_COLUMN, (char *)timeStr,
3266 LENGTH_COLUMN, pkt_len,
3267 FOREGROUND_COLOR_COL, NULL,
3268 BACKGROUND_COLOR_COL, (char *)color_str,
3271 if (flags & STAT_FLAG_FIRST) {
3272 /* Set first row as active */
3273 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
3277 /****************************************************************************
3278 * Functions needed to present values from the list
3282 /* Present boolean value */
3284 rtp_boolean_data_func (GtkTreeViewColumn *column _U_,
3285 GtkCellRenderer *renderer,
3286 GtkTreeModel *model,
3292 /* the col to get data from is in userdata */
3293 gint bool_col = GPOINTER_TO_INT(user_data);
3295 gtk_tree_model_get(model, iter, bool_col, &bool_val, -1);
3299 g_strlcpy(buf, bool_val ? "SET" : "", sizeof(buf));
3302 g_assert_not_reached();
3305 g_object_set(renderer, "text", buf, NULL);
3310 create_list(user_data_t* user_data)
3313 GtkListStore *list_store;
3315 GtkTreeViewColumn *column;
3316 GtkCellRenderer *renderer;
3317 GtkTreeSortable *sortable;
3318 GtkTreeView *list_view;
3319 GtkTreeSelection *selection;
3321 /* Create the store */
3322 list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX */
3323 G_TYPE_UINT, /* Packet */
3324 G_TYPE_UINT, /* Sequence */
3325 G_TYPE_UINT, /* Time stamp */
3326 G_TYPE_FLOAT, /* Delta(ms) */
3327 G_TYPE_FLOAT, /* Filtered Jitter(ms) */
3328 G_TYPE_FLOAT, /* Skew(ms) */
3329 G_TYPE_FLOAT, /* IP BW(kbps) */
3330 G_TYPE_BOOLEAN, /* Marker */
3331 G_TYPE_STRING, /* Status */
3332 G_TYPE_STRING, /* Date */
3333 G_TYPE_UINT, /* Length */
3334 G_TYPE_STRING, /* Foreground color */
3335 G_TYPE_STRING); /* Background color */
3338 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
3340 list_view = GTK_TREE_VIEW(list);
3341 sortable = GTK_TREE_SORTABLE(list_store);
3343 /* Speed up the list display */
3344 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
3346 /* Setup the sortable columns */
3347 gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
3348 gtk_tree_view_set_headers_clickable(list_view, FALSE);
3350 /* The view now holds a reference. We can get rid of our own reference */
3351 g_object_unref (G_OBJECT (list_store));
3354 * Create the first column packet, associating the "text" attribute of the
3355 * cell_renderer to the first column of the model
3357 renderer = gtk_cell_renderer_text_new ();
3358 column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
3359 "text", PACKET_COLUMN,
3360 "foreground", FOREGROUND_COLOR_COL,
3361 "background", BACKGROUND_COLOR_COL,
3363 gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
3364 gtk_tree_view_column_set_resizable(column, TRUE);
3365 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3366 gtk_tree_view_column_set_min_width(column, 55);
3368 /* Add the column to the view. */
3369 gtk_tree_view_append_column (list_view, column);
3372 renderer = gtk_cell_renderer_text_new ();
3373 column = gtk_tree_view_column_new_with_attributes ("Sequence", renderer,
3374 "text", SEQUENCE_COLUMN,
3375 "foreground", FOREGROUND_COLOR_COL,
3376 "background", BACKGROUND_COLOR_COL,
3378 gtk_tree_view_column_set_sort_column_id(column, SEQUENCE_COLUMN);
3379 gtk_tree_view_column_set_resizable(column, TRUE);
3380 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3381 gtk_tree_view_column_set_min_width(column, 75);
3382 gtk_tree_view_append_column (list_view, column);
3385 Currently not visible
3387 renderer = gtk_cell_renderer_text_new ();
3388 column = gtk_tree_view_column_new_with_attributes ("Time stamp", renderer,
3389 "text", TIMESTAMP_COLUMN,
3390 "foreground", FOREGROUND_COLOR_COL,
3391 "background", BACKGROUND_COLOR_COL,
3393 gtk_tree_view_column_set_sort_column_id(column, TIMESTAMP_COLUMN);
3394 gtk_tree_view_column_set_resizable(column, TRUE);
3395 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3396 gtk_tree_view_column_set_min_width(column, 75);
3397 gtk_tree_view_append_column (list_view, column);
3400 renderer = gtk_cell_renderer_text_new ();
3401 column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
3402 "text", DELTA_COLUMN,
3403 "foreground", FOREGROUND_COLOR_COL,
3404 "background", BACKGROUND_COLOR_COL,
3407 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3408 GINT_TO_POINTER(DELTA_COLUMN), NULL);
3410 gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
3411 gtk_tree_view_column_set_resizable(column, TRUE);
3412 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3413 gtk_tree_view_column_set_min_width(column, 75);
3414 gtk_tree_view_append_column (list_view, column);
3417 renderer = gtk_cell_renderer_text_new ();
3418 column = gtk_tree_view_column_new_with_attributes ("Filtered Jitter(ms)", renderer,
3419 "text", JITTER_COLUMN,
3420 "foreground", FOREGROUND_COLOR_COL,
3421 "background", BACKGROUND_COLOR_COL,
3424 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3425 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3427 gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3428 gtk_tree_view_column_set_resizable(column, TRUE);
3429 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3430 gtk_tree_view_column_set_min_width(column, 110);
3431 gtk_tree_view_append_column (list_view, column);
3434 renderer = gtk_cell_renderer_text_new ();
3435 column = gtk_tree_view_column_new_with_attributes ("Skew(ms)", renderer,
3436 "text", SKEW_COLUMN,
3437 "foreground", FOREGROUND_COLOR_COL,
3438 "background", BACKGROUND_COLOR_COL,
3441 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3442 GINT_TO_POINTER(SKEW_COLUMN), NULL);
3444 gtk_tree_view_column_set_sort_column_id(column, SKEW_COLUMN);
3445 gtk_tree_view_column_set_resizable(column, TRUE);
3446 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3447 gtk_tree_view_column_set_min_width(column, 110);
3448 gtk_tree_view_append_column (list_view, column);
3451 renderer = gtk_cell_renderer_text_new ();
3452 column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
3453 "text", IPBW_COLUMN,
3454 "foreground", FOREGROUND_COLOR_COL,
3455 "background", BACKGROUND_COLOR_COL,
3458 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3459 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3461 gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3462 gtk_tree_view_column_set_resizable(column, TRUE);
3463 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3464 gtk_tree_view_column_set_min_width(column, 80);
3465 gtk_tree_view_append_column (list_view, column);
3468 renderer = gtk_cell_renderer_text_new ();
3469 column = gtk_tree_view_column_new_with_attributes ("Marker", renderer,
3470 "text", MARKER_COLUMN,
3471 "foreground", FOREGROUND_COLOR_COL,
3472 "background", BACKGROUND_COLOR_COL,
3475 gtk_tree_view_column_set_cell_data_func(column, renderer, rtp_boolean_data_func,
3476 GINT_TO_POINTER(MARKER_COLUMN), NULL);
3478 gtk_tree_view_column_set_sort_column_id(column, MARKER_COLUMN);
3479 gtk_tree_view_column_set_resizable(column, TRUE);
3480 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3481 gtk_tree_view_column_set_min_width(column, 60);
3482 gtk_tree_view_append_column (list_view, column);
3485 renderer = gtk_cell_renderer_text_new ();
3486 column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3487 "text", STATUS_COLUMN,
3488 "foreground", FOREGROUND_COLOR_COL,
3489 "background", BACKGROUND_COLOR_COL,
3491 gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3492 gtk_tree_view_column_set_resizable(column, TRUE);
3493 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3494 gtk_tree_view_column_set_min_width(column, 100);
3495 gtk_tree_view_append_column (list_view, column);
3497 /* Now enable the sorting of each column */
3498 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3499 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3501 /* Setup the selection handler */
3502 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3503 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3505 g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3506 G_CALLBACK (on_list_select_row),
3511 /****************************************************************************/
3512 /* Create the dialog box with all widgets */
3514 create_rtp_dialog(user_data_t* user_data)
3517 GtkWidget *list_fwd;
3518 GtkWidget *list_rev;
3519 GtkWidget *label_stats_fwd;
3520 GtkWidget *label_stats_rev;
3521 GtkWidget *notebook;
3523 GtkWidget *main_vb, *page, *page_r;
3525 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3526 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3527 #ifdef HAVE_LIBPORTAUDIO
3528 GtkWidget *player_bt;
3529 #endif /* HAVE_LIBPORTAUDIO */
3530 GtkWidget *graph_bt;
3531 gchar label_forward[150];
3532 gchar label_forward_tree[150];
3533 gchar label_reverse[150];
3538 window = dlg_window_new("Wireshark: RTP Stream Analysis"); /* transient_for top_level */
3539 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3541 /* Container for each row of widgets */
3542 main_vb =ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 2, FALSE);
3543 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3544 gtk_container_add(GTK_CONTAINER(window), main_vb);
3545 gtk_widget_show(main_vb);
3548 g_strlcpy(str_src, ep_address_to_display(&(user_data->src_fwd)), sizeof(str_src));
3549 g_strlcpy(str_dst, ep_address_to_display(&(user_data->dst_fwd)), sizeof(str_dst));
3551 g_snprintf(label_forward, sizeof(label_forward),
3552 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3553 str_src, user_data->port_src_fwd, str_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3555 g_snprintf(label_forward_tree, sizeof(label_forward_tree),
3556 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X \n"
3557 "Note many things affects the accurasy of the analysis, use with caution",
3558 str_src, user_data->port_src_fwd, str_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3561 g_strlcpy(str_src, ep_address_to_display(&(user_data->src_rev)), sizeof(str_src));
3562 g_strlcpy(str_dst, ep_address_to_display(&(user_data->dst_rev)), sizeof(str_dst));
3564 g_snprintf(label_reverse, sizeof(label_reverse),
3565 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X \n"
3566 "Note many things affects the accurasy of the analysis, use with caution",
3567 str_src, user_data->port_src_rev, str_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3569 /* Start a notebook for flipping between sets of changes */
3570 notebook = gtk_notebook_new();
3571 gtk_box_pack_start(GTK_BOX(main_vb), notebook, TRUE, TRUE, 0);
3572 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3574 user_data->dlg.notebook_signal_id =
3575 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3577 /* page for forward connection */
3578 page = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 8, FALSE);
3579 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3581 /* direction label */
3582 label = gtk_label_new(label_forward);
3583 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3585 /* place for some statistics */
3586 label_stats_fwd = gtk_label_new("\n");
3587 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3589 /* scrolled window */
3590 scrolled_window = scrolled_window_new(NULL, NULL);
3593 list_fwd = create_list(user_data);
3594 gtk_widget_show(list_fwd);
3595 gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3596 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3597 gtk_widget_show(scrolled_window);
3600 label = gtk_label_new(" Forward Direction ");
3601 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3603 /* same page for reversed connection */
3604 page_r = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 8, FALSE);
3605 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3606 label = gtk_label_new(label_reverse);
3607 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3608 label_stats_rev = gtk_label_new("\n");
3609 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3611 scrolled_window_r = scrolled_window_new(NULL, NULL);
3613 list_rev = create_list(user_data);
3614 gtk_widget_show(list_rev);
3615 gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3616 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3617 gtk_widget_show(scrolled_window_r);
3619 label = gtk_label_new(" Reversed Direction ");
3620 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3622 /* page for help&about or future */
3624 page_help = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5, FALSE);
3625 label = gtk_label_new(" Future ");
3626 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3627 frame = gtk_frame_new("");
3628 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution, ...");
3629 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3630 gtk_container_add(GTK_CONTAINER(frame), text);
3631 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3632 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3635 /* show all notebooks */
3636 gtk_widget_show_all(notebook);
3639 box4 = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
3640 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3641 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3642 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3643 gtk_box_set_spacing(GTK_BOX (box4), 0);
3644 gtk_widget_show(box4);
3646 voice_bt = gtk_button_new_with_label("Save payload...");
3647 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3648 gtk_widget_show(voice_bt);
3649 g_signal_connect(voice_bt, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
3651 csv_bt = gtk_button_new_with_label("Save as CSV...");
3652 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3653 gtk_widget_show(csv_bt);
3654 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3656 refresh_bt = ws_gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3657 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3658 gtk_widget_show(refresh_bt);
3659 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3661 goto_bt = ws_gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3662 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3663 gtk_widget_show(goto_bt);
3664 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked_lst), user_data);
3666 graph_bt = gtk_button_new_with_label("Graph");
3667 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3668 gtk_widget_show(graph_bt);
3669 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3671 #ifdef HAVE_LIBPORTAUDIO
3672 player_bt = ws_gtk_button_new_from_stock(WIRESHARK_STOCK_AUDIO_PLAYER);
3673 gtk_container_add(GTK_CONTAINER(box4), player_bt);
3674 gtk_widget_show(player_bt);
3675 g_signal_connect(player_bt, "clicked", G_CALLBACK(on_player_bt_clicked), NULL);
3676 /*gtk_widget_set_tooltip_text (player_bt, "Launch the RTP player to listen the audio stream");*/
3677 #endif /* HAVE_LIBPORTAUDIO */
3679 next_bt = gtk_button_new_with_label("Next non-Ok");
3680 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3681 gtk_widget_show(next_bt);
3682 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked_list), user_data);
3684 close_bt = ws_gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3685 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3686 gtk_widget_set_can_default(close_bt, TRUE);
3687 gtk_widget_show(close_bt);
3688 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3690 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3691 g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3693 gtk_widget_show(window);
3694 window_present(window);
3697 /* some widget references need to be saved for outside use */
3698 user_data->dlg.window = window;
3699 user_data->dlg.list_fwd = list_fwd;
3700 user_data->dlg.list_rev = list_rev;
3701 user_data->dlg.label_stats_fwd = label_stats_fwd;
3702 user_data->dlg.label_stats_rev = label_stats_rev;
3703 user_data->dlg.notebook = notebook;
3704 user_data->dlg.selected_list = list_fwd;
3705 user_data->dlg.number_of_nok = 0;
3708 * select the initial row
3710 gtk_widget_grab_focus(list_fwd);
3715 /****************************************************************************/
3717 process_node(proto_node *ptree_node, header_field_info *hfinformation,
3718 const gchar* proto_field, guint32* p_result)
3721 proto_node *proto_sibling_node;
3722 header_field_info *hfssrc;
3725 finfo = PNODE_FINFO(ptree_node);
3727 /* Caller passed top of the protocol tree. Expected child node */
3730 if (hfinformation == (finfo->hfinfo)) {
3731 hfssrc = proto_registrar_get_byname(proto_field);
3734 for (ptree_node = ptree_node->first_child;
3736 ptree_node = ptree_node->next) {
3737 finfo = PNODE_FINFO(ptree_node);
3738 if (hfssrc == finfo->hfinfo) {
3739 if (hfinformation->type == FT_IPv4) {
3740 ipv4 = (ipv4_addr *)fvalue_get(&finfo->value);
3741 *p_result = ipv4_get_net_order_addr(ipv4);
3744 *p_result = fvalue_get_uinteger(&finfo->value);
3753 proto_sibling_node = ptree_node->next;
3755 if (proto_sibling_node) {
3756 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3762 /****************************************************************************/
3764 get_int_value_from_proto_tree(proto_tree *protocol_tree,
3765 const gchar *proto_name,
3766 const gchar *proto_field,
3769 proto_node *ptree_node;
3770 header_field_info *hfinformation;
3772 hfinformation = proto_registrar_get_byname(proto_name);
3773 if (hfinformation == NULL)
3776 ptree_node = ((proto_node *)protocol_tree)->first_child;
3780 return process_node(ptree_node, hfinformation, proto_field, p_result);
3784 /****************************************************************************/
3786 rtp_analysis(address *src_fwd,
3787 guint32 port_src_fwd,
3789 guint32 port_dst_fwd,
3792 guint32 port_src_rev,
3794 guint32 port_dst_rev,
3797 user_data_t *user_data;
3800 static GdkColor col[MAX_GRAPHS] = {
3801 {0, 0x0000, 0x0000, 0x0000}, /* Black */
3802 {0, 0xffff, 0x0000, 0x0000}, /* Red */
3803 {0, 0x0000, 0xffff, 0x0000}, /* Green */
3804 {0, 0xdddd, 0xcccc, 0x6666}, /* Light amber yellow */
3805 {0, 0x6666, 0xcccc, 0xdddd}, /* Light bluish cyan */
3806 {0, 0x0000, 0x0000, 0xffff} /* Blue */
3808 static GdkRGBA rgba_col[MAX_GRAPHS] = {
3809 {0.0, 0.0, 0.0, 1.0}, /* Black */
3810 {1.0, 0.0, 0.1, 1.0}, /* Red */
3811 {0.0, 1.0, 0.0, 1.0}, /* Green */
3812 {0.867, 0.800, 0.400, 1.0}, /* Light amber yellow */
3813 {0.400, 0.800, 0.867, 1.0}, /* Light bluish cyan */
3814 {0.0, 0.0, 1.0, 1.0}, /* Blue */
3820 user_data = (user_data_t *)g_malloc(sizeof(user_data_t));
3822 COPY_ADDRESS(&(user_data->src_fwd), src_fwd);
3823 user_data->port_src_fwd = port_src_fwd;
3824 COPY_ADDRESS(&(user_data->dst_fwd), dst_fwd);
3825 user_data->port_dst_fwd = port_dst_fwd;
3826 user_data->ssrc_fwd = ssrc_fwd;
3827 COPY_ADDRESS(&(user_data->src_rev), src_rev);
3828 user_data->port_src_rev = port_src_rev;
3829 COPY_ADDRESS(&(user_data->dst_rev), dst_rev);
3830 user_data->port_dst_rev = port_dst_rev;
3831 user_data->ssrc_rev = ssrc_rev;
3834 /* file names for storing sound data */
3835 fd = create_tempfile(&tempname, "wireshark_rtp_f");
3837 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3838 "Can't create temporary file for RTP analysis:\n%s.",
3843 user_data->f_tempname = g_strdup(tempname);
3845 fd = create_tempfile(&tempname, "wireshark_rtp_r");
3847 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3848 "Can't create temporary file for RTP analysis:\n%s.",
3850 g_free(user_data->f_tempname);
3854 user_data->r_tempname = g_strdup(tempname);
3856 user_data->forward.saveinfo.fp = NULL;
3857 user_data->reversed.saveinfo.fp = NULL;
3858 user_data->dlg.save_voice_as_w = NULL;
3859 user_data->dlg.save_csv_as_w = NULL;
3860 user_data->dlg.dialog_graph.window = NULL;
3862 /* init dialog_graph */
3863 user_data->dlg.dialog_graph.needs_redraw = TRUE;
3864 user_data->dlg.dialog_graph.interval = tick_interval_values[DEFAULT_TICK_VALUE_INDEX];
3865 user_data->dlg.dialog_graph.draw_area = NULL;
3866 #if GTK_CHECK_VERSION(2,22,0)
3867 user_data->dlg.dialog_graph.surface = NULL;
3869 user_data->dlg.dialog_graph.pixmap = NULL;
3871 user_data->dlg.dialog_graph.scrollbar = NULL;
3872 user_data->dlg.dialog_graph.scrollbar_adjustment = NULL;
3873 user_data->dlg.dialog_graph.surface_width = 500;
3874 user_data->dlg.dialog_graph.surface_height = 200;
3875 user_data->dlg.dialog_graph.pixels_per_tick = pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
3876 user_data->dlg.dialog_graph.max_y_units = AUTO_MAX_YSCALE;
3877 user_data->dlg.dialog_graph.last_interval = 0xffffffff;
3878 user_data->dlg.dialog_graph.max_interval = 0;
3879 user_data->dlg.dialog_graph.num_items = 0;
3880 user_data->dlg.dialog_graph.start_time = -1;
3882 for (i = 0; i < MAX_GRAPHS; i++) {
3883 user_data->dlg.dialog_graph.graph[i].color.pixel = 0;
3884 user_data->dlg.dialog_graph.graph[i].color.red = col[i].red;
3885 user_data->dlg.dialog_graph.graph[i].color.green = col[i].green;
3886 user_data->dlg.dialog_graph.graph[i].color.blue = col[i].blue;
3887 user_data->dlg.dialog_graph.graph[i].rgba_color.red = rgba_col[i].red;
3888 user_data->dlg.dialog_graph.graph[i].rgba_color.green = rgba_col[i].green;
3889 user_data->dlg.dialog_graph.graph[i].rgba_color.blue = rgba_col[i].blue;
3890 user_data->dlg.dialog_graph.graph[i].rgba_color.alpha = rgba_col[i].alpha;
3891 user_data->dlg.dialog_graph.graph[i].display = TRUE;
3892 user_data->dlg.dialog_graph.graph[i].display_button = NULL;
3893 user_data->dlg.dialog_graph.graph[i].ud = user_data;
3896 /* create the dialog box */
3897 create_rtp_dialog(user_data);
3899 /* proceed as if the Refresh button would have been pressed */
3900 on_refresh_bt_clicked(NULL, user_data);
3903 /****************************************************************************/
3904 /* entry point from main menu */
3906 rtp_analysis_cb(GtkAction *action _U_, gpointer user_data _U_)
3909 guint32 port_src_fwd;
3911 guint32 port_dst_fwd;
3912 guint32 ssrc_fwd = 0;
3914 guint32 port_src_rev;
3916 guint32 port_dst_rev;
3917 guint32 ssrc_rev = 0;
3918 unsigned int version_fwd;
3920 gchar filter_text[256];
3924 GList *strinfo_list;
3925 GList *filtered_list = NULL;
3928 rtp_stream_info_t *strinfo;
3930 /* Try to compile the filter. */
3931 g_strlcpy(filter_text, "rtp && rtp.version && rtp.ssrc && (ip || ipv6)", sizeof(filter_text));
3932 if (!dfilter_compile(filter_text, &sfcode)) {
3933 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3936 /* we load the current file into cf variable */
3938 fdata = cf->current_frame;
3940 /* we are on the selected frame now */
3942 return; /* if we exit here it's an error */
3944 /* dissect the current record */
3945 if (!cf_read_record(cf, fdata))
3946 return; /* error reading the record */
3947 epan_dissect_init(&edt, cf->epan, TRUE, FALSE);
3948 epan_dissect_prime_dfilter(&edt, sfcode);
3949 epan_dissect_run(&edt, cf->cd_t, &cf->phdr, frame_tvbuff_new_buffer(fdata, &cf->buf), fdata, NULL);
3951 /* if it is not an rtp packet, show the rtpstream dialog */
3952 if (!dfilter_apply_edt(sfcode, &edt)) {
3953 epan_dissect_cleanup(&edt);
3954 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3955 "Please select an RTP packet.");
3959 /* ok, it is a RTP frame, so let's get the ip and port values */
3960 COPY_ADDRESS(&(src_fwd), &(edt.pi.src));
3961 COPY_ADDRESS(&(dst_fwd), &(edt.pi.dst));
3962 port_src_fwd = edt.pi.srcport;
3963 port_dst_fwd = edt.pi.destport;
3965 /* assume the inverse ip/port combination for the reverse direction */
3966 COPY_ADDRESS(&(src_rev), &(edt.pi.dst));
3967 COPY_ADDRESS(&(dst_rev), &(edt.pi.src));
3968 port_src_rev = edt.pi.destport;
3969 port_dst_rev = edt.pi.srcport;
3971 /* check if it is RTP Version 2 */
3972 if (!get_int_value_from_proto_tree(edt.tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3973 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3974 "Only RTP version 2 is supported.");
3978 /* now we need the SSRC value of the current frame */
3979 if (!get_int_value_from_proto_tree(edt.tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3980 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3981 "SSRC value not found.");
3985 /* Scan for rtpstream */
3987 /* search for reversed direction in the global rtp streams list */
3989 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3990 while (strinfo_list)
3992 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3993 if (ADDRESSES_EQUAL(&(strinfo->src_addr), &(src_fwd))
3994 && (strinfo->src_port == port_src_fwd)
3995 && (ADDRESSES_EQUAL(&(strinfo->dest_addr), &(dst_fwd)))
3996 && (strinfo->dest_port == port_dst_fwd))
3998 filtered_list = g_list_prepend(filtered_list, strinfo);
4001 if (ADDRESSES_EQUAL(&(strinfo->src_addr), &(src_rev))
4002 && (strinfo->src_port == port_src_rev)
4003 && (ADDRESSES_EQUAL(&(strinfo->dest_addr), &(dst_rev)))
4004 && (strinfo->dest_port == port_dst_rev))
4007 filtered_list = g_list_append(filtered_list, strinfo);
4009 ssrc_rev = strinfo->ssrc;
4012 strinfo_list = g_list_next(strinfo_list);
4015 /* if more than one reverse streams found, we let the user choose the right one */
4017 rtpstream_dlg_show(filtered_list);
4036 /****************************************************************************/
4038 rtp_analysis_init(const char *dummy _U_, void *userdata _U_)
4040 rtp_analysis_cb(NULL, NULL);
4043 /****************************************************************************/
4044 static stat_tap_ui rtp_analysis_ui = {
4045 REGISTER_STAT_GROUP_GENERIC,
4055 register_tap_listener_rtp_analysis(void)
4057 register_stat_tap_ui(&rtp_analysis_ui, NULL);