2 * RTP analysis addition for Wireshark
6 * Copyright 2003, Alcatel Business Systems
7 * By Lars Ruoff <lars.ruoff@gmx.net>
10 * Copyright 2003, Iskratel, Ltd, Kranj
11 * By Miha Jemec <m.jemec@iskratel.si>
13 * Graph. Copyright 2004, Verso Technology
14 * By Alejandro Vaquero <alejandro.vaquero@verso.com>
15 * Based on io_stat.c by Ronnie Sahlberg
17 * Wireshark - Network traffic analyzer
18 * By Gerald Combs <gerald@wireshark.org>
19 * Copyright 1998 Gerald Combs
21 * This program is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU General Public License
23 * as published by the Free Software Foundation; either version 2
24 * of the License, or (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
55 #include <epan/epan_dissect.h>
56 #include <epan/filesystem.h>
57 #include <epan/pint.h>
59 #include <epan/dissectors/packet-rtp.h>
60 #include <epan/rtp_pt.h>
61 #include <epan/addr_resolv.h>
62 #include <epan/stat_cmd_args.h>
63 #include <epan/strutil.h>
66 #include "../register.h"
68 #include "../alert_box.h"
69 #include "../simple_dialog.h"
70 #include "../stat_menu.h"
71 #include "../progress_dlg.h"
73 #include "../tempfile.h"
74 #include <wsutil/file_util.h>
76 #include "gtk/gtkglobals.h"
77 #include "gtk/dlg_utils.h"
78 #include "gtk/file_dlg.h"
79 #include "gtk/gui_utils.h"
80 #include "gtk/gui_stat_menu.h"
81 #include "gtk/pixmap_save.h"
83 #include "gtk/rtp_analysis.h"
84 #include "gtk/rtp_stream.h"
85 #include "gtk/rtp_stream_dlg.h"
86 #include "gtk/stock_icons.h"
88 #ifdef HAVE_LIBPORTAUDIO
89 #include "gtk/graph_analysis.h"
90 #include "gtk/voip_calls.h"
91 #include "gtk/rtp_player.h"
92 #endif /* HAVE_LIBPORTAUDIO */
107 FOREGROUND_COLOR_COL,
108 BACKGROUND_COLOR_COL,
109 N_COLUMN /* The number of columns */
111 /****************************************************************************/
114 #define NUM_GRAPH_ITEMS 100000
115 #define MAX_YSCALE 16
116 #define AUTO_MAX_YSCALE 0
118 #define GRAPH_FWD_JITTER 0
119 #define GRAPH_FWD_DIFF 1
120 #define GRAPH_REV_JITTER 2
121 #define GRAPH_REV_DIFF 3
122 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
124 #define MAX_PIXELS_PER_TICK 4
125 #define DEFAULT_PIXELS_PER_TICK 1
126 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
127 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
129 #define MAX_TICK_VALUES 5
130 #define DEFAULT_TICK_VALUE 1
131 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
132 typedef struct _dialog_graph_graph_item_t {
135 } dialog_graph_graph_item_t;
137 typedef struct _dialog_graph_graph_t {
138 struct _user_data_t *ud;
139 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
142 GtkWidget *display_button;
147 } dialog_graph_graph_t;
150 typedef struct _dialog_graph_t {
151 gboolean needs_redraw;
152 gint32 interval; /* measurement interval in ms */
153 guint32 last_interval;
154 guint32 max_interval; /* XXX max_interval and num_items are redundant */
156 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
158 GtkWidget *draw_area;
160 GtkAdjustment *scrollbar_adjustment;
161 GtkWidget *scrollbar;
169 typedef struct _dialog_data_t {
174 GtkWidget *label_stats_fwd;
175 GtkWidget *label_stats_rev;
176 GtkWidget *selected_list;
178 GtkTreeSelection *selected_list_sel;
179 gint selected_list_row;
181 GtkWidget *save_voice_as_w;
182 GtkWidget *save_csv_as_w;
183 gint notebook_signal_id;
184 dialog_graph_t dialog_graph;
187 #define OK_TEXT "[ Ok ]"
189 /* type of error when saving voice in a file didn't succeed */
192 TAP_RTP_WRONG_LENGTH,
193 TAP_RTP_PADDING_ERROR,
195 TAP_RTP_FILE_OPEN_ERROR,
199 typedef struct _tap_rtp_save_info_t {
202 error_type_t error_type;
204 } tap_rtp_save_info_t;
207 /* structure that holds the information about the forward and reversed direction */
208 struct _info_direction {
209 tap_rtp_stat_t statinfo;
210 tap_rtp_save_info_t saveinfo;
213 #define SILENCE_PCMU (guint8)0xFF
214 #define SILENCE_PCMA (guint8)0x55
216 /* structure that holds general information about the connection
217 * and structures for both directions */
218 typedef struct _user_data_t {
219 /* tap associated data*/
221 guint16 port_src_fwd;
223 guint16 port_dst_fwd;
226 guint16 port_src_rev;
228 guint16 port_dst_rev;
231 struct _info_direction forward;
232 struct _info_direction reversed;
237 /* dialog associated data */
244 static const gchar *titles[11] = {
258 #define SAVE_FORWARD_DIRECTION_MASK 0x01
259 #define SAVE_REVERSE_DIRECTION_MASK 0x02
260 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
262 #define SAVE_NONE_FORMAT 0
263 #define SAVE_WAV_FORMAT 1
264 #define SAVE_AU_FORMAT 2
265 #define SAVE_SW_FORMAT 3
266 #define SAVE_RAW_FORMAT 4
269 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data);
270 /****************************************************************************/
271 static void enable_graph(dialog_graph_graph_t *dgg)
278 static void dialog_graph_reset(user_data_t* user_data);
282 /****************************************************************************/
285 /****************************************************************************/
286 /* when there is a [re]reading of packet's */
288 rtp_reset(void *user_data_arg)
290 user_data_t *user_data = user_data_arg;
291 user_data->forward.statinfo.first_packet = TRUE;
292 user_data->reversed.statinfo.first_packet = TRUE;
293 user_data->forward.statinfo.max_delta = 0;
294 user_data->reversed.statinfo.max_delta = 0;
295 user_data->forward.statinfo.max_jitter = 0;
296 user_data->reversed.statinfo.max_jitter = 0;
297 user_data->forward.statinfo.max_skew = 0;
298 user_data->reversed.statinfo.max_skew = 0;
299 user_data->forward.statinfo.mean_jitter = 0;
300 user_data->reversed.statinfo.mean_jitter = 0;
301 user_data->forward.statinfo.delta = 0;
302 user_data->reversed.statinfo.delta = 0;
303 user_data->forward.statinfo.diff = 0;
304 user_data->reversed.statinfo.diff = 0;
305 user_data->forward.statinfo.jitter = 0;
306 user_data->reversed.statinfo.jitter = 0;
307 user_data->forward.statinfo.skew = 0;
308 user_data->reversed.statinfo.skew = 0;
309 user_data->forward.statinfo.sumt = 0;
310 user_data->reversed.statinfo.sumt = 0;
311 user_data->forward.statinfo.sumTS = 0;
312 user_data->reversed.statinfo.sumTS = 0;
313 user_data->forward.statinfo.sumt2 = 0;
314 user_data->reversed.statinfo.sumt2 = 0;
315 user_data->forward.statinfo.sumtTS = 0;
316 user_data->reversed.statinfo.sumtTS = 0;
317 user_data->forward.statinfo.bandwidth = 0;
318 user_data->reversed.statinfo.bandwidth = 0;
319 user_data->forward.statinfo.total_bytes = 0;
320 user_data->reversed.statinfo.total_bytes = 0;
321 user_data->forward.statinfo.bw_start_index = 0;
322 user_data->reversed.statinfo.bw_start_index = 0;
323 user_data->forward.statinfo.bw_index = 0;
324 user_data->reversed.statinfo.bw_index = 0;
325 user_data->forward.statinfo.timestamp = 0;
326 user_data->reversed.statinfo.timestamp = 0;
327 user_data->forward.statinfo.max_nr = 0;
328 user_data->reversed.statinfo.max_nr = 0;
329 user_data->forward.statinfo.total_nr = 0;
330 user_data->reversed.statinfo.total_nr = 0;
331 user_data->forward.statinfo.sequence = 0;
332 user_data->reversed.statinfo.sequence = 0;
333 user_data->forward.statinfo.start_seq_nr = 0;
334 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
335 user_data->forward.statinfo.stop_seq_nr = 0;
336 user_data->reversed.statinfo.stop_seq_nr = 0;
337 user_data->forward.statinfo.cycles = 0;
338 user_data->reversed.statinfo.cycles = 0;
339 user_data->forward.statinfo.under = FALSE;
340 user_data->reversed.statinfo.under = FALSE;
341 user_data->forward.statinfo.start_time = 0;
342 user_data->reversed.statinfo.start_time = 0;
343 user_data->forward.statinfo.time = 0;
344 user_data->reversed.statinfo.time = 0;
345 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
346 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
348 user_data->forward.saveinfo.count = 0;
349 user_data->reversed.saveinfo.count = 0;
350 user_data->forward.saveinfo.saved = FALSE;
351 user_data->reversed.saveinfo.saved = FALSE;
353 /* clear the dialog box lists */
354 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
355 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
357 /* reset graph info */
358 dialog_graph_reset(user_data);
360 #ifdef HAVE_LIBPORTAUDIO
361 /* reset the RTP player */
364 /* XXX check for error at fclose? */
365 if (user_data->forward.saveinfo.fp != NULL)
366 fclose(user_data->forward.saveinfo.fp);
367 if (user_data->reversed.saveinfo.fp != NULL)
368 fclose(user_data->reversed.saveinfo.fp);
369 user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
370 if (user_data->forward.saveinfo.fp == NULL)
371 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
372 user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
373 if (user_data->reversed.saveinfo.fp == NULL)
374 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
378 /****************************************************************************/
379 static gboolean rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
381 dialog_graph_graph_item_t *it;
386 * We sometimes get called when dgg is disabled.
387 * This is a bug since the tap listener should be removed first
393 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
396 * Find which interval this is supposed to go in and store the
397 * interval index as idx
399 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
400 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
402 rtp_time = nstime_to_msec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
406 idx = (guint32)(rtp_time)/dgg->ud->dlg.dialog_graph.interval;
408 /* some sanity checks */
409 if(idx>=NUM_GRAPH_ITEMS){
413 /* update num_items */
414 if(idx > dgg->ud->dlg.dialog_graph.num_items){
415 dgg->ud->dlg.dialog_graph.num_items=idx;
416 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
420 * Find the appropriate dialog_graph_graph_item_t structure
425 * Use the max value to highlight RTP problems
427 if (value > it->value) {
430 it->flags = it->flags | statinfo->flags;
435 /****************************************************************************/
436 /* here we can redraw the output */
438 static void rtp_draw(void *prs _U_)
443 /* forward declarations */
444 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
445 double delta, double jitter, double skew ,double bandwidth, gchar *status, gboolean marker,
446 gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
448 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
449 tap_rtp_stat_t *statinfo, packet_info *pinfo,
450 const struct _rtp_info *rtpinfo);
452 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
453 tap_rtp_stat_t *statinfo,
455 const struct _rtp_info *rtpinfo);
458 /****************************************************************************/
459 /* whenever a RTP packet is seen by the tap listener */
460 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
462 user_data_t *user_data = user_data_arg;
463 const struct _rtp_info *rtpinfo = rtpinfo_arg;
464 gboolean rtp_selected = FALSE;
466 /* we ignore packets that are not displayed */
467 if (pinfo->fd->flags.passed_dfilter == 0)
469 /* also ignore RTP Version != 2 */
470 else if (rtpinfo->info_version !=2)
472 /* is it the forward direction? */
473 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
474 && CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
475 && user_data->port_src_fwd == pinfo->srcport
476 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
477 && user_data->port_dst_fwd == pinfo->destport) {
478 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
479 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]),
480 &(user_data->forward.statinfo), pinfo,
481 (guint32)(user_data->forward.statinfo.jitter*1000));
482 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]),
483 &(user_data->forward.statinfo), pinfo,
484 (guint32)(user_data->forward.statinfo.diff*1000));
485 rtp_packet_add_info(user_data->dlg.list_fwd, user_data,
486 &(user_data->forward.statinfo), pinfo, rtpinfo);
487 rtp_packet_save_payload(&(user_data->forward.saveinfo),
488 &(user_data->forward.statinfo), pinfo, rtpinfo);
491 /* is it the reversed direction? */
492 else if (user_data->ssrc_rev == rtpinfo->info_sync_src
493 && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
494 && user_data->port_src_rev == pinfo->srcport
495 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
496 && user_data->port_dst_rev == pinfo->destport) {
497 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
498 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]),
499 &(user_data->reversed.statinfo), pinfo,
500 (guint32)(user_data->reversed.statinfo.jitter*1000));
501 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]),
502 &(user_data->reversed.statinfo), pinfo,
503 (guint32)(user_data->reversed.statinfo.diff*1000));
504 rtp_packet_add_info(user_data->dlg.list_rev, user_data,
505 &(user_data->reversed.statinfo), pinfo, rtpinfo);
506 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
507 &(user_data->reversed.statinfo), pinfo, rtpinfo);
510 /* add this RTP for future listening using the RTP Player*/
511 #ifdef HAVE_LIBPORTAUDIO
513 add_rtp_packet(rtpinfo, pinfo);
520 Replaced by using the strings instead.
521 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
522 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
523 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
524 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
525 COLOR_T_EVENT g_snprintf(color_str,sizeof(color_str),"#ef8c bfff ffff");
526 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
528 /****************************************************************************/
529 /* adds statistics information from the packet to the list */
530 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
531 tap_rtp_stat_t *statinfo, packet_info *pinfo,
532 const struct _rtp_info *rtpinfo)
540 then = pinfo->fd->abs_ts.secs;
541 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
542 tm_tmp = localtime(&then);
543 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
546 tm_tmp->tm_year + 1900,
552 /* Default to using black on white text if nothing below overrides it */
553 g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
555 if (statinfo->pt == PT_CN) {
556 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
557 /* color = COLOR_CN; */
558 g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
560 else if (statinfo->pt == PT_CN_OLD) {
561 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
562 /* color = COLOR_CN; */
563 g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
565 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
566 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
567 /* color = COLOR_ERROR; */
568 g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
570 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
571 if (statinfo->flags & STAT_FLAG_PT_T_EVENT){
572 g_snprintf(status,sizeof(status),"Payload changed to PT=%u telephone/event", statinfo->pt);
574 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
576 /* color = COLOR_WARNING; */
577 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
579 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
580 g_snprintf(status,sizeof(status),"Incorrect timestamp");
581 /* color = COLOR_WARNING; */
582 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
584 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
585 && !(statinfo->flags & STAT_FLAG_FIRST)
586 && !(statinfo->flags & STAT_FLAG_PT_CN)
587 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
588 && !(statinfo->flags & STAT_FLAG_MARKER)) {
589 g_snprintf(status,sizeof(status),"Marker missing?");
590 /* color = COLOR_WARNING; */
591 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
592 }else if (statinfo->flags & STAT_FLAG_PT_T_EVENT){
593 g_snprintf(status,sizeof(status),"PT=%u telephone/event", statinfo->pt);
595 /* color = COLOR_T_EVENT; */
596 g_snprintf(color_str,sizeof(color_str),"#ef8cbfffffff");
598 if (statinfo->flags & STAT_FLAG_MARKER) {
599 /* color = COLOR_WARNING; */
600 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
602 g_snprintf(status,sizeof(status),OK_TEXT);
604 /* is this the first packet we got in this direction? */
605 if (statinfo->flags & STAT_FLAG_FIRST) {
606 add_to_list(list, user_data,
607 pinfo->fd->num, rtpinfo->info_seq_num,
614 rtpinfo->info_marker_set,
615 timeStr, pinfo->fd->pkt_len,
620 add_to_list(list, user_data,
621 pinfo->fd->num, rtpinfo->info_seq_num,
628 rtpinfo->info_marker_set,
629 timeStr, pinfo->fd->pkt_len,
636 #define MAX_SILENCE_TICKS 1000000
637 /****************************************************************************/
638 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
639 tap_rtp_stat_t *statinfo,
641 const struct _rtp_info *rtpinfo)
648 /* is this the first packet we got in this direction? */
649 if (statinfo->flags & STAT_FLAG_FIRST) {
650 if (saveinfo->fp == NULL) {
651 saveinfo->saved = FALSE;
652 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
655 saveinfo->saved = TRUE;
658 /* save the voice information */
659 /* if there was already an error, we quit */
660 if (saveinfo->saved == FALSE)
663 /* if the captured length and packet length aren't equal, we quit
664 * if also the RTP dissector thinks there is some information missing */
665 if ((pinfo->fd->pkt_len != pinfo->fd->cap_len) &&
666 (!rtpinfo->info_all_data_present)) {
667 saveinfo->saved = FALSE;
668 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
672 /* if padding bit is set, but the padding count is bigger
673 * then the whole RTP data - error with padding count */
674 if ( (rtpinfo->info_padding_set != FALSE) &&
675 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
676 saveinfo->saved = FALSE;
677 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
681 /* do we need to insert some silence? */
682 if ((rtpinfo->info_marker_set) &&
683 !(statinfo->flags & STAT_FLAG_FIRST) &&
684 !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) &&
685 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
686 /* the amount of silence should be the difference between
687 * the last timestamp and the current one minus x
688 * x should equal the amount of information in the last frame
689 * XXX not done yet */
690 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
691 rtpinfo->info_padding_count) && i < MAX_SILENCE_TICKS; i++) {
692 switch (statinfo->reg_pt) {
703 nchars=fwrite(&tmp, 1, 1, saveinfo->fp);
706 fflush(saveinfo->fp);
710 if (rtpinfo->info_payload_type == PT_CN
711 || rtpinfo->info_payload_type == PT_CN_OLD) {
713 /*all other payloads*/
715 if (!rtpinfo->info_all_data_present) {
716 /* Not all the data was captured. */
717 saveinfo->saved = FALSE;
718 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
722 /* we put the pointer at the beginning of the RTP
723 * payload, that is, at the beginning of the RTP data
724 * plus the offset of the payload from the beginning
726 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
727 nchars=fwrite(data, sizeof(unsigned char), (rtpinfo->info_payload_len - rtpinfo->info_padding_count), saveinfo->fp);
728 saveinfo->count+=(rtpinfo->info_payload_len - rtpinfo->info_padding_count);
730 fflush(saveinfo->fp);
731 saveinfo->saved = TRUE;
739 /****************************************************************************/
742 /****************************************************************************/
744 /****************************************************************************/
745 /* close the dialog window and remove the tap listener */
746 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data)
748 /* remove tap listener */
749 protect_thread_critical_region();
750 remove_tap_listener(user_data);
751 unprotect_thread_critical_region();
753 /* close and remove temporary files */
754 if (user_data->forward.saveinfo.fp != NULL)
755 fclose(user_data->forward.saveinfo.fp);
756 if (user_data->reversed.saveinfo.fp != NULL)
757 fclose(user_data->reversed.saveinfo.fp);
758 /*XXX: test for error **/
759 ws_remove(user_data->f_tempname);
760 ws_remove(user_data->r_tempname);
762 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
763 /* destroy save_voice_as window if open */
764 if (user_data->dlg.save_voice_as_w != NULL)
765 window_destroy(user_data->dlg.save_voice_as_w);
767 /* destroy graph window if open */
768 if (user_data->dlg.dialog_graph.window != NULL)
769 window_destroy(user_data->dlg.dialog_graph.window);
771 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
772 g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
774 g_free(user_data->f_tempname);
775 g_free(user_data->r_tempname);
780 /****************************************************************************/
781 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
782 GtkNotebookPage *page _U_,
784 user_data_t *user_data _U_)
786 user_data->dlg.selected_list =
787 (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
789 user_data->dlg.selected_list_row = 0;
792 /****************************************************************************/
794 static void on_list_select_row(GtkTreeSelection *selection,
795 user_data_t *user_data/*gpointer data */)
797 user_data->dlg.selected_list_sel = selection;
801 /****************************************************************************/
802 static void dialog_graph_set_title(user_data_t* user_data)
805 if (!user_data->dlg.dialog_graph.window){
808 title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
809 get_addr_name(&(user_data->ip_src_fwd)),
810 user_data->port_src_fwd,
811 get_addr_name(&(user_data->ip_dst_fwd)),
812 user_data->port_dst_fwd,
813 get_addr_name(&(user_data->ip_src_rev)),
814 user_data->port_src_rev,
815 get_addr_name(&(user_data->ip_dst_rev)),
816 user_data->port_dst_rev);
818 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
824 /****************************************************************************/
825 static void dialog_graph_reset(user_data_t* user_data)
829 user_data->dlg.dialog_graph.needs_redraw=TRUE;
830 for(i=0;i<MAX_GRAPHS;i++){
831 for(j=0;j<NUM_GRAPH_ITEMS;j++){
832 dialog_graph_graph_item_t *dggi;
833 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
838 user_data->dlg.dialog_graph.last_interval=0xffffffff;
839 user_data->dlg.dialog_graph.max_interval=0;
840 user_data->dlg.dialog_graph.num_items=0;
842 /* create the color titles near the filter buttons */
843 for(i=0;i<MAX_GRAPHS;i++){
846 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
847 sizeof(user_data->dlg.dialog_graph.graph[0].title),
848 "%s: %s:%u to %s:%u (SSRC=0x%X)",
850 get_addr_name(&(user_data->ip_src_fwd)),
851 user_data->port_src_fwd,
852 get_addr_name(&(user_data->ip_dst_fwd)),
853 user_data->port_dst_fwd,
854 user_data->ssrc_fwd);
857 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
858 sizeof(user_data->dlg.dialog_graph.graph[0].title),
859 "%s: %s:%u to %s:%u (SSRC=0x%X)",
861 get_addr_name(&(user_data->ip_src_rev)),
862 user_data->port_src_rev,
863 get_addr_name(&(user_data->ip_dst_rev)),
864 user_data->port_dst_rev,
865 user_data->ssrc_rev);
869 dialog_graph_set_title(user_data);
872 /****************************************************************************/
873 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
875 dialog_graph_graph_item_t *it;
882 /****************************************************************************/
883 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
886 g_snprintf(buf, buf_len, "%ds",t/1000000);
887 } else if(t>=1000000){
888 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
890 g_snprintf(buf, buf_len, "%dms",t/1000);
892 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
894 g_snprintf(buf, buf_len, "%dus",t);
898 /****************************************************************************/
899 static void dialog_graph_draw(user_data_t* user_data)
902 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
903 gint32 current_interval;
904 guint32 left_x_border;
905 guint32 right_x_border;
906 guint32 top_y_border;
907 guint32 bottom_y_border;
909 int label_width, label_height;
910 guint32 draw_width, draw_height;
911 char label_string[15];
914 guint32 num_time_intervals;
915 guint32 max_value; /* max value of seen data */
916 guint32 max_y; /* max value of the Y scale */
918 if(!user_data->dlg.dialog_graph.needs_redraw){
921 user_data->dlg.dialog_graph.needs_redraw=FALSE;
924 * Find the length of the intervals we have data for
925 * so we know how large arrays we need to malloc()
927 num_time_intervals=user_data->dlg.dialog_graph.num_items;
928 /* if there isnt anything to do, just return */
929 if(num_time_intervals==0){
932 num_time_intervals+=1;
933 /* XXX move this check to _packet() */
934 if(num_time_intervals>NUM_GRAPH_ITEMS){
935 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
940 * find the max value so we can autoscale the y axis
943 for(i=0;i<MAX_GRAPHS;i++){
946 if(!user_data->dlg.dialog_graph.graph[i].display){
949 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
952 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
954 /* keep track of the max value we have encountered */
964 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
965 user_data->dlg.dialog_graph.draw_area->style->white_gc,
968 user_data->dlg.dialog_graph.draw_area->allocation.width,
969 user_data->dlg.dialog_graph.draw_area->allocation.height);
973 * Calculate the y scale we should use
975 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
976 max_y=yscale_max[MAX_YSCALE-1];
977 for(i=MAX_YSCALE-1;i>0;i--){
978 if(max_value<yscale_max[i]){
983 /* the user had specified an explicit y scale to use */
984 max_y=user_data->dlg.dialog_graph.max_y_units;
988 * Calculate size of borders surrounding the plot
989 * The border on the right side needs to be adjusted depending
990 * on the width of the text labels. For simplicity we assume that the
991 * top y scale label will be the widest one
993 print_time_scale_string(label_string, sizeof(label_string), max_y);
994 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
995 pango_layout_get_pixel_size(layout, &label_width, &label_height);
997 right_x_border=label_width+20;
999 bottom_y_border=label_height+20;
1003 * Calculate the size of the drawing area for the actual plot
1005 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1006 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1010 * Draw the y axis and labels
1011 * (we always draw the y scale with 11 ticks along the axis)
1013 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1014 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1016 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1017 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1023 /* first, middle and last tick are slightly longer */
1027 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1028 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1029 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1030 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1031 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1032 /* draw the labels */
1034 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1035 pango_layout_set_text(layout, label_string, -1);
1036 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1037 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1038 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1039 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1040 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1044 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1045 pango_layout_set_text(layout, label_string, -1);
1046 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1047 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1048 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1049 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1050 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1054 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1055 pango_layout_set_text(layout, label_string, -1);
1056 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1057 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1058 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1059 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1060 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1068 * if we have not specified the last_interval via the gui,
1069 * then just pick the current end of the capture so that is scrolls
1070 * nicely when doing live captures
1072 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1073 last_interval=user_data->dlg.dialog_graph.max_interval;
1075 last_interval=user_data->dlg.dialog_graph.last_interval;
1082 /* plot the x-scale */
1083 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc, left_x_border, user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1, user_data->dlg.dialog_graph.pixmap_width-right_x_border+1, user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1);
1085 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1086 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1087 first_interval*=user_data->dlg.dialog_graph.interval;
1094 while(interval_delta<((last_interval-first_interval)/10)){
1095 interval_delta*=delta_multiplier;
1096 if(delta_multiplier==5){
1103 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1106 /* if pixels_per_tick is <5, only draw every 10 ticks */
1107 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1111 if(current_interval%interval_delta){
1117 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1118 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1119 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1120 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1121 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1122 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1125 if(user_data->dlg.dialog_graph.interval>=1000){
1126 g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
1127 } else if(user_data->dlg.dialog_graph.interval>=100){
1128 g_snprintf(label_string, sizeof(label_string), "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1129 } else if(user_data->dlg.dialog_graph.interval>=10){
1130 g_snprintf(label_string, sizeof(label_string), "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1132 g_snprintf(label_string, sizeof(label_string), "%d.%3ds", current_interval/1000,current_interval%1000);
1134 pango_layout_set_text(layout, label_string, -1);
1135 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1136 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1137 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1138 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1139 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1151 * Draw "x" for Sequence Errors and "m" for Marks
1153 /* Draw the labels Fwd and Rev */
1154 g_strlcpy(label_string,"<-Fwd",sizeof(label_string));
1155 pango_layout_set_text(layout, label_string, -1);
1156 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1157 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1158 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1159 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1160 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1162 g_strlcpy(label_string,"<-Rev",sizeof(label_string));
1163 pango_layout_set_text(layout, label_string, -1);
1164 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1165 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1166 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1167 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1168 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1171 /* Draw the marks */
1172 for(i=MAX_GRAPHS-1;i>=0;i--){
1174 guint32 x_pos, prev_x_pos;
1176 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1177 if (!user_data->dlg.dialog_graph.graph[i].display){
1180 /* initialize prev x/y to the low left corner of the graph */
1181 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;
1183 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1184 x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1186 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1187 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1188 g_strlcpy(label_string,"x",sizeof(label_string));
1190 g_strlcpy(label_string,"m",sizeof(label_string));
1193 pango_layout_set_text(layout, label_string, -1);
1194 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1195 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1196 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1198 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1206 g_object_unref(G_OBJECT(layout));
1209 * Loop over all graphs and draw them
1211 for(i=MAX_GRAPHS-1;i>=0;i--){
1213 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1214 if (!user_data->dlg.dialog_graph.graph[i].display){
1217 /* initialize prev x/y to the low left corner of the graph */
1218 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;
1219 prev_y_pos=draw_height-1+top_y_border;
1221 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1223 x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1224 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1228 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1231 /* dont need to draw anything if the segment
1232 * is entirely above the top of the graph
1234 if( (prev_y_pos==0) && (y_pos==0) ){
1241 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1242 x_pos, draw_height-1+top_y_border,
1252 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1253 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1254 user_data->dlg.dialog_graph.pixmap,
1257 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1260 /* update the scrollbar */
1261 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1262 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1263 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1264 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1265 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1267 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1269 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1270 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1271 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1275 /****************************************************************************/
1276 static void dialog_graph_redraw(user_data_t* user_data)
1278 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1279 dialog_graph_draw(user_data);
1282 /****************************************************************************/
1283 static void quit(GtkWidget *widget _U_, user_data_t *user_data)
1285 user_data->dlg.dialog_graph.window = NULL;
1288 /****************************************************************************/
1289 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1291 user_data_t *user_data;
1293 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1299 gdk_draw_pixmap(widget->window,
1300 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1301 user_data->dlg.dialog_graph.pixmap,
1302 event->area.x, event->area.y,
1303 event->area.x, event->area.y,
1304 event->area.width, event->area.height);
1309 /****************************************************************************/
1310 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1312 user_data_t *user_data;
1314 #if GTK_CHECK_VERSION(2,6,0)
1318 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1324 if(user_data->dlg.dialog_graph.pixmap){
1325 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1326 user_data->dlg.dialog_graph.pixmap=NULL;
1329 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1330 widget->allocation.width,
1331 widget->allocation.height,
1333 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1334 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1336 #if GTK_CHECK_VERSION(2,6,0)
1337 bt_save = g_object_get_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save");
1338 g_object_set_data(G_OBJECT(bt_save), "pixmap", user_data->dlg.dialog_graph.pixmap);
1339 gtk_widget_set_sensitive(bt_save, TRUE);
1342 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1343 widget->style->white_gc,
1346 widget->allocation.width,
1347 widget->allocation.height);
1349 /* set up the colors and the GC structs for this pixmap */
1350 for(i=0;i<MAX_GRAPHS;i++){
1351 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1352 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1355 dialog_graph_redraw(user_data);
1359 /****************************************************************************/
1360 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1362 user_data_t *user_data=(user_data_t *)data;
1365 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1366 if(user_data->dlg.dialog_graph.last_interval==mi){
1369 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1370 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1374 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1376 dialog_graph_redraw(user_data);
1380 /****************************************************************************/
1381 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1383 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1384 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1385 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.draw_area), "user_data_t", user_data);
1387 gtk_widget_set_size_request(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1389 /* signals needed to handle backing pixmap */
1390 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1391 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1393 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1394 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1396 /* create the associated scrollbar */
1397 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1398 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1399 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1400 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1401 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1404 /****************************************************************************/
1405 static void disable_graph(dialog_graph_graph_t *dgg)
1409 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1414 /****************************************************************************/
1415 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1417 /* this graph is not active, just update display and redraw */
1418 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1420 dialog_graph_redraw(dgg->ud);
1425 cf_retap_packets(&cfile);
1426 dialog_graph_redraw(dgg->ud);
1431 /****************************************************************************/
1432 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1438 hbox=gtk_hbox_new(FALSE, 3);
1439 gtk_container_add(GTK_CONTAINER(box), hbox);
1440 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1441 gtk_widget_show(hbox);
1443 g_snprintf(str, sizeof(str), "Graph %d", num);
1444 dgg->display_button=gtk_toggle_button_new_with_label(str);
1445 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1446 gtk_widget_show(dgg->display_button);
1447 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1448 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1450 label=gtk_label_new(dgg->title);
1451 gtk_widget_show(label);
1452 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1454 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1455 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1456 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1457 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1458 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1463 /****************************************************************************/
1464 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1471 frame=gtk_frame_new("Graphs");
1472 gtk_container_add(GTK_CONTAINER(box), frame);
1473 gtk_widget_show(frame);
1475 vbox=gtk_vbox_new(FALSE, 1);
1476 gtk_container_add(GTK_CONTAINER(frame), vbox);
1477 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1478 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1479 gtk_widget_show(vbox);
1481 for(i=0;i<MAX_GRAPHS;i++){
1482 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1485 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1486 gtk_widget_show(label);
1487 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1492 /****************************************************************************/
1493 static void yscale_select(GtkWidget *item, gpointer key)
1496 user_data_t *user_data;
1498 user_data=(user_data_t *)key;
1499 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1501 user_data->dlg.dialog_graph.max_y_units=yscale_max[i];
1502 dialog_graph_redraw(user_data);
1505 /****************************************************************************/
1506 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1509 user_data_t *user_data;
1511 user_data=(user_data_t *)key;
1512 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1513 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[i];
1514 dialog_graph_redraw(user_data);
1517 /****************************************************************************/
1518 static void tick_interval_select(GtkWidget *item, gpointer key)
1521 user_data_t *user_data;
1523 user_data=(user_data_t *)key;
1524 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1526 user_data->dlg.dialog_graph.interval=tick_interval_values[i];
1527 cf_retap_packets(&cfile);
1528 dialog_graph_redraw(user_data);
1531 /****************************************************************************/
1533 create_yscale_max_menu_items(user_data_t* user_data)
1536 GtkWidget *combo_box;
1539 combo_box = gtk_combo_box_new_text ();
1541 for(i=0;i<MAX_YSCALE;i++){
1542 if(yscale_max[i]==AUTO_MAX_YSCALE){
1543 g_strlcpy(str,"Auto",sizeof(str));
1545 g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1547 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1549 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0);
1550 g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), (gpointer)user_data);
1555 /****************************************************************************/
1557 create_pixels_per_tick_menu_items(user_data_t *user_data)
1560 GtkWidget *combo_box;
1563 combo_box = gtk_combo_box_new_text ();
1565 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1566 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1567 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1569 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), DEFAULT_PIXELS_PER_TICK);
1571 g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), (gpointer)user_data);
1576 /****************************************************************************/
1578 create_tick_interval_menu_items(user_data_t *user_data)
1580 GtkWidget *combo_box;
1584 combo_box = gtk_combo_box_new_text ();
1586 for(i=0;i<MAX_TICK_VALUES;i++){
1587 if(tick_interval_values[i]>=1000){
1588 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1589 } else if(tick_interval_values[i]>=100){
1590 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1591 } else if(tick_interval_values[i]>=10){
1592 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1594 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1596 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1598 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), 0);
1599 g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1604 /****************************************************************************/
1605 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, GtkWidget *(*func)(user_data_t* user_data))
1609 GtkWidget *combo_box;
1611 hbox=gtk_hbox_new(FALSE, 0);
1612 gtk_container_add(GTK_CONTAINER(box), hbox);
1613 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1614 gtk_widget_show(hbox);
1616 label=gtk_label_new(name);
1617 gtk_widget_show(label);
1618 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1620 combo_box = (*func)(user_data);
1621 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1622 gtk_widget_show(combo_box);
1625 /****************************************************************************/
1626 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1628 GtkWidget *frame_vbox;
1632 frame_vbox=gtk_vbox_new(FALSE, 0);
1633 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1634 gtk_widget_show(frame_vbox);
1636 frame = gtk_frame_new("X Axis");
1637 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1638 gtk_widget_show(frame);
1640 vbox=gtk_vbox_new(FALSE, 0);
1641 gtk_container_add(GTK_CONTAINER(frame), vbox);
1642 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1643 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1644 gtk_widget_show(vbox);
1646 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1647 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1649 frame = gtk_frame_new("Y Axis");
1650 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1651 gtk_widget_show(frame);
1653 vbox=gtk_vbox_new(FALSE, 0);
1654 gtk_container_add(GTK_CONTAINER(frame), vbox);
1655 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1656 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1657 gtk_widget_show(vbox);
1659 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1664 /****************************************************************************/
1665 static void dialog_graph_init_window(user_data_t* user_data)
1669 GtkWidget *bt_close;
1670 #if GTK_CHECK_VERSION(2,6,0)
1672 GtkTooltips *tooltips = gtk_tooltips_new();
1675 /* create the main window */
1676 user_data->dlg.dialog_graph.window=dlg_window_new("I/O Graphs"); /* transient_for top_level */
1678 vbox=gtk_vbox_new(FALSE, 0);
1679 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1680 gtk_widget_show(vbox);
1682 create_draw_area(user_data, vbox);
1684 hbox=gtk_hbox_new(FALSE, 3);
1685 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1686 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1687 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1688 gtk_widget_show(hbox);
1690 create_filter_area(user_data, hbox);
1691 create_ctrl_area(user_data, hbox);
1693 dialog_graph_set_title(user_data);
1695 #if GTK_CHECK_VERSION(2,6,0)
1696 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE, NULL);
1698 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1700 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1701 gtk_widget_show(hbox);
1703 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1704 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1706 #if GTK_CHECK_VERSION(2,6,0)
1707 bt_save = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_SAVE);
1708 gtk_widget_set_sensitive(bt_save, FALSE);
1709 gtk_tooltips_set_tip(tooltips, bt_save, "Save the displayed graph to a file", NULL);
1710 g_signal_connect(bt_save, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
1711 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save", bt_save);
1714 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1716 gtk_widget_show(user_data->dlg.dialog_graph.window);
1717 window_present(user_data->dlg.dialog_graph.window);
1722 /****************************************************************************/
1723 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1725 if (user_data->dlg.dialog_graph.window != NULL) {
1726 /* There's already a graph window; reactivate it. */
1727 reactivate_window(user_data->dlg.dialog_graph.window);
1731 dialog_graph_init_window(user_data);
1735 /****************************************************************************/
1737 static void on_goto_bt_clicked_lst(GtkWidget *bt _U_, user_data_t *user_data _U_)
1740 GtkTreeModel *model;
1741 GtkTreeSelection *selection;
1744 selection = user_data->dlg.selected_list_sel;
1746 if (selection==NULL)
1749 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1750 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1751 cf_goto_frame(&cfile, fnumber);
1756 static void draw_stat(user_data_t *user_data);
1758 /****************************************************************************/
1759 /* re-dissects all packets */
1760 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1762 GString *error_string;
1764 /* remove tap listener */
1765 protect_thread_critical_region();
1766 remove_tap_listener(user_data);
1767 unprotect_thread_critical_region();
1769 /* register tap listener */
1770 error_string = register_tap_listener("rtp", user_data, NULL, 0,
1771 rtp_reset, rtp_packet, rtp_draw);
1772 if (error_string != NULL) {
1773 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1774 g_string_free(error_string, TRUE);
1778 /* retap all packets */
1779 cf_retap_packets(&cfile);
1781 /* draw statistics info */
1782 draw_stat(user_data);
1786 #ifdef HAVE_LIBPORTAUDIO
1787 /****************************************************************************/
1789 on_player_bt_clicked(GtkButton *button _U_, gpointer user_data _U_)
1791 /*rtp_player_init(voip_calls_get_info());*/
1792 rtp_player_init(NULL);
1794 #endif /* HAVE_LIBPORTAUDIO */
1796 static void on_next_bt_clicked_list(GtkWidget *bt _U_, user_data_t *user_data _U_)
1799 GtkTreeModel *model;
1801 GtkTreeSelection *selection;
1804 selection = user_data->dlg.selected_list_sel;
1806 if (selection==NULL)
1810 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1811 while (gtk_tree_model_iter_next (model,&iter)) {
1812 gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1813 if (strcmp(text, OK_TEXT) != 0) {
1814 gtk_tree_selection_select_iter (selection, &iter);
1815 path = gtk_tree_model_get_path(model, &iter);
1816 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1819 gtk_tree_path_free(path);
1826 if (user_data->dlg.number_of_nok>1){
1827 /* Get the first iter and select it before starting over */
1828 gtk_tree_model_get_iter_first(model, &iter);
1829 gtk_tree_selection_select_iter (selection, &iter);
1836 /****************************************************************************/
1837 /* when we want to save the information */
1838 static gboolean save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
1841 GtkWidget *rev, *forw, *both;
1842 user_data_t *user_data;
1844 GtkListStore *store;
1846 GtkTreeModel *model;
1847 gboolean more_items = TRUE;
1849 /* To Hold data from the list row */
1850 guint32 packet; /* Packet */
1851 guint16 sequence; /* Sequence */
1852 guint32 timestamp; /* timestamp */
1853 gfloat delta; /* Delta(ms) */
1854 gfloat jitter; /* Jitter(ms) */
1855 gfloat skew; /* Skew(ms) */
1856 gfloat ipbw; /* IP BW(kbps) */
1857 gboolean marker; /* Marker */
1858 char * status_str; /* Status */
1859 char * date_str; /* Date */
1860 guint length; /* Length */
1866 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
1868 /* Perhaps the user specified a directory instead of a file.
1869 * Check whether they did.
1871 if (test_for_directory(g_dest) == EISDIR) {
1872 /* It's a directory - set the file selection box to display it. */
1873 set_last_open_dir(g_dest);
1875 file_selection_set_current_folder(fc, get_last_open_dir());
1876 gtk_file_chooser_set_current_name(fc, "");
1877 return FALSE; /* run the dialog again */
1880 rev = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
1881 forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
1882 both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
1883 user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
1885 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
1886 fp = ws_fopen(g_dest, "w");
1888 open_failure_alert_box(g_dest, errno, TRUE);
1890 return TRUE; /* we're done */
1893 if (GTK_TOGGLE_BUTTON(both)->active) {
1894 fprintf(fp, "Forward\n");
1896 write_failure_alert_box(g_dest, errno);
1899 return TRUE; /* we're done */
1903 for(j = 0; j < NUM_COLS; j++) {
1905 fprintf(fp,"%s",titles[j]);
1907 fprintf(fp,",%s",titles[j]);
1912 write_failure_alert_box(g_dest, errno);
1917 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
1918 store = GTK_LIST_STORE(model);
1919 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
1922 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1923 PACKET_COLUMN, &packet,
1924 SEQUENCE_COLUMN, &sequence,
1925 TIMESTAMP_COLUMN, ×tamp,
1926 DELTA_COLUMN, &delta,
1927 JITTER_COLUMN, &jitter,
1930 MARKER_COLUMN, &marker,
1931 STATUS_COLUMN, &status_str,
1932 DATE_COLUMN, &date_str,
1933 LENGTH_COLUMN, &length,
1935 fprintf(fp, "%u",packet);
1936 fprintf(fp, ",%u", sequence);
1937 fprintf(fp, ",%u", timestamp);
1938 fprintf(fp, ",%.2f", delta);
1939 fprintf(fp, ",%.2f", jitter);
1940 fprintf(fp, ",%.2f", skew);
1941 fprintf(fp, ",%.2f", ipbw);
1942 fprintf(fp, ",%s", marker? "SET" : "");
1943 fprintf(fp, ",%s", status_str);
1944 fprintf(fp, ",%s", date_str);
1945 fprintf(fp, ",%u", length);
1950 write_failure_alert_box(g_dest, errno);
1953 return TRUE; /* we're done */
1956 more_items = gtk_tree_model_iter_next (model,&iter);
1960 if (fclose(fp) == EOF) {
1961 write_failure_alert_box(g_dest, errno);
1963 return TRUE; /* we're done */
1967 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
1969 if (GTK_TOGGLE_BUTTON(both)->active) {
1970 fp = ws_fopen(g_dest, "a");
1972 open_failure_alert_box(g_dest, errno, TRUE);
1974 return TRUE; /* we're done */
1976 fprintf(fp, "\nReverse\n");
1978 write_failure_alert_box(g_dest, errno);
1981 return TRUE; /* we're done */
1984 fp = ws_fopen(g_dest, "w");
1986 open_failure_alert_box(g_dest, errno, TRUE);
1988 return TRUE; /* we're done */
1991 for(j = 0; j < NUM_COLS; j++) {
1993 fprintf(fp,"%s",titles[j]);
1995 fprintf(fp,",%s",titles[j]);
2000 write_failure_alert_box(g_dest, errno);
2003 return TRUE; /* we're done */
2005 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
2006 store = GTK_LIST_STORE(model);
2007 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2012 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2013 PACKET_COLUMN, &packet,
2014 SEQUENCE_COLUMN, &sequence,
2015 TIMESTAMP_COLUMN, ×tamp,
2016 DELTA_COLUMN, &delta,
2017 JITTER_COLUMN, &jitter,
2020 MARKER_COLUMN, &marker,
2021 STATUS_COLUMN, &status_str,
2022 DATE_COLUMN, &date_str,
2023 LENGTH_COLUMN, &length,
2025 fprintf(fp, "%u",packet);
2026 fprintf(fp, ",%u", sequence);
2027 fprintf(fp, ",%u", timestamp);
2028 fprintf(fp, ",%.2f", delta);
2029 fprintf(fp, ",%.2f", jitter);
2030 fprintf(fp, ",%.2f", skew);
2031 fprintf(fp, ",%.2f", ipbw);
2032 fprintf(fp, ",%s", marker? "SET" : "");
2033 fprintf(fp, ",%s", status_str);
2034 fprintf(fp, ",%s", date_str);
2035 fprintf(fp, ",%u", length);
2040 write_failure_alert_box(g_dest, errno);
2043 return TRUE; /* we're done */
2046 more_items = gtk_tree_model_iter_next (model,&iter);
2049 if (fclose(fp) == EOF) {
2050 write_failure_alert_box(g_dest, errno);
2052 return TRUE; /* we're done */
2057 return TRUE; /* we're done */
2060 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2062 user_data->dlg.save_csv_as_w = NULL;
2065 /* when the user wants to save the csv information in a file */
2066 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
2070 GtkWidget *label_format;
2071 GtkWidget *channels_label;
2072 GSList *channels_group = NULL;
2073 GtkWidget *forward_rb;
2074 GtkWidget *reversed_rb;
2077 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2078 if (user_data->dlg.save_csv_as_w != NULL) {
2079 /* There's already a Save CSV info dialog box; reactivate it. */
2080 reactivate_window(user_data->dlg.save_csv_as_w);
2084 user_data->dlg.save_csv_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2085 GTK_WINDOW(user_data->dlg.notebook),
2086 GTK_FILE_CHOOSER_ACTION_SAVE,
2087 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2088 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2090 #if GTK_CHECK_VERSION(2,8,0)
2091 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
2093 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_csv_as_w),GTK_WINDOW(user_data->dlg.window));
2095 /* Container for each row of widgets */
2096 vertb = gtk_vbox_new(FALSE, 0);
2097 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2098 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2099 gtk_widget_show (vertb);
2101 table1 = gtk_table_new (2, 4, FALSE);
2102 gtk_widget_show (table1);
2103 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2104 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2105 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2107 label_format = gtk_label_new ("Format: Comma Separated Values");
2108 gtk_widget_show (label_format);
2109 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2110 (GtkAttachOptions) (GTK_FILL),
2111 (GtkAttachOptions) (0), 0, 0);
2113 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2116 channels_label = gtk_label_new ("Channels: ");
2117 gtk_widget_show (channels_label);
2118 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2119 (GtkAttachOptions) (GTK_FILL),
2120 (GtkAttachOptions) (0), 0, 0);
2121 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2123 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2124 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2125 gtk_widget_show (forward_rb);
2126 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2127 (GtkAttachOptions) (GTK_FILL),
2128 (GtkAttachOptions) (0), 0, 0);
2130 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed ");
2131 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2132 gtk_widget_show (reversed_rb);
2133 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2134 (GtkAttachOptions) (GTK_FILL),
2135 (GtkAttachOptions) (0), 0, 0);
2137 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2138 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2139 gtk_widget_show (both_rb);
2140 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2141 (GtkAttachOptions) (GTK_FILL),
2142 (GtkAttachOptions) (0), 0, 0);
2144 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2146 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2147 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2148 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2149 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2151 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2152 G_CALLBACK(window_delete_event_cb), NULL);
2153 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2154 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2156 gtk_widget_show(user_data->dlg.save_csv_as_w);
2157 window_present(user_data->dlg.save_csv_as_w);
2159 /* "Run" the GtkFileChooserDialog. */
2160 /* Upon exit: If "Accept" run the OK callback. */
2161 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2162 /* Destroy the window. */
2163 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2164 /* return with a TRUE status so that the dialog window will be destroyed. */
2165 /* Trying to re-run the dialog after popping up an alert box will not work */
2166 /* since the user will not be able to dismiss the alert box. */
2167 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2168 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2170 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2171 /* GtkFileChooserDialog. */
2172 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
2173 if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
2174 break; /* we're done */
2177 window_destroy(user_data->dlg.save_csv_as_w);
2181 /****************************************************************************/
2182 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2184 /* Note that we no longer have a Save voice info dialog box. */
2185 user_data->dlg.save_voice_as_w = NULL;
2188 /****************************************************************************/
2189 /* here we save it into a file that user specified */
2190 /* XXX what about endians here? could go something wrong? */
2191 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2193 FILE *to_stream, *forw_stream, *rev_stream;
2194 size_t fwritten, rwritten;
2195 int f_rawvalue, r_rawvalue, rawvalue;
2198 guint32 f_write_silence = 0;
2199 guint32 r_write_silence = 0;
2201 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2202 gboolean stop_flag = FALSE;
2205 forw_stream = ws_fopen(user_data->f_tempname, "rb");
2206 if (forw_stream == NULL)
2208 rev_stream = ws_fopen(user_data->r_tempname, "rb");
2209 if (rev_stream == NULL) {
2210 fclose(forw_stream);
2214 /* open file for saving */
2215 to_stream = ws_fopen(dest, "wb");
2216 if (to_stream == NULL) {
2217 fclose(forw_stream);
2222 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2224 if (format == SAVE_AU_FORMAT) /* au format */
2226 /* First we write the .au header. XXX Hope this is endian independant */
2227 /* the magic word 0x2e736e64 == .snd */
2228 phtonl(pd, 0x2e736e64);
2229 nchars=fwrite(pd, 1, 4, to_stream);
2230 /* header offset == 24 bytes */
2232 nchars=fwrite(pd, 1, 4, to_stream);
2233 /* total length; it is permitted to set this to 0xffffffff */
2235 nchars=fwrite(pd, 1, 4, to_stream);
2236 /* encoding format == 16-bit linear PCM */
2238 nchars=fwrite(pd, 1, 4, to_stream);
2239 /* sample rate == 8000 Hz */
2241 nchars=fwrite(pd, 1, 4, to_stream);
2244 nchars=fwrite(pd, 1, 4, to_stream);
2248 /* only forward direction */
2249 case SAVE_FORWARD_DIRECTION_MASK: {
2250 progbar_count = user_data->forward.saveinfo.count;
2251 progbar_quantum = user_data->forward.saveinfo.count/100;
2252 while ((f_rawvalue = getc(forw_stream)) != EOF) {
2255 if((count > progbar_nextstep) && (count <= progbar_count)) {
2256 update_progress_dlg(progbar,
2257 (gfloat) count/progbar_count, "Saving");
2258 progbar_nextstep = progbar_nextstep + progbar_quantum;
2262 if (user_data->forward.statinfo.pt == PT_PCMU){
2263 sample = ulaw2linear((unsigned char)f_rawvalue);
2266 else if(user_data->forward.statinfo.pt == PT_PCMA){
2267 sample = alaw2linear((unsigned char)f_rawvalue);
2271 fclose(forw_stream);
2274 destroy_progress_dlg(progbar);
2278 fwritten = fwrite(pd, 1, 2, to_stream);
2280 fclose(forw_stream);
2283 destroy_progress_dlg(progbar);
2289 /* only reversed direction */
2290 case SAVE_REVERSE_DIRECTION_MASK: {
2291 progbar_count = user_data->reversed.saveinfo.count;
2292 progbar_quantum = user_data->reversed.saveinfo.count/100;
2293 while ((r_rawvalue = getc(rev_stream)) != EOF) {
2296 if((count > progbar_nextstep) && (count <= progbar_count)) {
2297 update_progress_dlg(progbar,
2298 (gfloat) count/progbar_count, "Saving");
2299 progbar_nextstep = progbar_nextstep + progbar_quantum;
2303 if (user_data->reversed.statinfo.pt == PT_PCMU){
2304 sample = ulaw2linear((unsigned char)r_rawvalue);
2307 else if(user_data->reversed.statinfo.pt == PT_PCMA){
2308 sample = alaw2linear((unsigned char)r_rawvalue);
2312 fclose(forw_stream);
2315 destroy_progress_dlg(progbar);
2319 rwritten = fwrite(pd, 1, 2, to_stream);
2321 fclose(forw_stream);
2324 destroy_progress_dlg(progbar);
2330 /* both directions */
2331 case SAVE_BOTH_DIRECTION_MASK: {
2332 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2333 (progbar_count = user_data->forward.saveinfo.count) :
2334 (progbar_count = user_data->reversed.saveinfo.count);
2335 progbar_quantum = progbar_count/100;
2336 /* since conversation in one way can start later than in the other one,
2337 * we have to write some silence information for one channel */
2338 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2339 f_write_silence = (guint32)
2340 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*(8000/1000));
2342 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2343 r_write_silence = (guint32)
2344 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*(8000/1000));
2349 if((count > progbar_nextstep) && (count <= progbar_count)) {
2350 update_progress_dlg(progbar,
2351 (gfloat) count/progbar_count, "Saving");
2352 progbar_nextstep = progbar_nextstep + progbar_quantum;
2355 if(f_write_silence > 0) {
2356 r_rawvalue = getc(rev_stream);
2357 switch (user_data->forward.statinfo.reg_pt) {
2359 f_rawvalue = SILENCE_PCMU;
2362 f_rawvalue = SILENCE_PCMA;
2370 else if(r_write_silence > 0) {
2371 f_rawvalue = getc(forw_stream);
2372 switch (user_data->reversed.statinfo.reg_pt) {
2374 r_rawvalue = SILENCE_PCMU;
2377 r_rawvalue = SILENCE_PCMA;
2386 f_rawvalue = getc(forw_stream);
2387 r_rawvalue = getc(rev_stream);
2389 if ((r_rawvalue == EOF) && (f_rawvalue == EOF))
2391 if ((user_data->forward.statinfo.pt == PT_PCMU) && (user_data->reversed.statinfo.pt == PT_PCMU)){
2392 sample = (ulaw2linear((unsigned char)r_rawvalue) + ulaw2linear((unsigned char)f_rawvalue)) / 2;
2395 else if((user_data->forward.statinfo.pt == PT_PCMA) && (user_data->reversed.statinfo.pt == PT_PCMA)){
2396 sample = (alaw2linear((unsigned char)r_rawvalue) + alaw2linear((unsigned char)f_rawvalue)) / 2;
2401 fclose(forw_stream);
2404 destroy_progress_dlg(progbar);
2409 rwritten = fwrite(pd, 1, 2, to_stream);
2411 fclose(forw_stream);
2414 destroy_progress_dlg(progbar);
2421 else if (format == SAVE_RAW_FORMAT) /* raw format */
2425 /* only forward direction */
2426 case SAVE_FORWARD_DIRECTION_MASK: {
2427 progbar_count = user_data->forward.saveinfo.count;
2428 progbar_quantum = user_data->forward.saveinfo.count/100;
2429 stream = forw_stream;
2432 /* only reversed direction */
2433 case SAVE_REVERSE_DIRECTION_MASK: {
2434 progbar_count = user_data->reversed.saveinfo.count;
2435 progbar_quantum = user_data->reversed.saveinfo.count/100;
2436 stream = rev_stream;
2440 fclose(forw_stream);
2443 destroy_progress_dlg(progbar);
2450 /* XXX how do you just copy the file? */
2451 while ((rawvalue = getc(stream)) != EOF) {
2454 if((count > progbar_nextstep) && (count <= progbar_count)) {
2455 update_progress_dlg(progbar,
2456 (gfloat) count/progbar_count, "Saving");
2457 progbar_nextstep = progbar_nextstep + progbar_quantum;
2461 if (putc(rawvalue, to_stream) == EOF) {
2462 fclose(forw_stream);
2465 destroy_progress_dlg(progbar);
2471 destroy_progress_dlg(progbar);
2472 fclose(forw_stream);
2479 /****************************************************************************/
2480 /* the user wants to save in a file */
2481 /* XXX support for different formats is currently commented out */
2482 static gboolean save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2485 /*GtkWidget *wav, *sw;*/
2486 GtkWidget *au, *raw;
2487 GtkWidget *rev, *forw, *both;
2488 user_data_t *user_data;
2489 gint channels, format;
2491 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2493 /* Perhaps the user specified a directory instead of a file.
2494 * Check whether they did.
2496 if (test_for_directory(g_dest) == EISDIR) {
2497 /* It's a directory - set the file selection box to display it. */
2498 set_last_open_dir(g_dest);
2500 file_selection_set_current_folder(fc, get_last_open_dir());
2501 gtk_file_chooser_set_current_name(fc, "");
2502 return FALSE; /* run the dialog again */
2506 wav = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2507 sw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2509 au = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
2510 raw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
2511 rev = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2512 forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
2513 both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
2514 user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
2516 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2517 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2518 * disable the ok button or disable the buttons for direction if only one is not ok. The
2519 * problem is if we open the save voice dialog and then click the refresh button and maybe
2520 * the state changes, so we can't save anymore. In this case we should be able to update
2521 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2524 /* we can not save in both directions */
2525 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2526 /* there are many combinations here, we just exit when first matches */
2527 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2528 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2529 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2530 "Can't save in a file: Unsupported codec!");
2531 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2532 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2533 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2534 "Can't save in a file: Wrong length of captured packets!");
2535 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2536 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2537 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2538 "Can't save in a file: RTP data with padding!");
2539 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2540 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2541 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2542 "Can't save in a file: Not all data in all packets was captured!");
2544 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2545 "Can't save in a file: File I/O problem!");
2547 return TRUE; /* we're done */
2549 /* we can not save forward direction */
2550 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2551 (GTK_TOGGLE_BUTTON (both)->active))) {
2552 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2553 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2554 "Can't save forward direction in a file: Unsupported codec!");
2555 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2556 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2557 "Can't save forward direction in a file: Wrong length of captured packets!");
2558 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2559 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2560 "Can't save forward direction in a file: RTP data with padding!");
2561 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2562 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2563 "Can't save forward direction in a file: Not all data in all packets was captured!");
2565 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2566 "Can't save forward direction in a file: File I/O problem!");
2568 return TRUE; /* we're done */
2570 /* we can not save reversed direction */
2571 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2572 (GTK_TOGGLE_BUTTON (both)->active))) {
2573 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2574 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2575 "Can't save reversed direction in a file: Unsupported codec!");
2576 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2577 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2578 "Can't save reversed direction in a file: Wrong length of captured packets!");
2579 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2580 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2581 "Can't save reversed direction in a file: RTP data with padding!");
2582 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2583 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2584 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2585 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2586 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2587 "Can't save reversed direction in a file: No RTP data!");
2589 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2590 "Can't save reversed direction in a file: File I/O problem!");
2592 return TRUE; /* we're done */
2596 if (GTK_TOGGLE_BUTTON (wav)->active)
2597 format = SAVE_WAV_FORMAT;
2600 if (GTK_TOGGLE_BUTTON (au)->active)
2601 format = SAVE_AU_FORMAT;
2603 else if (GTK_TOGGLE_BUTTON (sw)->active)
2604 format = SAVE_SW_FORMAT;
2606 else if (GTK_TOGGLE_BUTTON (raw)->active)
2607 format = SAVE_RAW_FORMAT;
2609 format = SAVE_NONE_FORMAT;
2611 if (GTK_TOGGLE_BUTTON (rev)->active)
2612 channels = SAVE_REVERSE_DIRECTION_MASK;
2613 else if (GTK_TOGGLE_BUTTON (both)->active)
2614 channels = SAVE_BOTH_DIRECTION_MASK;
2616 channels = SAVE_FORWARD_DIRECTION_MASK;
2618 /* direction/format validity*/
2619 if (format == SAVE_AU_FORMAT)
2621 /* make sure streams are alaw/ulaw */
2622 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != PT_PCMA) && (user_data->forward.statinfo.pt != PT_PCMU)){
2623 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2624 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2626 return TRUE; /* we're done */
2628 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != PT_PCMA) && (user_data->reversed.statinfo.pt != PT_PCMU)){
2629 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2630 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2632 return TRUE; /* we're done */
2634 /* make sure pt's don't differ */
2635 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2636 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2637 "Can't save in a file: Forward and reverse direction differ in type");
2639 return TRUE; /* we're done */
2642 else if (format == SAVE_RAW_FORMAT)
2644 /* can't save raw in both directions */
2645 if (channels == SAVE_BOTH_DIRECTION_MASK){
2646 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2647 "Can't save in a file: Unable to save raw data in both directions");
2649 return TRUE; /* we're done */
2654 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2655 "Can't save in a file: Invalid save format");
2657 return TRUE; /* we're done */
2660 if(!copy_file(g_dest, channels, format, user_data)) {
2661 /* XXX - report the error type! */
2662 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2663 "An error occurred while saving voice in a file!");
2665 return TRUE; /* we're done */
2669 return TRUE; /* we're done */
2672 /****************************************************************************/
2673 /* when the user wants to save the voice information in a file */
2674 /* XXX support for different formats is currently commented out */
2675 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
2679 GtkWidget *label_format;
2680 GtkWidget *channels_label;
2681 GSList *format_group = NULL;
2682 GSList *channels_group = NULL;
2683 GtkWidget *forward_rb;
2684 GtkWidget *reversed_rb;
2686 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2690 /* if we can't save in a file: wrong codec, cut packets or other errors */
2691 /* Should the error arise here or later when you click ok button ?
2692 * if we do it here, then we must disable the refresh button, so we don't do it here
2695 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2696 if (user_data->dlg.save_voice_as_w != NULL) {
2697 /* There's already a Save voice info dialog box; reactivate it. */
2698 reactivate_window(user_data->dlg.save_voice_as_w);
2702 /* XXX - use file_selection from dlg_utils instead! */
2703 user_data->dlg.save_voice_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2704 GTK_WINDOW(user_data->dlg.notebook),
2705 GTK_FILE_CHOOSER_ACTION_SAVE,
2706 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2707 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2709 #if GTK_CHECK_VERSION(2,8,0)
2710 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
2712 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_voice_as_w),GTK_WINDOW(user_data->dlg.window));
2714 /* Container for each row of widgets */
2715 vertb = gtk_vbox_new(FALSE, 0);
2716 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2717 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2718 gtk_widget_show (vertb);
2720 table1 = gtk_table_new (2, 4, FALSE);
2721 gtk_widget_show (table1);
2722 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2723 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2724 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2726 /*label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2727 gtk_widget_show (label_format);
2728 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2729 (GtkAttachOptions) (GTK_FILL),
2730 (GtkAttachOptions) (0), 0, 0);*/
2732 label_format = gtk_label_new ("Format: ");
2733 gtk_widget_show (label_format);
2734 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2735 (GtkAttachOptions) (GTK_FILL),
2736 (GtkAttachOptions) (0), 0, 0);
2738 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2740 raw_rb = gtk_radio_button_new_with_label (format_group, ".raw");
2741 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (raw_rb));
2742 gtk_widget_show (raw_rb);
2743 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2744 (GtkAttachOptions) (GTK_FILL),
2745 (GtkAttachOptions) (0), 0, 0);
2748 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2749 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2750 gtk_widget_show (au_rb);
2751 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2752 (GtkAttachOptions) (GTK_FILL),
2753 (GtkAttachOptions) (0), 0, 0);
2756 /* we support .au - ulaw*/
2757 wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2758 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (wav_rb));
2759 gtk_widget_show (wav_rb);
2760 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2761 (GtkAttachOptions) (GTK_FILL),
2762 (GtkAttachOptions) (0), 0, 0);
2764 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2765 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (sw_rb));
2766 gtk_widget_show (sw_rb);
2767 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2768 (GtkAttachOptions) (GTK_FILL),
2769 (GtkAttachOptions) (0), 0, 0);
2770 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2771 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2772 gtk_widget_show (au_rb);
2773 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2774 (GtkAttachOptions) (GTK_FILL),
2775 (GtkAttachOptions) (0), 0, 0);
2778 channels_label = gtk_label_new ("Channels: ");
2779 gtk_widget_show (channels_label);
2780 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2781 (GtkAttachOptions) (GTK_FILL),
2782 (GtkAttachOptions) (0), 0, 0);
2783 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2785 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2786 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2787 gtk_widget_show (forward_rb);
2788 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2789 (GtkAttachOptions) (GTK_FILL),
2790 (GtkAttachOptions) (0), 0, 0);
2792 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed ");
2793 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2794 gtk_widget_show (reversed_rb);
2795 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2796 (GtkAttachOptions) (GTK_FILL),
2797 (GtkAttachOptions) (0), 0, 0);
2799 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2800 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2801 gtk_widget_show (both_rb);
2802 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2803 (GtkAttachOptions) (GTK_FILL),
2804 (GtkAttachOptions) (0), 0, 0);
2807 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
2810 /* if one direction is nok we don't allow saving
2811 XXX this is not ok since the user can click the refresh button and cause changes
2812 but we can not update this window. So we move all the decision on the time the ok
2815 if (user_data->forward.saved == FALSE) {
2816 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2817 gtk_widget_set_sensitive(forward_rb, FALSE);
2818 gtk_widget_set_sensitive(both_rb, FALSE);
2820 else if (user_data->reversed.saved == FALSE) {
2821 gtk_widget_set_sensitive(reversed_rb, FALSE);
2822 gtk_widget_set_sensitive(both_rb, FALSE);
2826 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
2827 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2828 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
2829 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2830 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2831 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2832 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2833 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2835 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2836 G_CALLBACK(window_delete_event_cb), NULL);
2837 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2838 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2840 gtk_widget_show(user_data->dlg.save_voice_as_w);
2841 window_present(user_data->dlg.save_voice_as_w);
2843 /* "Run" the GtkFileChooserDialog. */
2844 /* Upon exit: If "Accept" run the OK callback. */
2845 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2846 /* Destroy the window. */
2847 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2848 /* return with a TRUE status so that the dialog window will be destroyed. */
2849 /* Trying to re-run the dialog after popping up an alert box will not work */
2850 /* since the user will not be able to dismiss the alert box. */
2851 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2852 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2854 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2855 /* GtkFileChooserDialog. */
2856 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
2857 if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
2858 break; /* we're done */
2861 window_destroy(user_data->dlg.save_voice_as_w);
2866 /****************************************************************************/
2867 /* when we are finished with redisection, we add the label for the statistic */
2868 static void draw_stat(user_data_t *user_data)
2870 gchar label_max[300];
2871 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
2872 - user_data->forward.statinfo.start_seq_nr + 1;
2873 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
2874 - user_data->reversed.statinfo.start_seq_nr + 1;
2875 guint32 f_total_nr = user_data->forward.statinfo.total_nr;
2876 guint32 r_total_nr = user_data->reversed.statinfo.total_nr;
2877 gint32 f_lost = f_expected - f_total_nr;
2878 gint32 r_lost = r_expected - r_total_nr;
2879 double f_sumt = user_data->forward.statinfo.sumt;
2880 double f_sumTS = user_data->forward.statinfo.sumTS;
2881 double f_sumt2 = user_data->forward.statinfo.sumt2;
2882 double f_sumtTS = user_data->forward.statinfo.sumtTS;
2884 double r_sumt = user_data->reversed.statinfo.sumt;
2885 double r_sumTS = user_data->reversed.statinfo.sumTS;
2886 double r_sumt2 = user_data->reversed.statinfo.sumt2;
2887 double r_sumtTS = user_data->reversed.statinfo.sumtTS;
2888 double f_perc, r_perc;
2889 double f_clock_drift = 1.0;
2890 double r_clock_drift = 1.0;
2891 double f_duration = user_data->forward.statinfo.time - user_data->forward.statinfo.start_time;
2892 double r_duration = user_data->reversed.statinfo.time - user_data->reversed.statinfo.start_time;
2893 guint32 f_clock_rate = user_data->forward.statinfo.clock_rate;
2894 guint32 r_clock_rate = user_data->reversed.statinfo.clock_rate;
2896 if (f_clock_rate == 0){
2900 if (r_clock_rate == 0){
2905 f_perc = (double)(f_lost*100)/(double)f_expected;
2910 r_perc = (double)(r_lost*100)/(double)r_expected;
2915 if ((f_total_nr >0)&&(f_sumt2 > 0)){
2916 f_clock_drift = (f_total_nr * f_sumtTS - f_sumt * f_sumTS) / (f_total_nr * f_sumt2 - f_sumt * f_sumt);
2918 if ((r_total_nr >0)&&(r_sumt2 > 0)){
2919 r_clock_drift = (r_total_nr * r_sumtTS - r_sumt * r_sumTS) / (r_total_nr * r_sumt2 - r_sumt * r_sumt);
2921 g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
2922 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
2923 "Max skew = %.2f ms.\n"
2924 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2925 " Sequence errors = %u \n"
2926 "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
2927 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
2928 user_data->forward.statinfo.max_jitter,user_data->forward.statinfo.mean_jitter,
2929 user_data->forward.statinfo.max_skew,
2930 f_expected, f_expected, f_lost, f_perc,
2931 user_data->forward.statinfo.sequence,
2932 f_duration/1000,f_duration*(f_clock_drift-1.0),f_clock_drift*f_clock_rate,100.0*(f_clock_drift-1.0));
2934 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2935 gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_fwd),TRUE);
2937 g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
2938 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
2939 "Max skew = %.2f ms.\n"
2940 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2941 " Sequence errors = %u \n"
2942 "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
2943 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
2944 user_data->reversed.statinfo.max_jitter,user_data->reversed.statinfo.mean_jitter,
2945 user_data->reversed.statinfo.max_skew,
2946 r_expected, r_expected, r_lost, r_perc,
2947 user_data->reversed.statinfo.sequence,
2948 r_duration/1000,r_duration*(r_clock_drift-1.0),r_clock_drift*r_clock_rate,100.0*(r_clock_drift-1.0));
2950 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2951 gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_rev),TRUE);
2958 /****************************************************************************/
2959 /* append a line to list */
2960 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
2961 double delta, double jitter,double skew, double bandwidth, gchar *status, gboolean marker,
2962 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
2964 GtkListStore *list_store;
2966 if (strcmp(status, OK_TEXT) != 0) {
2967 user_data->dlg.number_of_nok++;
2970 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
2972 /* Creates a new row at position. iter will be changed to point to this new row.
2973 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
2974 * The row will be filled with the values given to this function.
2976 * should generally be preferred when inserting rows in a sorted list store.
2978 #if GTK_CHECK_VERSION(2,6,0)
2979 gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
2981 gtk_list_store_append (list_store, &user_data->dlg.iter);
2982 gtk_list_store_set (list_store, &user_data->dlg.iter,
2984 PACKET_COLUMN, number,
2985 SEQUENCE_COLUMN, seq_num,
2986 TIMESTAMP_COLUMN, timestamp,
2987 DELTA_COLUMN, delta,
2988 JITTER_COLUMN, jitter,
2990 IPBW_COLUMN, bandwidth,
2991 MARKER_COLUMN, marker,
2992 STATUS_COLUMN, (char *)status,
2993 DATE_COLUMN, (char *)timeStr,
2994 LENGTH_COLUMN, pkt_len,
2995 FOREGROUND_COLOR_COL, NULL,
2996 BACKGROUND_COLOR_COL, (char *)color_str,
2999 if(flags & STAT_FLAG_FIRST){
3000 /* Set first row as active */
3001 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
3005 /****************************************************************************
3006 * Functions needed to present values from the list
3010 /* Present boolean value */
3012 rtp_boolean_data_func (GtkTreeViewColumn *column _U_,
3013 GtkCellRenderer *renderer,
3014 GtkTreeModel *model,
3020 /* the col to get data from is in userdata */
3021 gint bool_col = GPOINTER_TO_INT(user_data);
3023 gtk_tree_model_get(model, iter, bool_col, &bool_val, -1);
3027 g_strlcpy(buf, bool_val ? "SET" : "", sizeof(buf));
3030 g_assert_not_reached();
3033 g_object_set(renderer, "text", buf, NULL);
3038 GtkWidget* create_list(user_data_t* user_data)
3041 GtkListStore *list_store;
3043 GtkTreeViewColumn *column;
3044 GtkCellRenderer *renderer;
3045 GtkTreeSortable *sortable;
3046 GtkTreeView *list_view;
3047 GtkTreeSelection *selection;
3049 /* Create the store */
3050 list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX*/
3051 G_TYPE_UINT, /* Packet */
3052 G_TYPE_UINT, /* Sequence */
3053 G_TYPE_UINT, /* Time stamp */
3054 G_TYPE_FLOAT, /* Delta(ms) */
3055 G_TYPE_FLOAT, /* Filtered Jitter(ms) */
3056 G_TYPE_FLOAT, /* Skew(ms) */
3057 G_TYPE_FLOAT, /* IP BW(kbps) */
3058 G_TYPE_BOOLEAN, /* Marker */
3059 G_TYPE_STRING, /* Status */
3060 G_TYPE_STRING, /* Date */
3061 G_TYPE_UINT, /* Length */
3062 G_TYPE_STRING, /* Foreground color */
3063 G_TYPE_STRING); /* Background color */
3066 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
3068 list_view = GTK_TREE_VIEW(list);
3069 sortable = GTK_TREE_SORTABLE(list_store);
3071 #if GTK_CHECK_VERSION(2,6,0)
3072 /* Speed up the list display */
3073 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
3076 /* Setup the sortable columns */
3077 gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
3078 gtk_tree_view_set_headers_clickable(list_view, FALSE);
3080 /* The view now holds a reference. We can get rid of our own reference */
3081 g_object_unref (G_OBJECT (list_store));
3084 * Create the first column packet, associating the "text" attribute of the
3085 * cell_renderer to the first column of the model
3087 renderer = gtk_cell_renderer_text_new ();
3088 column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
3089 "text", PACKET_COLUMN,
3090 "foreground", FOREGROUND_COLOR_COL,
3091 "background", BACKGROUND_COLOR_COL,
3093 gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
3094 gtk_tree_view_column_set_resizable(column, TRUE);
3095 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3096 gtk_tree_view_column_set_min_width(column, 55);
3098 /* Add the column to the view. */
3099 gtk_tree_view_append_column (list_view, column);
3102 renderer = gtk_cell_renderer_text_new ();
3103 column = gtk_tree_view_column_new_with_attributes ("Sequence", renderer,
3104 "text", SEQUENCE_COLUMN,
3105 "foreground", FOREGROUND_COLOR_COL,
3106 "background", BACKGROUND_COLOR_COL,
3108 gtk_tree_view_column_set_sort_column_id(column, SEQUENCE_COLUMN);
3109 gtk_tree_view_column_set_resizable(column, TRUE);
3110 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3111 gtk_tree_view_column_set_min_width(column, 75);
3112 gtk_tree_view_append_column (list_view, column);
3115 Currently not visible
3117 renderer = gtk_cell_renderer_text_new ();
3118 column = gtk_tree_view_column_new_with_attributes ("Time stamp", renderer,
3119 "text", TIMESTAMP_COLUMN,
3120 "foreground", FOREGROUND_COLOR_COL,
3121 "background", BACKGROUND_COLOR_COL,
3123 gtk_tree_view_column_set_sort_column_id(column, TIMESTAMP_COLUMN);
3124 gtk_tree_view_column_set_resizable(column, TRUE);
3125 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3126 gtk_tree_view_column_set_min_width(column, 75);
3127 gtk_tree_view_append_column (list_view, column);
3130 renderer = gtk_cell_renderer_text_new ();
3131 column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
3132 "text", DELTA_COLUMN,
3133 "foreground", FOREGROUND_COLOR_COL,
3134 "background", BACKGROUND_COLOR_COL,
3137 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3138 GINT_TO_POINTER(DELTA_COLUMN), NULL);
3140 gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
3141 gtk_tree_view_column_set_resizable(column, TRUE);
3142 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3143 gtk_tree_view_column_set_min_width(column, 75);
3144 gtk_tree_view_append_column (list_view, column);
3147 renderer = gtk_cell_renderer_text_new ();
3148 column = gtk_tree_view_column_new_with_attributes ("Filtered Jitter(ms)", renderer,
3149 "text", JITTER_COLUMN,
3150 "foreground", FOREGROUND_COLOR_COL,
3151 "background", BACKGROUND_COLOR_COL,
3154 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3155 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3157 gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3158 gtk_tree_view_column_set_resizable(column, TRUE);
3159 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3160 gtk_tree_view_column_set_min_width(column, 110);
3161 gtk_tree_view_append_column (list_view, column);
3164 renderer = gtk_cell_renderer_text_new ();
3165 column = gtk_tree_view_column_new_with_attributes ("Skew(ms)", renderer,
3166 "text", SKEW_COLUMN,
3167 "foreground", FOREGROUND_COLOR_COL,
3168 "background", BACKGROUND_COLOR_COL,
3171 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3172 GINT_TO_POINTER(SKEW_COLUMN), NULL);
3174 gtk_tree_view_column_set_sort_column_id(column, SKEW_COLUMN);
3175 gtk_tree_view_column_set_resizable(column, TRUE);
3176 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3177 gtk_tree_view_column_set_min_width(column, 110);
3178 gtk_tree_view_append_column (list_view, column);
3181 renderer = gtk_cell_renderer_text_new ();
3182 column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
3183 "text", IPBW_COLUMN,
3184 "foreground", FOREGROUND_COLOR_COL,
3185 "background", BACKGROUND_COLOR_COL,
3188 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3189 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3191 gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3192 gtk_tree_view_column_set_resizable(column, TRUE);
3193 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3194 gtk_tree_view_column_set_min_width(column, 80);
3195 gtk_tree_view_append_column (list_view, column);
3198 renderer = gtk_cell_renderer_text_new ();
3199 column = gtk_tree_view_column_new_with_attributes ("Marker", renderer,
3200 "text", MARKER_COLUMN,
3201 "foreground", FOREGROUND_COLOR_COL,
3202 "background", BACKGROUND_COLOR_COL,
3205 gtk_tree_view_column_set_cell_data_func(column, renderer, rtp_boolean_data_func,
3206 GINT_TO_POINTER(MARKER_COLUMN), NULL);
3208 gtk_tree_view_column_set_sort_column_id(column, MARKER_COLUMN);
3209 gtk_tree_view_column_set_resizable(column, TRUE);
3210 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3211 gtk_tree_view_column_set_min_width(column, 60);
3212 gtk_tree_view_append_column (list_view, column);
3215 renderer = gtk_cell_renderer_text_new ();
3216 column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3217 "text", STATUS_COLUMN,
3218 "foreground", FOREGROUND_COLOR_COL,
3219 "background", BACKGROUND_COLOR_COL,
3221 gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3222 gtk_tree_view_column_set_resizable(column, TRUE);
3223 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3224 gtk_tree_view_column_set_min_width(column, 100);
3225 gtk_tree_view_append_column (list_view, column);
3227 /* Now enable the sorting of each column */
3228 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3229 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3231 /* Setup the selection handler */
3232 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3233 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3235 g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3236 G_CALLBACK (on_list_select_row),
3241 /****************************************************************************/
3242 /* Create the dialog box with all widgets */
3243 static void create_rtp_dialog(user_data_t* user_data)
3245 GtkWidget *window = NULL;
3246 GtkWidget *list_fwd;
3247 GtkWidget *list_rev;
3248 GtkWidget *label_stats_fwd;
3249 GtkWidget *label_stats_rev;
3250 GtkWidget *notebook;
3252 GtkWidget *main_vb, *page, *page_r;
3254 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3255 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3256 #ifdef HAVE_LIBPORTAUDIO
3257 GtkWidget *player_bt = NULL;
3258 #endif /* HAVE_LIBPORTAUDIO */
3259 GtkWidget *graph_bt;
3260 gchar label_forward[150];
3261 gchar label_forward_tree[150];
3262 gchar label_reverse[150];
3264 gchar str_ip_src[16];
3265 gchar str_ip_dst[16];
3267 window = dlg_window_new("Wireshark: RTP Stream Analysis"); /* transient_for top_level */
3268 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3270 /* Container for each row of widgets */
3271 main_vb = gtk_vbox_new(FALSE, 2);
3272 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3273 gtk_container_add(GTK_CONTAINER(window), main_vb);
3274 gtk_widget_show(main_vb);
3277 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), sizeof(str_ip_src));
3278 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), sizeof(str_ip_dst));
3280 g_snprintf(label_forward, sizeof(label_forward),
3281 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3282 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3284 g_snprintf(label_forward_tree, sizeof(label_forward_tree),
3285 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3286 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3289 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), sizeof(str_ip_src));
3290 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), sizeof(str_ip_dst));
3292 g_snprintf(label_reverse, sizeof(label_reverse),
3293 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3294 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3296 /* Start a notebook for flipping between sets of changes */
3297 notebook = gtk_notebook_new();
3298 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3299 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3301 user_data->dlg.notebook_signal_id =
3302 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3304 /* page for forward connection */
3305 page = gtk_vbox_new(FALSE, 8);
3306 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3308 /* direction label */
3309 label = gtk_label_new(label_forward);
3310 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3312 /* place for some statistics */
3313 label_stats_fwd = gtk_label_new("\n");
3314 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3316 /* scrolled window */
3317 scrolled_window = scrolled_window_new(NULL, NULL);
3320 list_fwd = create_list(user_data);
3321 gtk_widget_show(list_fwd);
3322 gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3323 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3324 gtk_widget_show(scrolled_window);
3327 label = gtk_label_new(" Forward Direction ");
3328 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3330 /* same page for reversed connection */
3331 page_r = gtk_vbox_new(FALSE, 8);
3332 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3333 label = gtk_label_new(label_reverse);
3334 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3335 label_stats_rev = gtk_label_new("\n");
3336 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3338 scrolled_window_r = scrolled_window_new(NULL, NULL);
3340 list_rev = create_list(user_data);
3341 gtk_widget_show(list_rev);
3342 gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3343 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3344 gtk_widget_show(scrolled_window_r);
3346 label = gtk_label_new(" Reversed Direction ");
3347 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3349 /* page for help&about or future */
3351 page_help = gtk_hbox_new(FALSE, 5);
3352 label = gtk_label_new(" Future ");
3353 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3354 frame = gtk_frame_new("");
3355 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3356 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3357 gtk_container_add(GTK_CONTAINER(frame), text);
3358 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3359 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3362 /* show all notebooks */
3363 gtk_widget_show_all(notebook);
3366 box4 = gtk_hbutton_box_new();
3367 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3368 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3369 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3370 gtk_box_set_spacing(GTK_BOX (box4), 0);
3371 gtk_widget_show(box4);
3373 voice_bt = gtk_button_new_with_label("Save payload...");
3374 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3375 gtk_widget_show(voice_bt);
3376 g_signal_connect(voice_bt, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
3378 csv_bt = gtk_button_new_with_label("Save as CSV...");
3379 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3380 gtk_widget_show(csv_bt);
3381 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3383 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3384 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3385 gtk_widget_show(refresh_bt);
3386 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3388 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3389 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3390 gtk_widget_show(goto_bt);
3391 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked_lst), user_data);
3393 graph_bt = gtk_button_new_with_label("Graph");
3394 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3395 gtk_widget_show(graph_bt);
3396 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3398 #ifdef HAVE_LIBPORTAUDIO
3399 player_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_AUDIO_PLAYER);
3400 gtk_container_add(GTK_CONTAINER(box4), player_bt);
3401 gtk_widget_show(player_bt);
3402 g_signal_connect(player_bt, "clicked", G_CALLBACK(on_player_bt_clicked), NULL);
3403 /*gtk_tooltips_set_tip (tooltips, player_bt, "Launch the RTP player to listen the audio stream", NULL);*/
3404 #endif /* HAVE_LIBPORTAUDIO */
3406 next_bt = gtk_button_new_with_label("Next non-Ok");
3407 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3408 gtk_widget_show(next_bt);
3409 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked_list), user_data);
3411 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3412 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3413 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3414 gtk_widget_show(close_bt);
3415 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3417 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3418 g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3420 gtk_widget_show(window);
3421 window_present(window);
3424 /* some widget references need to be saved for outside use */
3425 user_data->dlg.window = window;
3426 user_data->dlg.list_fwd = list_fwd;
3427 user_data->dlg.list_rev = list_rev;
3428 user_data->dlg.label_stats_fwd = label_stats_fwd;
3429 user_data->dlg.label_stats_rev = label_stats_rev;
3430 user_data->dlg.notebook = notebook;
3431 user_data->dlg.selected_list = list_fwd;
3432 user_data->dlg.number_of_nok = 0;
3435 * select the initial row
3437 gtk_widget_grab_focus(list_fwd);
3442 /****************************************************************************/
3443 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3444 const gchar* proto_field, guint32* p_result)
3447 proto_node *proto_sibling_node;
3448 header_field_info *hfssrc;
3451 finfo = PNODE_FINFO(ptree_node);
3453 g_assert(finfo && "Caller passed top of the protocol tree. Expected child node");
3455 if (hfinformation==(finfo->hfinfo)) {
3456 hfssrc = proto_registrar_get_byname(proto_field);
3459 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3460 ptree_node=ptree_node->next) {
3461 finfo=PNODE_FINFO(ptree_node);
3462 if (hfssrc==finfo->hfinfo) {
3463 if (hfinformation->type==FT_IPv4) {
3464 ipv4 = fvalue_get(&finfo->value);
3465 *p_result = ipv4_get_net_order_addr(ipv4);
3468 *p_result = fvalue_get_uinteger(&finfo->value);
3477 proto_sibling_node = ptree_node->next;
3479 if (proto_sibling_node) {
3480 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3486 /****************************************************************************/
3487 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3488 const gchar* proto_name,
3489 const gchar* proto_field,
3492 proto_node *ptree_node;
3493 header_field_info *hfinformation;
3495 hfinformation = proto_registrar_get_byname(proto_name);
3496 if (hfinformation == NULL)
3499 ptree_node = ((proto_node *)protocol_tree)->first_child;
3503 return process_node(ptree_node, hfinformation, proto_field, p_result);
3507 /****************************************************************************/
3509 address *ip_src_fwd,
3510 guint16 port_src_fwd,
3511 address *ip_dst_fwd,
3512 guint16 port_dst_fwd,
3514 address *ip_src_rev,
3515 guint16 port_src_rev,
3516 address *ip_dst_rev,
3517 guint16 port_dst_rev,
3521 user_data_t *user_data;
3524 static color_t col[MAX_GRAPHS] = {
3525 {0, 0x0000, 0x0000, 0x0000},
3526 {0, 0xffff, 0x0000, 0x0000},
3527 {0, 0x0000, 0xffff, 0x0000},
3528 {0, 0x0000, 0x0000, 0xffff}
3533 user_data = g_malloc(sizeof(user_data_t));
3535 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3536 user_data->port_src_fwd = port_src_fwd;
3537 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3538 user_data->port_dst_fwd = port_dst_fwd;
3539 user_data->ssrc_fwd = ssrc_fwd;
3540 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3541 user_data->port_src_rev = port_src_rev;
3542 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3543 user_data->port_dst_rev = port_dst_rev;
3544 user_data->ssrc_rev = ssrc_rev;
3547 /* file names for storing sound data */
3548 /*XXX: check for errors*/
3549 fd = create_tempfile(&tempname, "wireshark_rtp_f");
3550 user_data->f_tempname = g_strdup(tempname);
3552 fd = create_tempfile(&tempname, "wireshark_rtp_r");
3553 user_data->r_tempname = g_strdup(tempname);
3555 user_data->forward.saveinfo.fp = NULL;
3556 user_data->reversed.saveinfo.fp = NULL;
3557 user_data->dlg.save_voice_as_w = NULL;
3558 user_data->dlg.save_csv_as_w = NULL;
3559 user_data->dlg.dialog_graph.window = NULL;
3561 /* init dialog_graph */
3562 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3563 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3564 user_data->dlg.dialog_graph.draw_area=NULL;
3565 user_data->dlg.dialog_graph.pixmap=NULL;
3566 user_data->dlg.dialog_graph.scrollbar=NULL;
3567 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3568 user_data->dlg.dialog_graph.pixmap_width=500;
3569 user_data->dlg.dialog_graph.pixmap_height=200;
3570 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3571 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3572 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3573 user_data->dlg.dialog_graph.max_interval=0;
3574 user_data->dlg.dialog_graph.num_items=0;
3575 user_data->dlg.dialog_graph.start_time = -1;
3577 for(i=0;i<MAX_GRAPHS;i++){
3578 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3579 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3580 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3581 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3582 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3583 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3584 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3585 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3588 /* create the dialog box */
3589 create_rtp_dialog(user_data);
3591 /* proceed as if the Refresh button would have been pressed */
3592 on_refresh_bt_clicked(NULL, user_data);
3595 /****************************************************************************/
3596 /* entry point from main menu */
3597 static void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3600 guint16 port_src_fwd;
3602 guint16 port_dst_fwd;
3603 guint32 ssrc_fwd = 0;
3605 guint16 port_src_rev;
3607 guint16 port_dst_rev;
3608 guint32 ssrc_rev = 0;
3609 unsigned int version_fwd;
3611 gchar filter_text[256];
3617 gboolean frame_matched;
3619 GList *strinfo_list;
3620 GList *filtered_list = NULL;
3621 rtp_stream_info_t *strinfo;
3624 /* Try to compile the filter. */
3625 g_strlcpy(filter_text,"rtp && rtp.version && rtp.ssrc && (ip || ipv6)",sizeof(filter_text));
3626 if (!dfilter_compile(filter_text, &sfcode)) {
3627 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3630 /* we load the current file into cf variable */
3632 fdata = cf->current_frame;
3634 /* we are on the selected frame now */
3636 return; /* if we exit here it's an error */
3638 /* dissect the current frame */
3639 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3640 cf->pd, fdata->cap_len, &err, &err_info)) {
3641 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3642 cf_read_error_message(err, err_info), cf->filename);
3645 epan_dissect_init(&edt, TRUE, FALSE);
3646 epan_dissect_prime_dfilter(&edt, sfcode);
3647 epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3648 frame_matched = dfilter_apply_edt(sfcode, &edt);
3650 /* if it is not an rtp frame, show the rtpstream dialog */
3651 frame_matched = dfilter_apply_edt(sfcode, &edt);
3652 if (frame_matched != 1) {
3653 epan_dissect_cleanup(&edt);
3654 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3655 "You didn't choose a RTP packet!");
3659 /* ok, it is a RTP frame, so let's get the ip and port values */
3660 COPY_ADDRESS(&(ip_src_fwd), &(edt.pi.src))
3661 COPY_ADDRESS(&(ip_dst_fwd), &(edt.pi.dst))
3662 port_src_fwd = edt.pi.srcport;
3663 port_dst_fwd = edt.pi.destport;
3665 /* assume the inverse ip/port combination for the reverse direction */
3666 COPY_ADDRESS(&(ip_src_rev), &(edt.pi.dst))
3667 COPY_ADDRESS(&(ip_dst_rev), &(edt.pi.src))
3668 port_src_rev = edt.pi.destport;
3669 port_dst_rev = edt.pi.srcport;
3671 /* check if it is RTP Version 2 */
3672 if (!get_int_value_from_proto_tree(edt.tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3673 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3674 "RTP Version != 2 isn't supported!");
3678 /* now we need the SSRC value of the current frame */
3679 if (!get_int_value_from_proto_tree(edt.tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3680 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3681 "SSRC value couldn't be found!");
3685 /* Scan for rtpstream */
3687 /* search for reversed direction in the global rtp streams list */
3689 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3690 while (strinfo_list)
3692 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3693 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3694 && strinfo->src_port==port_src_fwd
3695 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3696 && strinfo->dest_port==port_dst_fwd)
3698 filtered_list = g_list_prepend(filtered_list, strinfo);
3701 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3702 && strinfo->src_port==port_src_rev
3703 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3704 && strinfo->dest_port==port_dst_rev)
3707 filtered_list = g_list_append(filtered_list, strinfo);
3709 ssrc_rev = strinfo->ssrc;
3712 strinfo_list = g_list_next(strinfo_list);
3715 /* if more than one reverse streams found, we let the user choose the right one */
3717 rtpstream_dlg_show(filtered_list);
3736 /****************************************************************************/
3738 rtp_analysis_init(const char *dummy _U_,void* userdata _U_)
3740 rtp_analysis_cb(NULL, NULL);
3743 /****************************************************************************/
3745 register_tap_listener_rtp_analysis(void)
3747 register_stat_cmd_arg("rtp", rtp_analysis_init,NULL);
3749 register_stat_menu_item("_RTP/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3750 rtp_analysis_cb, NULL, NULL, NULL);