2 * RTP analysis addition for Wireshark
6 * Copyright 2003, Alcatel Business Systems
7 * By Lars Ruoff <lars.ruoff@gmx.net>
10 * Copyright 2003, Iskratel, Ltd, Kranj
11 * By Miha Jemec <m.jemec@iskratel.si>
13 * Graph. Copyright 2004, Verso Technology
14 * By Alejandro Vaquero <alejandro.vaquero@verso.com>
15 * Based on io_stat.c by Ronnie Sahlberg
17 * Wireshark - Network traffic analyzer
18 * By Gerald Combs <gerald@wireshark.org>
19 * Copyright 1998 Gerald Combs
21 * This program is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU General Public License
23 * as published by the Free Software Foundation; either version 2
24 * of the License, or (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
53 #include <epan/epan_dissect.h>
54 #include <epan/filesystem.h>
55 #include <epan/pint.h>
57 #include <epan/dissectors/packet-rtp.h>
58 #include <epan/rtp_pt.h>
59 #include <epan/addr_resolv.h>
60 #include <epan/stat_cmd_args.h>
61 #include <epan/strutil.h>
64 #include "../register.h"
66 #include "../alert_box.h"
67 #include "../simple_dialog.h"
68 #include "../stat_menu.h"
69 #include "../progress_dlg.h"
71 #include "../tempfile.h"
72 #include <wsutil/file_util.h>
74 #include "gtk/gtkglobals.h"
75 #include "gtk/dlg_utils.h"
76 #include "gtk/file_dlg.h"
77 #include "gtk/gui_utils.h"
78 #include "gtk/gui_stat_menu.h"
80 #include "gtk/rtp_analysis.h"
81 #include "gtk/rtp_stream.h"
82 #include "gtk/rtp_stream_dlg.h"
98 N_COLUMN /* The number of columns */
100 /****************************************************************************/
103 #define NUM_GRAPH_ITEMS 100000
104 #define MAX_YSCALE 16
105 #define AUTO_MAX_YSCALE 0
107 #define GRAPH_FWD_JITTER 0
108 #define GRAPH_FWD_DIFF 1
109 #define GRAPH_REV_JITTER 2
110 #define GRAPH_REV_DIFF 3
111 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
113 #define MAX_PIXELS_PER_TICK 4
114 #define DEFAULT_PIXELS_PER_TICK 1
115 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
116 static const char *graph_descr[4] = {"Fwd Jitter", "Fwd Difference", "Rvr Jitter", "Rvr Difference"};
118 #define MAX_TICK_VALUES 5
119 #define DEFAULT_TICK_VALUE 1
120 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
121 typedef struct _dialog_graph_graph_item_t {
124 } dialog_graph_graph_item_t;
126 typedef struct _dialog_graph_graph_t {
127 struct _user_data_t *ud;
128 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
131 GtkWidget *display_button;
136 } dialog_graph_graph_t;
139 typedef struct _dialog_graph_t {
140 gboolean needs_redraw;
141 gint32 interval; /* measurement interval in ms */
142 guint32 last_interval;
143 guint32 max_interval; /* XXX max_interval and num_items are redundant */
145 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
147 GtkWidget *draw_area;
149 GtkAdjustment *scrollbar_adjustment;
150 GtkWidget *scrollbar;
158 typedef struct _dialog_data_t {
163 GtkWidget *label_stats_fwd;
164 GtkWidget *label_stats_rev;
165 GtkWidget *selected_list;
167 GtkTreeSelection *selected_list_sel;
168 gint selected_list_row;
170 GtkWidget *save_voice_as_w;
171 GtkWidget *save_csv_as_w;
172 gint notebook_signal_id;
173 dialog_graph_t dialog_graph;
174 #ifdef USE_CONVERSATION_GRAPH
175 GtkWidget *graph_window;
179 #define OK_TEXT "[ Ok ]"
181 /* type of error when saving voice in a file didn't succeed */
184 TAP_RTP_WRONG_LENGTH,
185 TAP_RTP_PADDING_ERROR,
187 TAP_RTP_FILE_OPEN_ERROR,
191 typedef struct _tap_rtp_save_info_t {
194 error_type_t error_type;
196 } tap_rtp_save_info_t;
199 /* structure that holds the information about the forward and reversed direction */
200 struct _info_direction {
201 tap_rtp_stat_t statinfo;
202 tap_rtp_save_info_t saveinfo;
205 #define TMPNAMSIZE 100
207 #define SILENCE_PCMU (guint8)0xFF
208 #define SILENCE_PCMA (guint8)0x55
210 /* structure that holds general information about the connection
211 * and structures for both directions */
212 typedef struct _user_data_t {
213 /* tap associated data*/
215 guint16 port_src_fwd;
217 guint16 port_dst_fwd;
220 guint16 port_src_rev;
222 guint16 port_dst_rev;
225 struct _info_direction forward;
226 struct _info_direction reversed;
228 char f_tempname[TMPNAMSIZE];
229 char r_tempname[TMPNAMSIZE];
231 /* dialog associated data */
234 #ifdef USE_CONVERSATION_GRAPH
235 time_series_t series_fwd;
236 time_series_t series_rev;
242 static const gchar *titles[10] = {
255 #define SAVE_FORWARD_DIRECTION_MASK 0x01
256 #define SAVE_REVERSE_DIRECTION_MASK 0x02
257 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
259 #define SAVE_NONE_FORMAT 0
260 #define SAVE_WAV_FORMAT 1
261 #define SAVE_AU_FORMAT 2
262 #define SAVE_SW_FORMAT 3
263 #define SAVE_RAW_FORMAT 4
266 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data);
267 /****************************************************************************/
268 static void enable_graph(dialog_graph_graph_t *dgg)
275 static void dialog_graph_reset(user_data_t* user_data);
279 /****************************************************************************/
282 /****************************************************************************/
283 /* when there is a [re]reading of packet's */
285 rtp_reset(void *user_data_arg)
287 user_data_t *user_data = user_data_arg;
288 user_data->forward.statinfo.first_packet = TRUE;
289 user_data->reversed.statinfo.first_packet = TRUE;
290 user_data->forward.statinfo.max_delta = 0;
291 user_data->reversed.statinfo.max_delta = 0;
292 user_data->forward.statinfo.max_jitter = 0;
293 user_data->reversed.statinfo.max_jitter = 0;
294 user_data->forward.statinfo.mean_jitter = 0;
295 user_data->reversed.statinfo.mean_jitter = 0;
296 user_data->forward.statinfo.delta = 0;
297 user_data->reversed.statinfo.delta = 0;
298 user_data->forward.statinfo.diff = 0;
299 user_data->reversed.statinfo.diff = 0;
300 user_data->forward.statinfo.jitter = 0;
301 user_data->reversed.statinfo.jitter = 0;
302 user_data->forward.statinfo.bandwidth = 0;
303 user_data->reversed.statinfo.bandwidth = 0;
304 user_data->forward.statinfo.total_bytes = 0;
305 user_data->reversed.statinfo.total_bytes = 0;
306 user_data->forward.statinfo.bw_start_index = 0;
307 user_data->reversed.statinfo.bw_start_index = 0;
308 user_data->forward.statinfo.bw_index = 0;
309 user_data->reversed.statinfo.bw_index = 0;
310 user_data->forward.statinfo.timestamp = 0;
311 user_data->reversed.statinfo.timestamp = 0;
312 user_data->forward.statinfo.max_nr = 0;
313 user_data->reversed.statinfo.max_nr = 0;
314 user_data->forward.statinfo.total_nr = 0;
315 user_data->reversed.statinfo.total_nr = 0;
316 user_data->forward.statinfo.sequence = 0;
317 user_data->reversed.statinfo.sequence = 0;
318 user_data->forward.statinfo.start_seq_nr = 0;
319 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
320 user_data->forward.statinfo.stop_seq_nr = 0;
321 user_data->reversed.statinfo.stop_seq_nr = 0;
322 user_data->forward.statinfo.cycles = 0;
323 user_data->reversed.statinfo.cycles = 0;
324 user_data->forward.statinfo.under = FALSE;
325 user_data->reversed.statinfo.under = FALSE;
326 user_data->forward.statinfo.start_time = 0;
327 user_data->reversed.statinfo.start_time = 0;
328 user_data->forward.statinfo.time = 0;
329 user_data->reversed.statinfo.time = 0;
330 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
331 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
333 user_data->forward.saveinfo.count = 0;
334 user_data->reversed.saveinfo.count = 0;
335 user_data->forward.saveinfo.saved = FALSE;
336 user_data->reversed.saveinfo.saved = FALSE;
338 /* clear the dialog box lists */
339 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
340 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
342 /* reset graph info */
343 dialog_graph_reset(user_data);
345 #ifdef USE_CONVERSATION_GRAPH
346 if (user_data->dlg.graph_window != NULL)
347 window_destroy(user_data->dlg.graph_window);
349 g_array_free(user_data->series_fwd.value_pairs, TRUE);
350 user_data->series_fwd.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
352 g_array_free(user_data->series_rev.value_pairs, TRUE);
353 user_data->series_rev.value_pairs = g_array_new(FALSE, FALSE, sizeof(value_pair_t));
356 /* XXX check for error at fclose? */
357 if (user_data->forward.saveinfo.fp != NULL)
358 fclose(user_data->forward.saveinfo.fp);
359 if (user_data->reversed.saveinfo.fp != NULL)
360 fclose(user_data->reversed.saveinfo.fp);
361 user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
362 if (user_data->forward.saveinfo.fp == NULL)
363 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
364 user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
365 if (user_data->reversed.saveinfo.fp == NULL)
366 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
370 /****************************************************************************/
371 static int rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
373 dialog_graph_graph_item_t *it;
377 /* we sometimes get called when dgg is disabled.
378 this is a bug since the tap listener should be removed first */
383 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
386 * Find which interval this is supposed to to in and store the
387 * interval index as idx
389 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
390 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
392 rtp_time = nstime_to_sec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
396 idx = (guint32)(rtp_time*1000)/dgg->ud->dlg.dialog_graph.interval;
398 /* some sanity checks */
399 if((idx<0)||(idx>=NUM_GRAPH_ITEMS)){
403 /* update num_items */
404 if((guint32)idx > dgg->ud->dlg.dialog_graph.num_items){
405 dgg->ud->dlg.dialog_graph.num_items=idx;
406 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
410 * Find the appropriate dialog_graph_graph_item_t structure
415 * Use the max value to highlight RTP problems
417 if (value > it->value) {
420 it->flags = it->flags | statinfo->flags;
425 /****************************************************************************/
426 /* here we can redraw the output */
428 static void rtp_draw(void *prs _U_)
433 /* forward declarations */
434 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num,
435 double delta, double jitter, double skew ,double bandwidth, gchar *status, gboolean marker,
436 gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
438 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
439 tap_rtp_stat_t *statinfo, packet_info *pinfo,
440 const struct _rtp_info *rtpinfo);
442 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
443 tap_rtp_stat_t *statinfo,
445 const struct _rtp_info *rtpinfo);
448 /****************************************************************************/
449 /* whenever a RTP packet is seen by the tap listener */
450 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
452 user_data_t *user_data = user_data_arg;
453 const struct _rtp_info *rtpinfo = rtpinfo_arg;
454 #ifdef USE_CONVERSATION_GRAPH
457 /* we ignore packets that are not displayed */
458 if (pinfo->fd->flags.passed_dfilter == 0)
460 /* also ignore RTP Version != 2 */
461 else if (rtpinfo->info_version !=2)
463 /* is it the forward direction? */
464 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
465 && CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
466 && user_data->port_src_fwd == pinfo->srcport
467 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
468 && user_data->port_dst_fwd == pinfo->destport) {
469 #ifdef USE_CONVERSATION_GRAPH
470 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
471 vp.fnumber = pinfo->fd->num;
472 g_array_append_val(user_data->series_fwd.value_pairs, vp);
474 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
475 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]), &(user_data->forward.statinfo), pinfo, (guint32)(user_data->forward.statinfo.jitter*1000000));
476 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]), &(user_data->forward.statinfo), pinfo, (guint32)(user_data->forward.statinfo.diff*1000000));
477 rtp_packet_add_info(user_data->dlg.list_fwd, user_data,
478 &(user_data->forward.statinfo), pinfo, rtpinfo);
479 rtp_packet_save_payload(&(user_data->forward.saveinfo),
480 &(user_data->forward.statinfo), pinfo, rtpinfo);
482 /* is it the reversed direction? */
483 else if (user_data->ssrc_rev == rtpinfo->info_sync_src
484 && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
485 && user_data->port_src_rev == pinfo->srcport
486 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
487 && user_data->port_dst_rev == pinfo->destport) {
488 #ifdef USE_CONVERSATION_GRAPH
489 vp.time = ((double)pinfo->fd->rel_secs + (double)pinfo->fd->rel_usecs/1000000);
490 vp.fnumber = pinfo->fd->num;
491 g_array_append_val(user_data->series_rev.value_pairs, vp);
493 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
494 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]), &(user_data->reversed.statinfo), pinfo, (guint32)(user_data->reversed.statinfo.jitter*1000000));
495 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]), &(user_data->reversed.statinfo), pinfo, (guint32)(user_data->reversed.statinfo.diff*1000000));
496 rtp_packet_add_info(user_data->dlg.list_rev, user_data,
497 &(user_data->reversed.statinfo), pinfo, rtpinfo);
498 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
499 &(user_data->reversed.statinfo), pinfo, rtpinfo);
506 Replaced by using the strings instead.
507 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
508 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
509 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
510 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
511 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
513 /****************************************************************************/
514 /* adds statistics information from the packet to the list */
515 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
516 tap_rtp_stat_t *statinfo, packet_info *pinfo,
517 const struct _rtp_info *rtpinfo)
525 then = pinfo->fd->abs_ts.secs;
526 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
527 tm_tmp = localtime(&then);
528 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
531 tm_tmp->tm_year + 1900,
537 /* Default to using black on white text if nothing below overrides it */
538 g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
540 if (statinfo->pt == PT_CN) {
541 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
542 /* color = COLOR_CN; */
543 g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
545 else if (statinfo->pt == PT_CN_OLD) {
546 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
547 /* color = COLOR_CN; */
548 g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
550 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
551 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
552 /* color = COLOR_ERROR; */
553 g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
555 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
556 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
557 /* color = COLOR_WARNING; */
558 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
560 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
561 g_snprintf(status,sizeof(status),"Incorrect timestamp");
562 /* color = COLOR_WARNING; */
563 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
565 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
566 && !(statinfo->flags & STAT_FLAG_FIRST)
567 && !(statinfo->flags & STAT_FLAG_PT_CN)
568 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
569 && !(statinfo->flags & STAT_FLAG_MARKER)) {
570 g_snprintf(status,sizeof(status),"Marker missing?");
571 /* color = COLOR_WARNING; */
572 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
575 if (statinfo->flags & STAT_FLAG_MARKER) {
576 /* color = COLOR_WARNING; */
577 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
579 g_snprintf(status,sizeof(status),OK_TEXT);
581 /* is this the first packet we got in this direction? */
582 if (statinfo->flags & STAT_FLAG_FIRST) {
583 add_to_list(list, user_data,
584 pinfo->fd->num, rtpinfo->info_seq_num,
590 rtpinfo->info_marker_set,
591 timeStr, pinfo->fd->pkt_len,
596 add_to_list(list, user_data,
597 pinfo->fd->num, rtpinfo->info_seq_num,
603 rtpinfo->info_marker_set,
604 timeStr, pinfo->fd->pkt_len,
611 #define MAX_SILENCE_TICKS 1000000
612 /****************************************************************************/
613 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
614 tap_rtp_stat_t *statinfo,
616 const struct _rtp_info *rtpinfo)
623 /* is this the first packet we got in this direction? */
624 if (statinfo->flags & STAT_FLAG_FIRST) {
625 if (saveinfo->fp == NULL) {
626 saveinfo->saved = FALSE;
627 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
630 saveinfo->saved = TRUE;
633 /* save the voice information */
634 /* if there was already an error, we quit */
635 if (saveinfo->saved == FALSE)
638 /* if the captured length and packet length aren't equal, we quit
639 * if also the RTP dissector thinks there is some information missing */
640 if ((pinfo->fd->pkt_len != pinfo->fd->cap_len) &&
641 (!rtpinfo->info_all_data_present)) {
642 saveinfo->saved = FALSE;
643 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
647 /* if padding bit is set, but the padding count is bigger
648 * then the whole RTP data - error with padding count */
649 if ( (rtpinfo->info_padding_set != FALSE) &&
650 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
651 saveinfo->saved = FALSE;
652 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
656 /* do we need to insert some silence? */
657 if ((rtpinfo->info_marker_set) &&
658 !(statinfo->flags & STAT_FLAG_FIRST) &&
659 !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) &&
660 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
661 /* the amount of silence should be the difference between
662 * the last timestamp and the current one minus x
663 * x should equal the amount of information in the last frame
664 * XXX not done yet */
665 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
666 rtpinfo->info_padding_count) && i < MAX_SILENCE_TICKS; i++) {
667 switch (statinfo->reg_pt) {
678 nchars=fwrite(&tmp, 1, 1, saveinfo->fp);
681 fflush(saveinfo->fp);
685 if (rtpinfo->info_payload_type == PT_CN
686 || rtpinfo->info_payload_type == PT_CN_OLD) {
688 /*all other payloads*/
690 if (!rtpinfo->info_all_data_present) {
691 /* Not all the data was captured. */
692 saveinfo->saved = FALSE;
693 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
697 /* we put the pointer at the beginning of the RTP
698 * payload, that is, at the beginning of the RTP data
699 * plus the offset of the payload from the beginning
701 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
702 nchars=fwrite(data, sizeof(unsigned char), (rtpinfo->info_payload_len - rtpinfo->info_padding_count), saveinfo->fp);
703 saveinfo->count+=(rtpinfo->info_payload_len - rtpinfo->info_padding_count);
705 fflush(saveinfo->fp);
706 saveinfo->saved = TRUE;
714 /****************************************************************************/
717 /****************************************************************************/
719 /****************************************************************************/
720 /* close the dialog window and remove the tap listener */
721 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data)
723 /* remove tap listener */
724 protect_thread_critical_region();
725 remove_tap_listener(user_data);
726 unprotect_thread_critical_region();
728 /* close and remove temporary files */
729 if (user_data->forward.saveinfo.fp != NULL)
730 fclose(user_data->forward.saveinfo.fp);
731 if (user_data->reversed.saveinfo.fp != NULL)
732 fclose(user_data->reversed.saveinfo.fp);
733 /*XXX: test for error **/
734 ws_remove(user_data->f_tempname);
735 ws_remove(user_data->r_tempname);
737 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
738 /* destroy save_voice_as window if open */
739 if (user_data->dlg.save_voice_as_w != NULL)
740 window_destroy(user_data->dlg.save_voice_as_w);
742 /* destroy graph window if open */
743 if (user_data->dlg.dialog_graph.window != NULL)
744 window_destroy(user_data->dlg.dialog_graph.window);
746 #ifdef USE_CONVERSATION_GRAPH
747 /* destroy graph window if open */
748 if (user_data->dlg.graph_window != NULL)
749 window_destroy(user_data->dlg.graph_window);
752 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
753 g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
759 /****************************************************************************/
760 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
761 GtkNotebookPage *page _U_,
763 user_data_t *user_data _U_)
765 user_data->dlg.selected_list =
766 (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
768 user_data->dlg.selected_list_row = 0;
771 /****************************************************************************/
773 static void on_list_select_row(GtkTreeSelection *selection,
774 user_data_t *user_data/*gpointer data */)
776 user_data->dlg.selected_list_sel = selection;
779 #ifdef USE_CONVERSATION_GRAPH
780 Note this will not work any more as clist is removed.
781 /****************************************************************************/
782 /* when the graph window gets destroyed */
783 static void on_destroy_graph(GtkWidget *win _U_, user_data_t *user_data)
785 /* note that graph window has been destroyed */
786 user_data->dlg.graph_window = NULL;
789 /****************************************************************************/
790 static void graph_selection_callback(value_pair_t vp, user_data_t *user_data)
793 GtkCList *clist = NULL;
794 if (vp.fnumber != 0) {
795 clist = GTK_CLIST(user_data->dlg.clist_fwd);
796 row = gtk_clist_find_row_from_data(clist,
797 GUINT_TO_POINTER(vp.fnumber));
799 clist = GTK_CLIST(user_data->dlg.clist_rev);
800 row = gtk_clist_find_row_from_data(clist,
801 GUINT_TO_POINTER(vp.fnumber));
804 gtk_notebook_set_current_page(GTK_NOTEBOOK(user_data->dlg.notebook),
805 (clist == GTK_CLIST(user_data->dlg.clist_fwd)) ? 0 : 1);
806 gtk_clist_select_row(clist, row, 0);
807 gtk_clist_moveto(clist, row, 0, 0.5, 0);
813 /****************************************************************************/
814 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data _U_)
820 if (user_data->dlg.graph_window != NULL) {
821 /* There's already a graph window; reactivate it. */
822 reactivate_window(user_data->dlg.graph_window);
825 list = g_list_append(list, &(user_data->series_fwd));
826 list = g_list_append(list, &(user_data->series_rev));
828 user_data->series_fwd.color.pixel = 0;
829 user_data->series_fwd.color.red = 0x80ff;
830 user_data->series_fwd.color.green = 0xe0ff;
831 user_data->series_fwd.color.blue = 0xffff;
832 user_data->series_fwd.yvalue = 0.5;
834 user_data->series_rev.color.pixel = 0;
835 user_data->series_rev.color.red = 0x60ff;
836 user_data->series_rev.color.green = 0xc0ff;
837 user_data->series_rev.color.blue = 0xffff;
838 user_data->series_rev.yvalue = -0.5;
840 g_snprintf(title1, sizeof(title1), "Forward: %s:%u to %s:%u (SSRC=0x%X)",
841 get_addr_name(&(user_data->ip_src_fwd)),
842 user_data->port_src_fwd,
843 get_addr_name(&(user_data->ip_dst_fwd)),
844 user_data->port_dst_fwd,
845 user_data->ssrc_fwd);
847 g_snprintf(title2, sizeof(title2), "Reverse: %s:%u to %s:%u (SSRC=0x%X)",
848 get_addr_name(&(user_data->ip_src_rev)),
849 user_data->port_src_rev,
850 get_addr_name(&(user_data->ip_dst_rev)),
851 user_data->port_dst_rev,
852 user_data->ssrc_rev);
854 user_data->dlg.graph_window = show_conversation_graph(list, title1, title2,
855 &graph_selection_callback, user_data);
856 g_signal_connect(user_data->dlg.graph_window, "destroy",
857 G_CALLBACK(on_destroy_graph), user_data);
859 #endif /*USE_CONVERSATION_GRAPH*/
861 /****************************************************************************/
862 static void dialog_graph_set_title(user_data_t* user_data)
865 if (!user_data->dlg.dialog_graph.window){
868 title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
869 get_addr_name(&(user_data->ip_src_fwd)),
870 user_data->port_src_fwd,
871 get_addr_name(&(user_data->ip_dst_fwd)),
872 user_data->port_dst_fwd,
873 get_addr_name(&(user_data->ip_src_rev)),
874 user_data->port_src_rev,
875 get_addr_name(&(user_data->ip_dst_rev)),
876 user_data->port_dst_rev);
878 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
884 /****************************************************************************/
885 static void dialog_graph_reset(user_data_t* user_data)
889 user_data->dlg.dialog_graph.needs_redraw=TRUE;
890 for(i=0;i<MAX_GRAPHS;i++){
891 for(j=0;j<NUM_GRAPH_ITEMS;j++){
892 dialog_graph_graph_item_t *dggi;
893 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
898 user_data->dlg.dialog_graph.last_interval=0xffffffff;
899 user_data->dlg.dialog_graph.max_interval=0;
900 user_data->dlg.dialog_graph.num_items=0;
902 /* create the color titles near the filter buttons */
903 for(i=0;i<MAX_GRAPHS;i++){
906 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
907 sizeof(user_data->dlg.dialog_graph.graph[0].title),
908 "%s: %s:%u to %s:%u (SSRC=0x%X)",
910 get_addr_name(&(user_data->ip_src_fwd)),
911 user_data->port_src_fwd,
912 get_addr_name(&(user_data->ip_dst_fwd)),
913 user_data->port_dst_fwd,
914 user_data->ssrc_fwd);
917 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
918 sizeof(user_data->dlg.dialog_graph.graph[0].title),
919 "%s: %s:%u to %s:%u (SSRC=0x%X)",
921 get_addr_name(&(user_data->ip_src_rev)),
922 user_data->port_src_rev,
923 get_addr_name(&(user_data->ip_dst_rev)),
924 user_data->port_dst_rev,
925 user_data->ssrc_rev);
929 dialog_graph_set_title(user_data);
932 /****************************************************************************/
933 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
935 dialog_graph_graph_item_t *it;
942 /****************************************************************************/
943 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
946 g_snprintf(buf, buf_len, "%ds",t/1000000);
947 } else if(t>=1000000){
948 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
950 g_snprintf(buf, buf_len, "%dms",t/1000);
952 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
954 g_snprintf(buf, buf_len, "%dus",t);
958 /****************************************************************************/
959 static void dialog_graph_draw(user_data_t* user_data)
962 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
963 gint32 current_interval;
964 guint32 left_x_border;
965 guint32 right_x_border;
966 guint32 top_y_border;
967 guint32 bottom_y_border;
969 int label_width, label_height;
970 guint32 draw_width, draw_height;
971 char label_string[15];
974 guint32 num_time_intervals;
975 guint32 max_value; /* max value of seen data */
976 guint32 max_y; /* max value of the Y scale */
978 if(!user_data->dlg.dialog_graph.needs_redraw){
981 user_data->dlg.dialog_graph.needs_redraw=FALSE;
984 * Find the length of the intervals we have data for
985 * so we know how large arrays we need to malloc()
987 num_time_intervals=user_data->dlg.dialog_graph.num_items;
988 /* if there isnt anything to do, just return */
989 if(num_time_intervals==0){
992 num_time_intervals+=1;
993 /* XXX move this check to _packet() */
994 if(num_time_intervals>NUM_GRAPH_ITEMS){
995 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
1000 * find the max value so we can autoscale the y axis
1003 for(i=0;i<MAX_GRAPHS;i++){
1006 if(!user_data->dlg.dialog_graph.graph[i].display){
1009 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
1012 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
1014 /* keep track of the max value we have encountered */
1022 * Clear out old plot
1024 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1025 user_data->dlg.dialog_graph.draw_area->style->white_gc,
1028 user_data->dlg.dialog_graph.draw_area->allocation.width,
1029 user_data->dlg.dialog_graph.draw_area->allocation.height);
1033 * Calculate the y scale we should use
1035 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1036 max_y=yscale_max[MAX_YSCALE-1];
1037 for(i=MAX_YSCALE-1;i>0;i--){
1038 if(max_value<yscale_max[i]){
1039 max_y=yscale_max[i];
1043 /* the user had specified an explicit y scale to use */
1044 max_y=user_data->dlg.dialog_graph.max_y_units;
1048 * Calculate size of borders surrounding the plot
1049 * The border on the right side needs to be adjusted depending
1050 * on the width of the text labels. For simplicity we assume that the
1051 * top y scale label will be the widest one
1053 print_time_scale_string(label_string, sizeof(label_string), max_y);
1054 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1055 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1057 right_x_border=label_width+20;
1059 bottom_y_border=label_height+20;
1063 * Calculate the size of the drawing area for the actual plot
1065 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1066 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1070 * Draw the y axis and labels
1071 * (we always draw the y scale with 11 ticks along the axis)
1073 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1074 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1076 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1077 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1083 /* first, middle and last tick are slightly longer */
1087 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1088 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1089 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1090 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1091 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1092 /* draw the labels */
1094 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1095 pango_layout_set_text(layout, label_string, -1);
1096 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1097 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1098 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1099 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1100 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1104 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1105 pango_layout_set_text(layout, label_string, -1);
1106 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1107 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1108 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1109 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1110 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1114 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1115 pango_layout_set_text(layout, label_string, -1);
1116 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1117 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1118 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1119 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1120 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1128 * if we have not specified the last_interval via the gui,
1129 * then just pick the current end of the capture so that is scrolls
1130 * nicely when doing live captures
1132 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1133 last_interval=user_data->dlg.dialog_graph.max_interval;
1135 last_interval=user_data->dlg.dialog_graph.last_interval;
1142 /* plot the x-scale */
1143 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);
1145 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1146 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1147 first_interval*=user_data->dlg.dialog_graph.interval;
1154 while(interval_delta<((last_interval-first_interval)/10)){
1155 interval_delta*=delta_multiplier;
1156 if(delta_multiplier==5){
1163 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1166 /* if pixels_per_tick is <5, only draw every 10 ticks */
1167 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1171 if(current_interval%interval_delta){
1177 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1178 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1179 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1180 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1181 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1182 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1186 if(user_data->dlg.dialog_graph.interval>=1000){
1187 g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
1188 } else if(user_data->dlg.dialog_graph.interval>=100){
1189 g_snprintf(label_string, sizeof(label_string), "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1190 } else if(user_data->dlg.dialog_graph.interval>=10){
1191 g_snprintf(label_string, sizeof(label_string), "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1193 g_snprintf(label_string, sizeof(label_string), "%d.%3ds", current_interval/1000,current_interval%1000);
1195 pango_layout_set_text(layout, label_string, -1);
1196 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1197 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1198 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1199 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1200 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1212 * Draw "x" for Sequence Errors and "m" for Marks
1214 /* Draw the labels Fwd and Rev */
1215 g_strlcpy(label_string,"<-Fwd",sizeof(label_string));
1216 pango_layout_set_text(layout, label_string, -1);
1217 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1218 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1219 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1220 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1221 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1223 g_strlcpy(label_string,"<-Rev",sizeof(label_string));
1224 pango_layout_set_text(layout, label_string, -1);
1225 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1226 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1227 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1228 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1229 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1232 /* Draw the marks */
1233 for(i=MAX_GRAPHS-1;i>=0;i--){
1235 guint32 x_pos, prev_x_pos;
1237 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1238 if (!user_data->dlg.dialog_graph.graph[i].display){
1241 /* initialize prev x/y to the low left corner of the graph */
1242 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;
1244 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1245 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;
1247 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1249 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1250 g_strlcpy(label_string,"x",sizeof(label_string));
1252 g_strlcpy(label_string,"m",sizeof(label_string));
1255 pango_layout_set_text(layout, label_string, -1);
1256 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1257 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1258 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1260 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1268 g_object_unref(G_OBJECT(layout));
1271 * Loop over all graphs and draw them
1273 for(i=MAX_GRAPHS-1;i>=0;i--){
1275 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1276 if (!user_data->dlg.dialog_graph.graph[i].display){
1279 /* initialize prev x/y to the low left corner of the graph */
1280 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;
1281 prev_y_pos=draw_height-1+top_y_border;
1283 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1285 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;
1286 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1290 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1293 /* dont need to draw anything if the segment
1294 * is entirely above the top of the graph
1296 if( (prev_y_pos==0) && (y_pos==0) ){
1303 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1304 x_pos, draw_height-1+top_y_border,
1314 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1315 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1316 user_data->dlg.dialog_graph.pixmap,
1319 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1322 /* update the scrollbar */
1323 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1324 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1325 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1326 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1327 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1329 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1331 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1332 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1333 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1337 /****************************************************************************/
1338 static void dialog_graph_redraw(user_data_t* user_data)
1340 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1341 dialog_graph_draw(user_data);
1344 /****************************************************************************/
1345 static void quit(GtkWidget *widget _U_, user_data_t *user_data)
1347 user_data->dlg.dialog_graph.window = NULL;
1350 /****************************************************************************/
1351 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1353 user_data_t *user_data;
1355 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1361 gdk_draw_pixmap(widget->window,
1362 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1363 user_data->dlg.dialog_graph.pixmap,
1364 event->area.x, event->area.y,
1365 event->area.x, event->area.y,
1366 event->area.width, event->area.height);
1371 /****************************************************************************/
1372 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1374 user_data_t *user_data;
1377 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1383 if(user_data->dlg.dialog_graph.pixmap){
1384 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1385 user_data->dlg.dialog_graph.pixmap=NULL;
1388 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1389 widget->allocation.width,
1390 widget->allocation.height,
1392 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1393 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1395 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1396 widget->style->white_gc,
1399 widget->allocation.width,
1400 widget->allocation.height);
1402 /* set up the colors and the GC structs for this pixmap */
1403 for(i=0;i<MAX_GRAPHS;i++){
1404 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1405 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1408 dialog_graph_redraw(user_data);
1412 /****************************************************************************/
1413 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1415 user_data_t *user_data=(user_data_t *)data;
1418 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1419 if(user_data->dlg.dialog_graph.last_interval==mi){
1422 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1423 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1427 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1429 dialog_graph_redraw(user_data);
1433 /****************************************************************************/
1434 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1436 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1437 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1438 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.draw_area), "user_data_t", user_data);
1440 gtk_widget_set_size_request(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1442 /* signals needed to handle backing pixmap */
1443 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1444 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1446 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1447 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1449 /* create the associated scrollbar */
1450 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1451 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1452 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1453 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1454 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1457 /****************************************************************************/
1458 static void disable_graph(dialog_graph_graph_t *dgg)
1462 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1467 /****************************************************************************/
1468 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1470 /* this graph is not active, just update display and redraw */
1471 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1473 dialog_graph_redraw(dgg->ud);
1478 cf_retap_packets(&cfile);
1479 dialog_graph_redraw(dgg->ud);
1484 /****************************************************************************/
1485 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1491 hbox=gtk_hbox_new(FALSE, 3);
1492 gtk_container_add(GTK_CONTAINER(box), hbox);
1493 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1494 gtk_widget_show(hbox);
1496 g_snprintf(str, sizeof(str), "Graph %d", num);
1497 dgg->display_button=gtk_toggle_button_new_with_label(str);
1498 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1499 gtk_widget_show(dgg->display_button);
1500 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1501 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1503 label=gtk_label_new(dgg->title);
1504 gtk_widget_show(label);
1505 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1507 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1508 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1509 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1510 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1511 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1516 /****************************************************************************/
1517 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1524 frame=gtk_frame_new("Graphs");
1525 gtk_container_add(GTK_CONTAINER(box), frame);
1526 gtk_widget_show(frame);
1528 vbox=gtk_vbox_new(FALSE, 1);
1529 gtk_container_add(GTK_CONTAINER(frame), vbox);
1530 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1531 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1532 gtk_widget_show(vbox);
1534 for(i=0;i<MAX_GRAPHS;i++){
1535 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1538 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1539 gtk_widget_show(label);
1540 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1545 /****************************************************************************/
1546 static void yscale_select(GtkWidget *item, gpointer key)
1549 user_data_t *user_data;
1551 user_data=(user_data_t *)key;
1552 val=(long)g_object_get_data(G_OBJECT(item), "yscale_max");
1554 user_data->dlg.dialog_graph.max_y_units=val;
1555 dialog_graph_redraw(user_data);
1558 /****************************************************************************/
1559 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1562 user_data_t *user_data;
1564 user_data=(user_data_t *)key;
1565 val=(long)g_object_get_data(G_OBJECT(item), "pixels_per_tick");
1566 user_data->dlg.dialog_graph.pixels_per_tick=val;
1567 dialog_graph_redraw(user_data);
1570 /****************************************************************************/
1571 static void tick_interval_select(GtkWidget *item, gpointer key)
1574 user_data_t *user_data;
1576 user_data=(user_data_t *)key;
1577 val=(long)g_object_get_data(G_OBJECT(item), "tick_interval");
1579 user_data->dlg.dialog_graph.interval=val;
1580 cf_retap_packets(&cfile);
1581 dialog_graph_redraw(user_data);
1584 /****************************************************************************/
1585 static void create_yscale_max_menu_items(user_data_t* user_data, GtkWidget *menu)
1588 GtkWidget *menu_item;
1591 for(i=0;i<MAX_YSCALE;i++){
1592 if(yscale_max[i]==AUTO_MAX_YSCALE){
1593 g_strlcpy(str,"Auto",sizeof(str));
1595 g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1597 menu_item=gtk_menu_item_new_with_label(str);
1598 g_object_set_data(G_OBJECT(menu_item), "yscale_max",
1599 GUINT_TO_POINTER(yscale_max[i]));
1600 g_signal_connect(menu_item, "activate", G_CALLBACK(yscale_select), user_data);
1601 gtk_widget_show(menu_item);
1602 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1607 /****************************************************************************/
1608 static void create_pixels_per_tick_menu_items(user_data_t* user_data, GtkWidget *menu)
1611 GtkWidget *menu_item;
1614 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1615 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1616 menu_item=gtk_menu_item_new_with_label(str);
1618 g_object_set_data(G_OBJECT(menu_item), "pixels_per_tick",
1619 GUINT_TO_POINTER(pixels_per_tick[i]));
1620 g_signal_connect(menu_item, "activate", G_CALLBACK(pixels_per_tick_select), user_data);
1621 gtk_widget_show(menu_item);
1622 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1624 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_PIXELS_PER_TICK);
1629 /****************************************************************************/
1630 static void create_tick_interval_menu_items(user_data_t* user_data, GtkWidget *menu)
1633 GtkWidget *menu_item;
1636 for(i=0;i<MAX_TICK_VALUES;i++){
1637 if(tick_interval_values[i]>=1000){
1638 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1639 } else if(tick_interval_values[i]>=100){
1640 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1641 } else if(tick_interval_values[i]>=10){
1642 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1644 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1647 menu_item=gtk_menu_item_new_with_label(str);
1648 g_object_set_data(G_OBJECT(menu_item), "tick_interval",
1649 GUINT_TO_POINTER(tick_interval_values[i]));
1650 g_signal_connect(menu_item, "activate", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1651 gtk_widget_show(menu_item);
1652 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item);
1654 gtk_menu_set_active(GTK_MENU(menu), DEFAULT_TICK_VALUE);
1658 /****************************************************************************/
1659 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, void (*func)(user_data_t* user_data, GtkWidget *menu))
1663 GtkWidget *option_menu;
1666 hbox=gtk_hbox_new(FALSE, 0);
1667 gtk_container_add(GTK_CONTAINER(box), hbox);
1668 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1669 gtk_widget_show(hbox);
1671 label=gtk_label_new(name);
1672 gtk_widget_show(label);
1673 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1675 option_menu=gtk_option_menu_new();
1676 menu=gtk_menu_new();
1677 (*func)(user_data, menu);
1678 gtk_option_menu_set_menu(GTK_OPTION_MENU(option_menu), menu);
1679 gtk_box_pack_end(GTK_BOX(hbox), option_menu, FALSE, FALSE, 0);
1680 gtk_widget_show(option_menu);
1683 /****************************************************************************/
1684 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1686 GtkWidget *frame_vbox;
1690 frame_vbox=gtk_vbox_new(FALSE, 0);
1691 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1692 gtk_widget_show(frame_vbox);
1694 frame = gtk_frame_new("X Axis");
1695 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1696 gtk_widget_show(frame);
1698 vbox=gtk_vbox_new(FALSE, 0);
1699 gtk_container_add(GTK_CONTAINER(frame), vbox);
1700 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1701 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1702 gtk_widget_show(vbox);
1704 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1705 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1707 frame = gtk_frame_new("Y Axis");
1708 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1709 gtk_widget_show(frame);
1711 vbox=gtk_vbox_new(FALSE, 0);
1712 gtk_container_add(GTK_CONTAINER(frame), vbox);
1713 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1714 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1715 gtk_widget_show(vbox);
1717 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1722 /****************************************************************************/
1723 static void dialog_graph_init_window(user_data_t* user_data)
1727 GtkWidget *bt_close;
1729 /* create the main window */
1730 user_data->dlg.dialog_graph.window=dlg_window_new("I/O Graphs"); /* transient_for top_level */
1732 vbox=gtk_vbox_new(FALSE, 0);
1733 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1734 gtk_widget_show(vbox);
1736 create_draw_area(user_data, vbox);
1738 hbox=gtk_hbox_new(FALSE, 3);
1739 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1740 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1741 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1742 gtk_widget_show(hbox);
1744 create_filter_area(user_data, hbox);
1745 create_ctrl_area(user_data, hbox);
1747 dialog_graph_set_title(user_data);
1749 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1750 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1751 gtk_widget_show(hbox);
1753 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1754 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1756 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1758 gtk_widget_show(user_data->dlg.dialog_graph.window);
1759 window_present(user_data->dlg.dialog_graph.window);
1764 /****************************************************************************/
1765 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1767 if (user_data->dlg.dialog_graph.window != NULL) {
1768 /* There's already a graph window; reactivate it. */
1769 reactivate_window(user_data->dlg.dialog_graph.window);
1773 dialog_graph_init_window(user_data);
1777 /****************************************************************************/
1779 static void on_goto_bt_clicked_lst(GtkWidget *bt _U_, user_data_t *user_data _U_)
1782 GtkTreeModel *model;
1783 GtkTreeSelection *selection;
1786 selection = user_data->dlg.selected_list_sel;
1788 if (selection==NULL)
1791 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1792 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1793 cf_goto_frame(&cfile, fnumber);
1798 static void draw_stat(user_data_t *user_data);
1800 /****************************************************************************/
1801 /* re-dissects all packets */
1802 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1804 GString *error_string;
1806 /* remove tap listener */
1807 protect_thread_critical_region();
1808 remove_tap_listener(user_data);
1809 unprotect_thread_critical_region();
1811 /* register tap listener */
1812 error_string = register_tap_listener("rtp", user_data, NULL, 0,
1813 rtp_reset, rtp_packet, rtp_draw);
1814 if (error_string != NULL) {
1815 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1816 g_string_free(error_string, TRUE);
1820 /* retap all packets */
1821 cf_retap_packets(&cfile);
1823 /* draw statistics info */
1824 draw_stat(user_data);
1828 static void on_next_bt_clicked_list(GtkWidget *bt _U_, user_data_t *user_data _U_)
1831 GtkTreeModel *model;
1833 GtkTreeSelection *selection;
1836 selection = user_data->dlg.selected_list_sel;
1838 if (selection==NULL)
1842 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1843 while (gtk_tree_model_iter_next (model,&iter)) {
1844 gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1845 if (strcmp(text, OK_TEXT) != 0) {
1846 gtk_tree_selection_select_iter (selection, &iter);
1847 path = gtk_tree_model_get_path(model, &iter);
1848 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1851 gtk_tree_path_free(path);
1858 if (user_data->dlg.number_of_nok>1){
1859 /* Get the first iter and select it before starting over */
1860 gtk_tree_model_get_iter_first(model, &iter);
1861 gtk_tree_selection_select_iter (selection, &iter);
1868 /****************************************************************************/
1869 /* when we want to save the information */
1870 static gboolean save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
1873 GtkWidget *rev, *forw, *both;
1874 user_data_t *user_data;
1876 GtkListStore *store;
1878 GtkTreeModel *model;
1879 gboolean more_items = TRUE;
1881 /* To Hold data from the list row */
1882 guint packet; /* Packet */
1883 guint sequence; /* Sequence */
1884 gfloat delta; /* Delta(ms) */
1885 gfloat jitter; /* Jitter(ms) */
1886 gfloat ipbw; /* IP BW(kbps) */
1887 gboolean marker; /* Marker */
1888 char * status_str; /* Status */
1889 char * date_str; /* Date */
1890 guint length; /* Length */
1896 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
1898 /* Perhaps the user specified a directory instead of a file.
1899 * Check whether they did.
1901 if (test_for_directory(g_dest) == EISDIR) {
1902 /* It's a directory - set the file selection box to display it. */
1903 set_last_open_dir(g_dest);
1905 file_selection_set_current_folder(fc, get_last_open_dir());
1906 gtk_file_chooser_set_current_name(fc, "");
1907 return FALSE; /* run the dialog again */
1910 rev = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
1911 forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
1912 both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
1913 user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
1915 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
1916 fp = ws_fopen(g_dest, "w");
1918 open_failure_alert_box(g_dest, errno, TRUE);
1920 return TRUE; /* we're done */
1923 if (GTK_TOGGLE_BUTTON(both)->active) {
1924 fprintf(fp, "Forward\n");
1926 write_failure_alert_box(g_dest, errno);
1929 return TRUE; /* we're done */
1933 for(j = 0; j < NUM_COLS; j++) {
1935 fprintf(fp,"%s",titles[j]);
1937 fprintf(fp,",%s",titles[j]);
1942 write_failure_alert_box(g_dest, errno);
1947 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
1948 store = GTK_LIST_STORE(model);
1949 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
1952 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1963 fprintf(fp, "%u",packet);
1964 fprintf(fp, ",%u", sequence);
1965 fprintf(fp, ",%.2f", delta);
1966 fprintf(fp, ",%.2f", jitter);
1967 fprintf(fp, ",%.2f", ipbw);
1968 fprintf(fp, ",%s", marker? "SET" : "");
1969 fprintf(fp, ",%s", status_str);
1970 fprintf(fp, ",%s", date_str);
1971 fprintf(fp, ",%u", length);
1976 write_failure_alert_box(g_dest, errno);
1979 return TRUE; /* we're done */
1982 more_items = gtk_tree_model_iter_next (model,&iter);
1986 if (fclose(fp) == EOF) {
1987 write_failure_alert_box(g_dest, errno);
1989 return TRUE; /* we're done */
1993 if (GTK_TOGGLE_BUTTON(rev)->active || GTK_TOGGLE_BUTTON(both)->active) {
1995 if (GTK_TOGGLE_BUTTON(both)->active) {
1996 fp = ws_fopen(g_dest, "a");
1998 open_failure_alert_box(g_dest, errno, TRUE);
2000 return TRUE; /* we're done */
2002 fprintf(fp, "\nReverse\n");
2004 write_failure_alert_box(g_dest, errno);
2007 return TRUE; /* we're done */
2010 fp = ws_fopen(g_dest, "w");
2012 open_failure_alert_box(g_dest, errno, TRUE);
2014 return TRUE; /* we're done */
2017 for(j = 0; j < NUM_COLS; j++) {
2019 fprintf(fp,"%s",titles[j]);
2021 fprintf(fp,",%s",titles[j]);
2026 write_failure_alert_box(g_dest, errno);
2029 return TRUE; /* we're done */
2031 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
2032 store = GTK_LIST_STORE(model);
2033 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2038 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2049 fprintf(fp, "%u",packet);
2050 fprintf(fp, ",%u", sequence);
2051 fprintf(fp, ",%.2f", delta);
2052 fprintf(fp, ",%.2f", jitter);
2053 fprintf(fp, ",%.2f", ipbw);
2054 fprintf(fp, ",%s", marker? "SET" : "");
2055 fprintf(fp, ",%s", status_str);
2056 fprintf(fp, ",%s", date_str);
2057 fprintf(fp, ",%u", length);
2062 write_failure_alert_box(g_dest, errno);
2065 return TRUE; /* we're done */
2068 more_items = gtk_tree_model_iter_next (model,&iter);
2071 if (fclose(fp) == EOF) {
2072 write_failure_alert_box(g_dest, errno);
2074 return TRUE; /* we're done */
2079 return TRUE; /* we're done */
2082 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2084 user_data->dlg.save_csv_as_w = NULL;
2087 /* when the user wants to save the csv information in a file */
2088 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
2092 GtkWidget *label_format;
2093 GtkWidget *channels_label;
2094 GSList *channels_group = NULL;
2095 GtkWidget *forward_rb;
2096 GtkWidget *reversed_rb;
2099 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2100 if (user_data->dlg.save_csv_as_w != NULL) {
2101 /* There's already a Save CSV info dialog box; reactivate it. */
2102 reactivate_window(user_data->dlg.save_csv_as_w);
2106 user_data->dlg.save_csv_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2107 GTK_WINDOW(user_data->dlg.notebook),
2108 GTK_FILE_CHOOSER_ACTION_SAVE,
2109 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2110 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2112 #if GTK_CHECK_VERSION(2,8,0)
2113 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
2115 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_csv_as_w),GTK_WINDOW(user_data->dlg.window));
2117 /* Container for each row of widgets */
2118 vertb = gtk_vbox_new(FALSE, 0);
2119 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2120 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2121 gtk_widget_show (vertb);
2123 table1 = gtk_table_new (2, 4, FALSE);
2124 gtk_widget_show (table1);
2125 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2126 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2127 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2129 label_format = gtk_label_new ("Format: Comma Separated Values");
2130 gtk_widget_show (label_format);
2131 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2132 (GtkAttachOptions) (GTK_FILL),
2133 (GtkAttachOptions) (0), 0, 0);
2135 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2138 channels_label = gtk_label_new ("Channels: ");
2139 gtk_widget_show (channels_label);
2140 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2141 (GtkAttachOptions) (GTK_FILL),
2142 (GtkAttachOptions) (0), 0, 0);
2143 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2145 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2146 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2147 gtk_widget_show (forward_rb);
2148 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2149 (GtkAttachOptions) (GTK_FILL),
2150 (GtkAttachOptions) (0), 0, 0);
2152 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed ");
2153 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2154 gtk_widget_show (reversed_rb);
2155 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2156 (GtkAttachOptions) (GTK_FILL),
2157 (GtkAttachOptions) (0), 0, 0);
2159 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2160 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2161 gtk_widget_show (both_rb);
2162 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2163 (GtkAttachOptions) (GTK_FILL),
2164 (GtkAttachOptions) (0), 0, 0);
2166 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2168 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2169 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2170 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2171 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2173 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2174 G_CALLBACK(window_delete_event_cb), NULL);
2175 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2176 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2178 gtk_widget_show(user_data->dlg.save_csv_as_w);
2179 window_present(user_data->dlg.save_csv_as_w);
2181 /* "Run" the GtkFileChooserDialog. */
2182 /* Upon exit: If "Accept" run the OK callback. */
2183 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2184 /* Destroy the window. */
2185 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2186 /* return with a TRUE status so that the dialog window will be destroyed. */
2187 /* Trying to re-run the dialog after popping up an alert box will not work */
2188 /* since the user will not be able to dismiss the alert box. */
2189 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2190 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2192 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2193 /* GtkFileChooserDialog. */
2194 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
2195 if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
2196 break; /* we're done */
2199 window_destroy(user_data->dlg.save_csv_as_w);
2203 /****************************************************************************/
2204 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2206 /* Note that we no longer have a Save voice info dialog box. */
2207 user_data->dlg.save_voice_as_w = NULL;
2210 /****************************************************************************/
2211 /* here we save it into a file that user specified */
2212 /* XXX what about endians here? could go something wrong? */
2213 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2215 FILE *to_stream, *forw_stream, *rev_stream;
2216 size_t fwritten, rwritten;
2217 int f_rawvalue, r_rawvalue, rawvalue;
2220 guint32 f_write_silence = 0;
2221 guint32 r_write_silence = 0;
2223 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2224 gboolean stop_flag = FALSE;
2227 forw_stream = ws_fopen(user_data->f_tempname, "rb");
2228 if (forw_stream == NULL)
2230 rev_stream = ws_fopen(user_data->r_tempname, "rb");
2231 if (rev_stream == NULL) {
2232 fclose(forw_stream);
2236 /* open file for saving */
2237 to_stream = ws_fopen(dest, "wb");
2238 if (to_stream == NULL) {
2239 fclose(forw_stream);
2244 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2246 if (format == SAVE_AU_FORMAT) /* au format */
2248 /* First we write the .au header. XXX Hope this is endian independant */
2249 /* the magic word 0x2e736e64 == .snd */
2250 phtonl(pd, 0x2e736e64);
2251 nchars=fwrite(pd, 1, 4, to_stream);
2252 /* header offset == 24 bytes */
2254 nchars=fwrite(pd, 1, 4, to_stream);
2255 /* total length; it is permitted to set this to 0xffffffff */
2257 nchars=fwrite(pd, 1, 4, to_stream);
2258 /* encoding format == 16-bit linear PCM */
2260 nchars=fwrite(pd, 1, 4, to_stream);
2261 /* sample rate == 8000 Hz */
2263 nchars=fwrite(pd, 1, 4, to_stream);
2266 nchars=fwrite(pd, 1, 4, to_stream);
2270 /* only forward direction */
2271 case SAVE_FORWARD_DIRECTION_MASK: {
2272 progbar_count = user_data->forward.saveinfo.count;
2273 progbar_quantum = user_data->forward.saveinfo.count/100;
2274 while ((f_rawvalue = getc(forw_stream)) != EOF) {
2277 if((count > progbar_nextstep) && (count <= progbar_count)) {
2278 update_progress_dlg(progbar,
2279 (gfloat) count/progbar_count, "Saving");
2280 progbar_nextstep = progbar_nextstep + progbar_quantum;
2284 if (user_data->forward.statinfo.pt == PT_PCMU){
2285 sample = ulaw2linear((unsigned char)f_rawvalue);
2288 else if(user_data->forward.statinfo.pt == PT_PCMA){
2289 sample = alaw2linear((unsigned char)f_rawvalue);
2293 fclose(forw_stream);
2296 destroy_progress_dlg(progbar);
2300 fwritten = fwrite(pd, 1, 2, to_stream);
2302 fclose(forw_stream);
2305 destroy_progress_dlg(progbar);
2311 /* only reversed direction */
2312 case SAVE_REVERSE_DIRECTION_MASK: {
2313 progbar_count = user_data->reversed.saveinfo.count;
2314 progbar_quantum = user_data->reversed.saveinfo.count/100;
2315 while ((r_rawvalue = getc(rev_stream)) != EOF) {
2318 if((count > progbar_nextstep) && (count <= progbar_count)) {
2319 update_progress_dlg(progbar,
2320 (gfloat) count/progbar_count, "Saving");
2321 progbar_nextstep = progbar_nextstep + progbar_quantum;
2325 if (user_data->reversed.statinfo.pt == PT_PCMU){
2326 sample = ulaw2linear((unsigned char)r_rawvalue);
2329 else if(user_data->reversed.statinfo.pt == PT_PCMA){
2330 sample = alaw2linear((unsigned char)r_rawvalue);
2334 fclose(forw_stream);
2337 destroy_progress_dlg(progbar);
2341 rwritten = fwrite(pd, 1, 2, to_stream);
2343 fclose(forw_stream);
2346 destroy_progress_dlg(progbar);
2352 /* both directions */
2353 case SAVE_BOTH_DIRECTION_MASK: {
2354 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2355 (progbar_count = user_data->forward.saveinfo.count) :
2356 (progbar_count = user_data->reversed.saveinfo.count);
2357 progbar_quantum = progbar_count/100;
2358 /* since conversation in one way can start later than in the other one,
2359 * we have to write some silence information for one channel */
2360 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2361 f_write_silence = (guint32)
2362 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*8000);
2364 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2365 r_write_silence = (guint32)
2366 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*8000);
2371 if((count > progbar_nextstep) && (count <= progbar_count)) {
2372 update_progress_dlg(progbar,
2373 (gfloat) count/progbar_count, "Saving");
2374 progbar_nextstep = progbar_nextstep + progbar_quantum;
2377 if(f_write_silence > 0) {
2378 r_rawvalue = getc(rev_stream);
2379 switch (user_data->forward.statinfo.reg_pt) {
2381 f_rawvalue = SILENCE_PCMU;
2384 f_rawvalue = SILENCE_PCMA;
2392 else if(r_write_silence > 0) {
2393 f_rawvalue = getc(forw_stream);
2394 switch (user_data->reversed.statinfo.reg_pt) {
2396 r_rawvalue = SILENCE_PCMU;
2399 r_rawvalue = SILENCE_PCMA;
2408 f_rawvalue = getc(forw_stream);
2409 r_rawvalue = getc(rev_stream);
2411 if ((r_rawvalue == EOF) && (f_rawvalue == EOF))
2413 if ((user_data->forward.statinfo.pt == PT_PCMU) && (user_data->reversed.statinfo.pt == PT_PCMU)){
2414 sample = (ulaw2linear((unsigned char)r_rawvalue) + ulaw2linear((unsigned char)f_rawvalue)) / 2;
2417 else if((user_data->forward.statinfo.pt == PT_PCMA) && (user_data->reversed.statinfo.pt == PT_PCMA)){
2418 sample = (alaw2linear((unsigned char)r_rawvalue) + alaw2linear((unsigned char)f_rawvalue)) / 2;
2423 fclose(forw_stream);
2426 destroy_progress_dlg(progbar);
2431 rwritten = fwrite(pd, 1, 2, to_stream);
2433 fclose(forw_stream);
2436 destroy_progress_dlg(progbar);
2443 else if (format == SAVE_RAW_FORMAT) /* raw format */
2447 /* only forward direction */
2448 case SAVE_FORWARD_DIRECTION_MASK: {
2449 progbar_count = user_data->forward.saveinfo.count;
2450 progbar_quantum = user_data->forward.saveinfo.count/100;
2451 stream = forw_stream;
2454 /* only reversed direction */
2455 case SAVE_REVERSE_DIRECTION_MASK: {
2456 progbar_count = user_data->reversed.saveinfo.count;
2457 progbar_quantum = user_data->reversed.saveinfo.count/100;
2458 stream = rev_stream;
2462 fclose(forw_stream);
2465 destroy_progress_dlg(progbar);
2472 /* XXX how do you just copy the file? */
2473 while ((rawvalue = getc(stream)) != EOF) {
2476 if((count > progbar_nextstep) && (count <= progbar_count)) {
2477 update_progress_dlg(progbar,
2478 (gfloat) count/progbar_count, "Saving");
2479 progbar_nextstep = progbar_nextstep + progbar_quantum;
2483 if (putc(rawvalue, to_stream) == EOF) {
2484 fclose(forw_stream);
2487 destroy_progress_dlg(progbar);
2493 destroy_progress_dlg(progbar);
2494 fclose(forw_stream);
2501 /****************************************************************************/
2502 /* the user wants to save in a file */
2503 /* XXX support for different formats is currently commented out */
2504 static gboolean save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2507 /*GtkWidget *wav, *sw;*/
2508 GtkWidget *au, *raw;
2509 GtkWidget *rev, *forw, *both;
2510 user_data_t *user_data;
2511 gint channels, format;
2513 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2515 /* Perhaps the user specified a directory instead of a file.
2516 * Check whether they did.
2518 if (test_for_directory(g_dest) == EISDIR) {
2519 /* It's a directory - set the file selection box to display it. */
2520 set_last_open_dir(g_dest);
2522 file_selection_set_current_folder(fc, get_last_open_dir());
2523 gtk_file_chooser_set_current_name(fc, "");
2524 return FALSE; /* run the dialog again */
2528 wav = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2529 sw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2531 au = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
2532 raw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
2533 rev = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2534 forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
2535 both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
2536 user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
2538 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2539 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2540 * disable the ok button or disable the buttons for direction if only one is not ok. The
2541 * problem is if we open the save voice dialog and then click the refresh button and maybe
2542 * the state changes, so we can't save anymore. In this case we should be able to update
2543 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2546 /* we can not save in both directions */
2547 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2548 /* there are many combinations here, we just exit when first matches */
2549 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2550 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2551 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2552 "Can't save in a file: Unsupported codec!");
2553 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2554 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2555 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2556 "Can't save in a file: Wrong length of captured packets!");
2557 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2558 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2559 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2560 "Can't save in a file: RTP data with padding!");
2561 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2562 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2563 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2564 "Can't save in a file: Not all data in all packets was captured!");
2566 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2567 "Can't save in a file: File I/O problem!");
2569 return TRUE; /* we're done */
2571 /* we can not save forward direction */
2572 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2573 (GTK_TOGGLE_BUTTON (both)->active))) {
2574 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2575 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2576 "Can't save forward direction in a file: Unsupported codec!");
2577 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2578 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2579 "Can't save forward direction in a file: Wrong length of captured packets!");
2580 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2581 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2582 "Can't save forward direction in a file: RTP data with padding!");
2583 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2584 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2585 "Can't save forward direction in a file: Not all data in all packets was captured!");
2587 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2588 "Can't save forward direction in a file: File I/O problem!");
2590 return TRUE; /* we're done */
2592 /* we can not save reversed direction */
2593 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2594 (GTK_TOGGLE_BUTTON (both)->active))) {
2595 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2596 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2597 "Can't save reversed direction in a file: Unsupported codec!");
2598 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2599 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2600 "Can't save reversed direction in a file: Wrong length of captured packets!");
2601 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2602 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2603 "Can't save reversed direction in a file: RTP data with padding!");
2604 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2605 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2606 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2607 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2608 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2609 "Can't save reversed direction in a file: No RTP data!");
2611 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2612 "Can't save reversed direction in a file: File I/O problem!");
2614 return TRUE; /* we're done */
2618 if (GTK_TOGGLE_BUTTON (wav)->active)
2619 format = SAVE_WAV_FORMAT;
2622 if (GTK_TOGGLE_BUTTON (au)->active)
2623 format = SAVE_AU_FORMAT;
2625 else if (GTK_TOGGLE_BUTTON (sw)->active)
2626 format = SAVE_SW_FORMAT;
2628 else if (GTK_TOGGLE_BUTTON (raw)->active)
2629 format = SAVE_RAW_FORMAT;
2631 format = SAVE_NONE_FORMAT;
2633 if (GTK_TOGGLE_BUTTON (rev)->active)
2634 channels = SAVE_REVERSE_DIRECTION_MASK;
2635 else if (GTK_TOGGLE_BUTTON (both)->active)
2636 channels = SAVE_BOTH_DIRECTION_MASK;
2638 channels = SAVE_FORWARD_DIRECTION_MASK;
2640 /* direction/format validity*/
2641 if (format == SAVE_AU_FORMAT)
2643 /* make sure streams are alaw/ulaw */
2644 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != PT_PCMA) && (user_data->forward.statinfo.pt != PT_PCMU)){
2645 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2646 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2648 return TRUE; /* we're done */
2650 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != PT_PCMA) && (user_data->reversed.statinfo.pt != PT_PCMU)){
2651 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2652 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2654 return TRUE; /* we're done */
2656 /* make sure pt's don't differ */
2657 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2658 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2659 "Can't save in a file: Forward and reverse direction differ in type");
2661 return TRUE; /* we're done */
2664 else if (format == SAVE_RAW_FORMAT)
2666 /* can't save raw in both directions */
2667 if (channels == SAVE_BOTH_DIRECTION_MASK){
2668 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2669 "Can't save in a file: Unable to save raw data in both directions");
2671 return TRUE; /* we're done */
2676 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2677 "Can't save in a file: Invalid save format");
2679 return TRUE; /* we're done */
2682 if(!copy_file(g_dest, channels, format, user_data)) {
2683 /* XXX - report the error type! */
2684 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2685 "An error occurred while saving voice in a file!");
2687 return TRUE; /* we're done */
2691 return TRUE; /* we're done */
2694 /****************************************************************************/
2695 /* when the user wants to save the voice information in a file */
2696 /* XXX support for different formats is currently commented out */
2697 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
2701 GtkWidget *label_format;
2702 GtkWidget *channels_label;
2703 GSList *format_group = NULL;
2704 GSList *channels_group = NULL;
2705 GtkWidget *forward_rb;
2706 GtkWidget *reversed_rb;
2708 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2712 /* if we can't save in a file: wrong codec, cut packets or other errors */
2713 /* Should the error arise here or later when you click ok button ?
2714 * if we do it here, then we must disable the refresh button, so we don't do it here
2717 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2718 if (user_data->dlg.save_voice_as_w != NULL) {
2719 /* There's already a Save voice info dialog box; reactivate it. */
2720 reactivate_window(user_data->dlg.save_voice_as_w);
2724 /* XXX - use file_selection from dlg_utils instead! */
2725 user_data->dlg.save_voice_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2726 GTK_WINDOW(user_data->dlg.notebook),
2727 GTK_FILE_CHOOSER_ACTION_SAVE,
2728 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2729 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2731 #if GTK_CHECK_VERSION(2,8,0)
2732 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
2734 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_voice_as_w),GTK_WINDOW(user_data->dlg.window));
2736 /* Container for each row of widgets */
2737 vertb = gtk_vbox_new(FALSE, 0);
2738 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2739 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2740 gtk_widget_show (vertb);
2742 table1 = gtk_table_new (2, 4, FALSE);
2743 gtk_widget_show (table1);
2744 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2745 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2746 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2748 /*label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2749 gtk_widget_show (label_format);
2750 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2751 (GtkAttachOptions) (GTK_FILL),
2752 (GtkAttachOptions) (0), 0, 0);*/
2754 label_format = gtk_label_new ("Format: ");
2755 gtk_widget_show (label_format);
2756 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2757 (GtkAttachOptions) (GTK_FILL),
2758 (GtkAttachOptions) (0), 0, 0);
2760 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2762 raw_rb = gtk_radio_button_new_with_label (format_group, ".raw");
2763 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (raw_rb));
2764 gtk_widget_show (raw_rb);
2765 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2766 (GtkAttachOptions) (GTK_FILL),
2767 (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 /* we support .au - ulaw*/
2779 wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2780 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (wav_rb));
2781 gtk_widget_show (wav_rb);
2782 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2783 (GtkAttachOptions) (GTK_FILL),
2784 (GtkAttachOptions) (0), 0, 0);
2786 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2787 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (sw_rb));
2788 gtk_widget_show (sw_rb);
2789 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2790 (GtkAttachOptions) (GTK_FILL),
2791 (GtkAttachOptions) (0), 0, 0);
2792 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2793 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2794 gtk_widget_show (au_rb);
2795 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2796 (GtkAttachOptions) (GTK_FILL),
2797 (GtkAttachOptions) (0), 0, 0);
2800 channels_label = gtk_label_new ("Channels: ");
2801 gtk_widget_show (channels_label);
2802 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2803 (GtkAttachOptions) (GTK_FILL),
2804 (GtkAttachOptions) (0), 0, 0);
2805 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2807 forward_rb = gtk_radio_button_new_with_label (channels_group, "forward ");
2808 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2809 gtk_widget_show (forward_rb);
2810 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2811 (GtkAttachOptions) (GTK_FILL),
2812 (GtkAttachOptions) (0), 0, 0);
2814 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed ");
2815 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (reversed_rb));
2816 gtk_widget_show (reversed_rb);
2817 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2818 (GtkAttachOptions) (GTK_FILL),
2819 (GtkAttachOptions) (0), 0, 0);
2821 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2822 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (both_rb));
2823 gtk_widget_show (both_rb);
2824 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2825 (GtkAttachOptions) (GTK_FILL),
2826 (GtkAttachOptions) (0), 0, 0);
2829 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
2832 /* if one direction is nok we don't allow saving
2833 XXX this is not ok since the user can click the refresh button and cause changes
2834 but we can not update this window. So we move all the decision on the time the ok
2837 if (user_data->forward.saved == FALSE) {
2838 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2839 gtk_widget_set_sensitive(forward_rb, FALSE);
2840 gtk_widget_set_sensitive(both_rb, FALSE);
2842 else if (user_data->reversed.saved == FALSE) {
2843 gtk_widget_set_sensitive(reversed_rb, FALSE);
2844 gtk_widget_set_sensitive(both_rb, FALSE);
2848 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
2849 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2850 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
2851 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2852 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2853 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2854 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2855 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2857 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2858 G_CALLBACK(window_delete_event_cb), NULL);
2859 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2860 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2862 gtk_widget_show(user_data->dlg.save_voice_as_w);
2863 window_present(user_data->dlg.save_voice_as_w);
2865 /* "Run" the GtkFileChooserDialog. */
2866 /* Upon exit: If "Accept" run the OK callback. */
2867 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2868 /* Destroy the window. */
2869 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2870 /* return with a TRUE status so that the dialog window will be destroyed. */
2871 /* Trying to re-run the dialog after popping up an alert box will not work */
2872 /* since the user will not be able to dismiss the alert box. */
2873 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2874 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2876 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2877 /* GtkFileChooserDialog. */
2878 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
2879 if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
2880 break; /* we're done */
2883 window_destroy(user_data->dlg.save_voice_as_w);
2888 /****************************************************************************/
2889 /* when we are finished with redisection, we add the label for the statistic */
2890 static void draw_stat(user_data_t *user_data)
2892 gchar label_max[300];
2893 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
2894 - user_data->forward.statinfo.start_seq_nr + 1;
2895 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
2896 - user_data->reversed.statinfo.start_seq_nr + 1;
2897 guint32 f_total_nr = user_data->forward.statinfo.total_nr;
2898 guint32 r_total_nr = user_data->reversed.statinfo.total_nr;
2899 gint32 f_lost = f_expected - f_total_nr;
2900 gint32 r_lost = r_expected - r_total_nr;
2901 double f_sumt = user_data->forward.statinfo.sumt;
2902 double f_sumTS = user_data->forward.statinfo.sumTS;
2903 double f_sumt2 = user_data->forward.statinfo.sumt2;
2904 double f_sumtTS = user_data->forward.statinfo.sumtTS;
2906 double r_sumt = user_data->reversed.statinfo.sumt;
2907 double r_sumTS = user_data->reversed.statinfo.sumTS;
2908 double r_sumt2 = user_data->reversed.statinfo.sumt2;
2909 double r_sumtTS = user_data->reversed.statinfo.sumtTS;
2910 double f_perc, r_perc;
2911 double f_clock_drift = 1.0;
2912 double r_clock_drift = 1.0;
2913 double f_duration = user_data->forward.statinfo.time - user_data->forward.statinfo.start_time;
2914 double r_duration = user_data->reversed.statinfo.time - user_data->reversed.statinfo.start_time;
2915 guint32 f_clock_rate = user_data->forward.statinfo.clock_rate;
2916 guint32 r_clock_rate = user_data->reversed.statinfo.clock_rate;
2918 if (f_clock_rate == 0){
2922 if (r_clock_rate == 0){
2927 f_perc = (double)(f_lost*100)/(double)f_expected;
2932 r_perc = (double)(r_lost*100)/(double)r_expected;
2937 if ((f_total_nr >0)&&(f_sumt2 > 0)){
2938 f_clock_drift = (f_total_nr * f_sumtTS - f_sumt * f_sumTS) / (f_total_nr * f_sumt2 - f_sumt * f_sumt);
2940 if ((r_total_nr >0)&&(r_sumt2 > 0)){
2941 r_clock_drift = (r_total_nr * r_sumtTS - r_sumt * r_sumTS) / (r_total_nr * r_sumt2 - r_sumt * r_sumt);
2943 g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
2944 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
2945 "Max skew = %.2f ms.\n"
2946 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2947 " Sequence errors = %u \n"
2948 "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
2949 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
2950 user_data->forward.statinfo.max_jitter,user_data->forward.statinfo.mean_jitter,
2951 user_data->forward.statinfo.max_skew,
2952 f_expected, f_expected, f_lost, f_perc,
2953 user_data->forward.statinfo.sequence,
2954 f_duration/1000,f_duration*(f_clock_drift-1.0),f_clock_drift*f_clock_rate,100.0*(f_clock_drift-1.0));
2956 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2957 gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_fwd),TRUE);
2959 g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
2960 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
2961 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2962 " Sequence errors = %u \n"
2963 "Duration %.0f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
2964 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
2965 user_data->reversed.statinfo.max_jitter,user_data->reversed.statinfo.mean_jitter,
2966 r_expected, r_expected, r_lost, r_perc,
2967 user_data->reversed.statinfo.sequence,
2968 r_duration/1000,r_duration*(r_clock_drift-1.0),r_clock_drift*r_clock_rate,100.0*(r_clock_drift-1.0));
2970 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2971 gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_rev),TRUE);
2978 /****************************************************************************/
2979 /* append a line to list */
2980 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num,
2981 double delta, double jitter,double skew, double bandwidth, gchar *status, gboolean marker,
2982 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
2984 GtkListStore *list_store;
2986 if (strcmp(status, OK_TEXT) != 0) {
2987 user_data->dlg.number_of_nok++;
2990 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
2992 /* Creates a new row at position. iter will be changed to point to this new row.
2993 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
2994 * The row will be filled with the values given to this function.
2996 * should generally be preferred when inserting rows in a sorted list store.
2998 #if GTK_CHECK_VERSION(2,6,0)
2999 gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
3001 gtk_list_store_append (list_store, &user_data->dlg.iter);
3002 gtk_list_store_set (list_store, &user_data->dlg.iter,
3004 PACKET_COLUMN, number,
3005 SEQUENCE_COLUMN, seq_num,
3006 DELTA_COLUMN, delta,
3007 JITTER_COLUMN, jitter,
3009 IPBW_COLUMN, bandwidth,
3010 MARKER_COLUMN, marker,
3011 STATUS_COLUMN, (char *)status,
3012 DATE_COLUMN, (char *)timeStr,
3013 LENGTH_COLUMN, pkt_len,
3014 FOREGROUND_COLOR_COL, NULL,
3015 BACKGROUND_COLOR_COL, (char *)color_str,
3018 if(flags & STAT_FLAG_FIRST){
3019 /* Set first row as active */
3020 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
3024 /****************************************************************************
3025 * Functions needed to present values from the list
3029 /* Present boolean value */
3031 rtp_boolean_data_func (GtkTreeViewColumn *column _U_,
3032 GtkCellRenderer *renderer,
3033 GtkTreeModel *model,
3039 /* the col to get data from is in userdata */
3040 gint bool_col = GPOINTER_TO_INT(user_data);
3042 gtk_tree_model_get(model, iter, bool_col, &bool_val, -1);
3046 g_strlcpy(buf, bool_val ? "SET" : "", sizeof(buf));
3049 g_assert_not_reached();
3052 g_object_set(renderer, "text", buf, NULL);
3057 GtkWidget* create_list(user_data_t* user_data)
3060 GtkListStore *list_store;
3062 GtkTreeViewColumn *column;
3063 GtkCellRenderer *renderer;
3064 GtkTreeSortable *sortable;
3065 GtkTreeView *list_view;
3066 GtkTreeSelection *selection;
3068 /* Create the store */
3069 list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX*/
3070 G_TYPE_UINT, /* Packet */
3071 G_TYPE_UINT, /* Sequence */
3072 G_TYPE_FLOAT, /* Delta(ms) */
3073 G_TYPE_FLOAT, /* Filtered Jitter(ms) */
3074 G_TYPE_FLOAT, /* Skew(ms) */
3075 G_TYPE_FLOAT, /* IP BW(kbps) */
3076 G_TYPE_BOOLEAN, /* Marker */
3077 G_TYPE_STRING, /* Status */
3078 G_TYPE_STRING, /* Date */
3079 G_TYPE_UINT, /* Length */
3080 G_TYPE_STRING, /* Foreground color */
3081 G_TYPE_STRING); /* Background color */
3084 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
3086 list_view = GTK_TREE_VIEW(list);
3087 sortable = GTK_TREE_SORTABLE(list_store);
3089 #if GTK_CHECK_VERSION(2,6,0)
3090 /* Speed up the list display */
3091 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
3094 /* Setup the sortable columns */
3095 gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
3096 gtk_tree_view_set_headers_clickable(list_view, FALSE);
3098 /* The view now holds a reference. We can get rid of our own reference */
3099 g_object_unref (G_OBJECT (list_store));
3102 * Create the first column packet, associating the "text" attribute of the
3103 * cell_renderer to the first column of the model
3105 renderer = gtk_cell_renderer_text_new ();
3106 column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
3107 "text", PACKET_COLUMN,
3108 "foreground", FOREGROUND_COLOR_COL,
3109 "background", BACKGROUND_COLOR_COL,
3111 gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
3112 gtk_tree_view_column_set_resizable(column, TRUE);
3113 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3114 gtk_tree_view_column_set_min_width(column, 55);
3116 /* Add the column to the view. */
3117 gtk_tree_view_append_column (list_view, column);
3120 renderer = gtk_cell_renderer_text_new ();
3121 column = gtk_tree_view_column_new_with_attributes ("Sequence", renderer,
3122 "text", SEQUENCE_COLUMN,
3123 "foreground", FOREGROUND_COLOR_COL,
3124 "background", BACKGROUND_COLOR_COL,
3126 gtk_tree_view_column_set_sort_column_id(column, SEQUENCE_COLUMN);
3127 gtk_tree_view_column_set_resizable(column, TRUE);
3128 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3129 gtk_tree_view_column_set_min_width(column, 75);
3130 gtk_tree_view_append_column (list_view, column);
3133 renderer = gtk_cell_renderer_text_new ();
3134 column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
3135 "text", DELTA_COLUMN,
3136 "foreground", FOREGROUND_COLOR_COL,
3137 "background", BACKGROUND_COLOR_COL,
3140 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3141 GINT_TO_POINTER(DELTA_COLUMN), NULL);
3143 gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
3144 gtk_tree_view_column_set_resizable(column, TRUE);
3145 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3146 gtk_tree_view_column_set_min_width(column, 75);
3147 gtk_tree_view_append_column (list_view, column);
3150 renderer = gtk_cell_renderer_text_new ();
3151 column = gtk_tree_view_column_new_with_attributes ("Filtered Jitter(ms)", renderer,
3152 "text", JITTER_COLUMN,
3153 "foreground", FOREGROUND_COLOR_COL,
3154 "background", BACKGROUND_COLOR_COL,
3157 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3158 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3160 gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3161 gtk_tree_view_column_set_resizable(column, TRUE);
3162 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3163 gtk_tree_view_column_set_min_width(column, 110);
3164 gtk_tree_view_append_column (list_view, column);
3167 renderer = gtk_cell_renderer_text_new ();
3168 column = gtk_tree_view_column_new_with_attributes ("Skew(ms)", renderer,
3169 "text", SKEW_COLUMN,
3170 "foreground", FOREGROUND_COLOR_COL,
3171 "background", BACKGROUND_COLOR_COL,
3174 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3175 GINT_TO_POINTER(SKEW_COLUMN), NULL);
3177 gtk_tree_view_column_set_sort_column_id(column, SKEW_COLUMN);
3178 gtk_tree_view_column_set_resizable(column, TRUE);
3179 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3180 gtk_tree_view_column_set_min_width(column, 110);
3181 gtk_tree_view_append_column (list_view, column);
3184 renderer = gtk_cell_renderer_text_new ();
3185 column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
3186 "text", IPBW_COLUMN,
3187 "foreground", FOREGROUND_COLOR_COL,
3188 "background", BACKGROUND_COLOR_COL,
3191 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3192 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3194 gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3195 gtk_tree_view_column_set_resizable(column, TRUE);
3196 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3197 gtk_tree_view_column_set_min_width(column, 80);
3198 gtk_tree_view_append_column (list_view, column);
3201 renderer = gtk_cell_renderer_text_new ();
3202 column = gtk_tree_view_column_new_with_attributes ("Marker", renderer,
3203 "text", MARKER_COLUMN,
3204 "foreground", FOREGROUND_COLOR_COL,
3205 "background", BACKGROUND_COLOR_COL,
3208 gtk_tree_view_column_set_cell_data_func(column, renderer, rtp_boolean_data_func,
3209 GINT_TO_POINTER(MARKER_COLUMN), NULL);
3211 gtk_tree_view_column_set_sort_column_id(column, MARKER_COLUMN);
3212 gtk_tree_view_column_set_resizable(column, TRUE);
3213 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3214 gtk_tree_view_column_set_min_width(column, 60);
3215 gtk_tree_view_append_column (list_view, column);
3218 renderer = gtk_cell_renderer_text_new ();
3219 column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3220 "text", STATUS_COLUMN,
3221 "foreground", FOREGROUND_COLOR_COL,
3222 "background", BACKGROUND_COLOR_COL,
3224 gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3225 gtk_tree_view_column_set_resizable(column, TRUE);
3226 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3227 gtk_tree_view_column_set_min_width(column, 100);
3228 gtk_tree_view_append_column (list_view, column);
3230 /* Now enable the sorting of each column */
3231 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3232 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3234 /* Setup the selection handler */
3235 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3236 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3238 g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3239 G_CALLBACK (on_list_select_row),
3244 /****************************************************************************/
3245 /* Create the dialog box with all widgets */
3246 static void create_rtp_dialog(user_data_t* user_data)
3248 GtkWidget *window = NULL;
3249 GtkWidget *list_fwd;
3250 GtkWidget *list_rev;
3251 GtkWidget *label_stats_fwd;
3252 GtkWidget *label_stats_rev;
3253 GtkWidget *notebook;
3255 GtkWidget *main_vb, *page, *page_r;
3257 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3258 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3259 #ifdef USE_CONVERSATION_GRAPH
3260 GtkWidget *graph_bt;
3262 GtkWidget *graph_bt;
3263 gchar label_forward[150];
3264 gchar label_forward_tree[150];
3265 gchar label_reverse[150];
3267 gchar str_ip_src[16];
3268 gchar str_ip_dst[16];
3270 window = dlg_window_new("Wireshark: RTP Stream Analysis"); /* transient_for top_level */
3271 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3273 /* Container for each row of widgets */
3274 main_vb = gtk_vbox_new(FALSE, 2);
3275 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3276 gtk_container_add(GTK_CONTAINER(window), main_vb);
3277 gtk_widget_show(main_vb);
3280 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), sizeof(str_ip_src));
3281 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), sizeof(str_ip_dst));
3283 g_snprintf(label_forward, sizeof(label_forward),
3284 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3285 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3287 g_snprintf(label_forward_tree, sizeof(label_forward_tree),
3288 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3289 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3292 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), sizeof(str_ip_src));
3293 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), sizeof(str_ip_dst));
3295 g_snprintf(label_reverse, sizeof(label_reverse),
3296 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3297 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3299 /* Start a notebook for flipping between sets of changes */
3300 notebook = gtk_notebook_new();
3301 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3302 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3304 user_data->dlg.notebook_signal_id =
3305 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3307 /* page for forward connection */
3308 page = gtk_vbox_new(FALSE, 8);
3309 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3311 /* direction label */
3312 label = gtk_label_new(label_forward);
3313 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3315 /* place for some statistics */
3316 label_stats_fwd = gtk_label_new("\n");
3317 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3319 /* scrolled window */
3320 scrolled_window = scrolled_window_new(NULL, NULL);
3323 list_fwd = create_list(user_data);
3324 gtk_widget_show(list_fwd);
3325 gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3326 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3327 gtk_widget_show(scrolled_window);
3330 label = gtk_label_new(" Forward Direction ");
3331 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3333 /* same page for reversed connection */
3334 page_r = gtk_vbox_new(FALSE, 8);
3335 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3336 label = gtk_label_new(label_reverse);
3337 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3338 label_stats_rev = gtk_label_new("\n");
3339 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3341 scrolled_window_r = scrolled_window_new(NULL, NULL);
3343 list_rev = create_list(user_data);
3344 gtk_widget_show(list_rev);
3345 gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3346 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3347 gtk_widget_show(scrolled_window_r);
3349 label = gtk_label_new(" Reversed Direction ");
3350 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3352 /* page for help&about or future */
3354 page_help = gtk_hbox_new(FALSE, 5);
3355 label = gtk_label_new(" Future ");
3356 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3357 frame = gtk_frame_new("");
3358 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3359 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3360 gtk_container_add(GTK_CONTAINER(frame), text);
3361 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3362 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3365 /* show all notebooks */
3366 gtk_widget_show_all(notebook);
3369 box4 = gtk_hbutton_box_new();
3370 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3371 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3372 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3373 gtk_box_set_spacing(GTK_BOX (box4), 0);
3374 gtk_widget_show(box4);
3376 voice_bt = gtk_button_new_with_label("Save payload...");
3377 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3378 gtk_widget_show(voice_bt);
3379 g_signal_connect(voice_bt, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
3381 csv_bt = gtk_button_new_with_label("Save as CSV...");
3382 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3383 gtk_widget_show(csv_bt);
3384 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3386 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3387 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3388 gtk_widget_show(refresh_bt);
3389 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3391 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3392 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3393 gtk_widget_show(goto_bt);
3394 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked_lst), user_data);
3396 graph_bt = gtk_button_new_with_label("Graph");
3397 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3398 gtk_widget_show(graph_bt);
3399 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3402 #ifdef USE_CONVERSATION_GRAPH
3403 graph_bt = gtk_button_new_with_label("Graph");
3404 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3405 gtk_widget_show(graph_bt);
3406 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3409 next_bt = gtk_button_new_with_label("Next non-Ok");
3410 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3411 gtk_widget_show(next_bt);
3412 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked_list), user_data);
3414 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3415 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3416 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3417 gtk_widget_show(close_bt);
3418 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3420 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3421 g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3423 gtk_widget_show(window);
3424 window_present(window);
3427 /* some widget references need to be saved for outside use */
3428 user_data->dlg.window = window;
3429 user_data->dlg.list_fwd = list_fwd;
3430 user_data->dlg.list_rev = list_rev;
3431 user_data->dlg.label_stats_fwd = label_stats_fwd;
3432 user_data->dlg.label_stats_rev = label_stats_rev;
3433 user_data->dlg.notebook = notebook;
3434 user_data->dlg.selected_list = list_fwd;
3435 user_data->dlg.number_of_nok = 0;
3438 * select the initial row
3440 gtk_widget_grab_focus(list_fwd);
3445 /****************************************************************************/
3446 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3447 const gchar* proto_field, guint32* p_result)
3450 proto_node *proto_sibling_node;
3451 header_field_info *hfssrc;
3454 finfo = PITEM_FINFO(ptree_node);
3456 if (hfinformation==(finfo->hfinfo)) {
3457 hfssrc = proto_registrar_get_byname(proto_field);
3460 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3461 ptree_node=ptree_node->next) {
3462 finfo=PITEM_FINFO(ptree_node);
3463 if (hfssrc==finfo->hfinfo) {
3464 if (hfinformation->type==FT_IPv4) {
3465 ipv4 = fvalue_get(&finfo->value);
3466 *p_result = ipv4_get_net_order_addr(ipv4);
3469 *p_result = fvalue_get_uinteger(&finfo->value);
3478 proto_sibling_node = ptree_node->next;
3480 if (proto_sibling_node) {
3481 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3487 /****************************************************************************/
3488 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3489 const gchar* proto_name,
3490 const gchar* proto_field,
3493 proto_node *ptree_node;
3494 header_field_info *hfinformation;
3496 hfinformation = proto_registrar_get_byname(proto_name);
3497 if (hfinformation == NULL)
3500 ptree_node = ((proto_node *)protocol_tree)->first_child;
3504 return process_node(ptree_node, hfinformation, proto_field, p_result);
3508 /****************************************************************************/
3510 address *ip_src_fwd,
3511 guint16 port_src_fwd,
3512 address *ip_dst_fwd,
3513 guint16 port_dst_fwd,
3515 address *ip_src_rev,
3516 guint16 port_src_rev,
3517 address *ip_dst_rev,
3518 guint16 port_dst_rev,
3522 user_data_t *user_data;
3525 static color_t col[MAX_GRAPHS] = {
3526 {0, 0x0000, 0x0000, 0x0000},
3527 {0, 0xffff, 0x0000, 0x0000},
3528 {0, 0x0000, 0xffff, 0x0000},
3529 {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(user_data->f_tempname, sizeof(user_data->f_tempname),
3552 fd = create_tempfile(user_data->r_tempname, sizeof(user_data->r_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 #ifdef USE_CONVERSATION_GRAPH
3562 user_data->dlg.graph_window = NULL;
3563 user_data->series_fwd.value_pairs = NULL;
3564 user_data->series_rev.value_pairs = NULL;
3567 /* init dialog_graph */
3568 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3569 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_VALUE];
3570 user_data->dlg.dialog_graph.draw_area=NULL;
3571 user_data->dlg.dialog_graph.pixmap=NULL;
3572 user_data->dlg.dialog_graph.scrollbar=NULL;
3573 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3574 user_data->dlg.dialog_graph.pixmap_width=500;
3575 user_data->dlg.dialog_graph.pixmap_height=200;
3576 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK];
3577 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3578 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3579 user_data->dlg.dialog_graph.max_interval=0;
3580 user_data->dlg.dialog_graph.num_items=0;
3581 user_data->dlg.dialog_graph.start_time = -1;
3583 for(i=0;i<MAX_GRAPHS;i++){
3584 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3585 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3586 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3587 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3588 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3589 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3590 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3591 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3594 /* create the dialog box */
3595 create_rtp_dialog(user_data);
3597 /* proceed as if the Refresh button would have been pressed */
3598 on_refresh_bt_clicked(NULL, user_data);
3601 /****************************************************************************/
3602 /* entry point from main menu */
3603 static void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3606 guint16 port_src_fwd;
3608 guint16 port_dst_fwd;
3609 guint32 ssrc_fwd = 0;
3611 guint16 port_src_rev;
3613 guint16 port_dst_rev;
3614 guint32 ssrc_rev = 0;
3615 unsigned int version_fwd;
3617 gchar filter_text[256];
3620 epan_dissect_t *edt;
3623 gboolean frame_matched;
3625 GList *strinfo_list;
3626 GList *filtered_list = NULL;
3627 rtp_stream_info_t *strinfo;
3630 /* Try to compile the filter. */
3631 g_strlcpy(filter_text,"rtp && rtp.version && rtp.ssrc && (ip || ipv6)",sizeof(filter_text));
3632 if (!dfilter_compile(filter_text, &sfcode)) {
3633 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3636 /* we load the current file into cf variable */
3638 fdata = cf->current_frame;
3640 /* we are on the selected frame now */
3642 return; /* if we exit here it's an error */
3644 /* dissect the current frame */
3645 if (!wtap_seek_read(cf->wth, fdata->file_off, &cf->pseudo_header,
3646 cf->pd, fdata->cap_len, &err, &err_info)) {
3647 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3648 cf_read_error_message(err, err_info), cf->filename);
3651 edt = epan_dissect_new(TRUE, FALSE);
3652 epan_dissect_prime_dfilter(edt, sfcode);
3653 epan_dissect_run(edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3654 frame_matched = dfilter_apply_edt(sfcode, edt);
3656 /* if it is not an rtp frame, show the rtpstream dialog */
3657 frame_matched = dfilter_apply_edt(sfcode, edt);
3658 if (frame_matched != 1) {
3659 epan_dissect_free(edt);
3660 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3661 "You didn't choose a RTP packet!");
3665 /* ok, it is a RTP frame, so let's get the ip and port values */
3666 COPY_ADDRESS(&(ip_src_fwd), &(edt->pi.src))
3667 COPY_ADDRESS(&(ip_dst_fwd), &(edt->pi.dst))
3668 port_src_fwd = edt->pi.srcport;
3669 port_dst_fwd = edt->pi.destport;
3671 /* assume the inverse ip/port combination for the reverse direction */
3672 COPY_ADDRESS(&(ip_src_rev), &(edt->pi.dst))
3673 COPY_ADDRESS(&(ip_dst_rev), &(edt->pi.src))
3674 port_src_rev = edt->pi.destport;
3675 port_dst_rev = edt->pi.srcport;
3677 /* check if it is RTP Version 2 */
3678 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3679 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3680 "RTP Version != 2 isn't supported!");
3684 /* now we need the SSRC value of the current frame */
3685 if (!get_int_value_from_proto_tree(edt->tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3686 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3687 "SSRC value couldn't be found!");
3691 /* Scan for rtpstream */
3693 /* search for reversed direction in the global rtp streams list */
3695 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3696 while (strinfo_list)
3698 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3699 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3700 && strinfo->src_port==port_src_fwd
3701 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3702 && strinfo->dest_port==port_dst_fwd)
3704 filtered_list = g_list_prepend(filtered_list, strinfo);
3707 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3708 && strinfo->src_port==port_src_rev
3709 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3710 && strinfo->dest_port==port_dst_rev)
3713 filtered_list = g_list_append(filtered_list, strinfo);
3715 ssrc_rev = strinfo->ssrc;
3718 strinfo_list = g_list_next(strinfo_list);
3721 /* if more than one reverse streams found, we let the user choose the right one */
3723 rtpstream_dlg_show(filtered_list);
3742 /****************************************************************************/
3744 rtp_analysis_init(const char *dummy _U_,void* userdata _U_)
3746 rtp_analysis_cb(NULL, NULL);
3749 /****************************************************************************/
3751 register_tap_listener_rtp_analysis(void)
3753 register_stat_cmd_arg("rtp", rtp_analysis_init,NULL);
3755 register_stat_menu_item("RTP/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3756 rtp_analysis_cb, NULL, NULL, NULL);