2 * RTP analysis addition for Wireshark
6 * Copyright 2003, Alcatel Business Systems
7 * By Lars Ruoff <lars.ruoff@gmx.net>
10 * Copyright 2003, Iskratel, Ltd, Kranj
11 * By Miha Jemec <m.jemec@iskratel.si>
13 * Graph. Copyright 2004, Verso Technology
14 * By Alejandro Vaquero <alejandro.vaquero@verso.com>
15 * Based on io_stat.c by Ronnie Sahlberg
17 * Wireshark - Network traffic analyzer
18 * By Gerald Combs <gerald@wireshark.org>
19 * Copyright 1998 Gerald Combs
21 * This program is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU General Public License
23 * as published by the Free Software Foundation; either version 2
24 * of the License, or (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
55 #include <epan/epan_dissect.h>
56 #include <epan/filesystem.h>
57 #include <epan/pint.h>
59 #include <epan/dissectors/packet-rtp.h>
60 #include <epan/rtp_pt.h>
61 #include <epan/addr_resolv.h>
62 #include <epan/stat_cmd_args.h>
63 #include <epan/strutil.h>
67 #include "../alert_box.h"
68 #include "../simple_dialog.h"
69 #include "../stat_menu.h"
70 #include "../progress_dlg.h"
72 #include "../tempfile.h"
73 #include <wsutil/file_util.h>
75 #include "gtk/gtkglobals.h"
76 #include "gtk/dlg_utils.h"
77 #include "gtk/file_dlg.h"
78 #include "gtk/gui_utils.h"
79 #include "gtk/gui_stat_menu.h"
80 #include "gtk/pixmap_save.h"
82 #include "gtk/rtp_analysis.h"
83 #include "gtk/rtp_stream.h"
84 #include "gtk/rtp_stream_dlg.h"
85 #include "gtk/stock_icons.h"
86 #include "gtk/utf8_entities.h"
88 #ifdef HAVE_LIBPORTAUDIO
89 #include "gtk/graph_analysis.h"
90 #include "gtk/voip_calls.h"
91 #include "gtk/rtp_player.h"
92 #endif /* HAVE_LIBPORTAUDIO */
107 FOREGROUND_COLOR_COL,
108 BACKGROUND_COLOR_COL,
109 N_COLUMN /* The number of columns */
111 /****************************************************************************/
114 #define NUM_GRAPH_ITEMS 100000
115 #define MAX_YSCALE 16
116 #define AUTO_MAX_YSCALE_INDEX 0
117 #define AUTO_MAX_YSCALE 0
119 #define GRAPH_FWD_JITTER 0
120 #define GRAPH_FWD_DIFF 1
121 #define GRAPH_FWD_DELTA 2
122 #define GRAPH_REV_JITTER 3
123 #define GRAPH_REV_DIFF 4
124 #define GRAPH_REV_DELTA 5
125 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
127 #define MAX_PIXELS_PER_TICK 4
128 #define DEFAULT_PIXELS_PER_TICK_INDEX 1
129 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
130 static const char *graph_descr[MAX_GRAPHS] = {"Fwd Jitter", "Fwd Difference", "Fwd Delta", "Rvr Jitter", "Rvr Difference", "Rvr Delta"};
132 #define MAX_TICK_VALUES 5
133 #define DEFAULT_TICK_INTERVAL_VALUES_INDEX 1
134 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
135 typedef struct _dialog_graph_graph_item_t {
138 } dialog_graph_graph_item_t;
140 typedef struct _dialog_graph_graph_t {
141 struct _user_data_t *ud;
142 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
145 GtkWidget *display_button;
150 } dialog_graph_graph_t;
153 typedef struct _dialog_graph_t {
154 gboolean needs_redraw;
155 gint32 interval_index; /* index into tick_interval_values_array */
156 gint32 interval; /* measurement interval in ms */
157 guint32 last_interval;
158 guint32 max_interval; /* XXX max_interval and num_items are redundant */
160 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
162 GtkWidget *draw_area;
164 GtkAdjustment *scrollbar_adjustment;
165 GtkWidget *scrollbar;
168 int pixels_per_tick_index; /* index into pixels_per_tick array */
170 int max_y_units_index; /* index into yscale_max array */
175 typedef struct _dialog_data_t {
180 GtkWidget *label_stats_fwd;
181 GtkWidget *label_stats_rev;
182 GtkWidget *selected_list;
184 GtkTreeSelection *selected_list_sel;
185 gint selected_list_row;
187 GtkWidget *save_voice_as_w;
188 GtkWidget *save_csv_as_w;
189 gint notebook_signal_id;
190 dialog_graph_t dialog_graph;
193 #define OK_TEXT "[ Ok ]"
195 /* type of error when saving voice in a file didn't succeed */
198 TAP_RTP_WRONG_LENGTH,
199 TAP_RTP_PADDING_ERROR,
201 TAP_RTP_FILE_OPEN_ERROR,
205 typedef struct _tap_rtp_save_info_t {
208 error_type_t error_type;
210 } tap_rtp_save_info_t;
213 /* structure that holds the information about the forward and reversed direction */
214 struct _info_direction {
215 tap_rtp_stat_t statinfo;
216 tap_rtp_save_info_t saveinfo;
219 #define SILENCE_PCMU (guint8)0xFF
220 #define SILENCE_PCMA (guint8)0x55
222 /* structure that holds general information about the connection
223 * and structures for both directions */
224 typedef struct _user_data_t {
225 /* tap associated data*/
227 guint16 port_src_fwd;
229 guint16 port_dst_fwd;
232 guint16 port_src_rev;
234 guint16 port_dst_rev;
237 struct _info_direction forward;
238 struct _info_direction reversed;
243 /* dialog associated data */
250 static const gchar *titles[11] = {
264 #define SAVE_FORWARD_DIRECTION_MASK 0x01
265 #define SAVE_REVERSE_DIRECTION_MASK 0x02
266 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
268 #define SAVE_NONE_FORMAT 0
269 #define SAVE_WAV_FORMAT 1
270 #define SAVE_AU_FORMAT 2
271 #define SAVE_SW_FORMAT 3
272 #define SAVE_RAW_FORMAT 4
275 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data);
276 /****************************************************************************/
277 static void enable_graph(dialog_graph_graph_t *dgg)
284 static void dialog_graph_reset(user_data_t* user_data);
288 /****************************************************************************/
291 /****************************************************************************/
292 /* when there is a [re]reading of packet's */
294 rtp_reset(void *user_data_arg)
296 user_data_t *user_data = user_data_arg;
297 user_data->forward.statinfo.first_packet = TRUE;
298 user_data->reversed.statinfo.first_packet = TRUE;
299 user_data->forward.statinfo.max_delta = 0;
300 user_data->reversed.statinfo.max_delta = 0;
301 user_data->forward.statinfo.max_jitter = 0;
302 user_data->reversed.statinfo.max_jitter = 0;
303 user_data->forward.statinfo.max_skew = 0;
304 user_data->reversed.statinfo.max_skew = 0;
305 user_data->forward.statinfo.mean_jitter = 0;
306 user_data->reversed.statinfo.mean_jitter = 0;
307 user_data->forward.statinfo.delta = 0;
308 user_data->reversed.statinfo.delta = 0;
309 user_data->forward.statinfo.diff = 0;
310 user_data->reversed.statinfo.diff = 0;
311 user_data->forward.statinfo.jitter = 0;
312 user_data->reversed.statinfo.jitter = 0;
313 user_data->forward.statinfo.skew = 0;
314 user_data->reversed.statinfo.skew = 0;
315 user_data->forward.statinfo.sumt = 0;
316 user_data->reversed.statinfo.sumt = 0;
317 user_data->forward.statinfo.sumTS = 0;
318 user_data->reversed.statinfo.sumTS = 0;
319 user_data->forward.statinfo.sumt2 = 0;
320 user_data->reversed.statinfo.sumt2 = 0;
321 user_data->forward.statinfo.sumtTS = 0;
322 user_data->reversed.statinfo.sumtTS = 0;
323 user_data->forward.statinfo.bandwidth = 0;
324 user_data->reversed.statinfo.bandwidth = 0;
325 user_data->forward.statinfo.total_bytes = 0;
326 user_data->reversed.statinfo.total_bytes = 0;
327 user_data->forward.statinfo.bw_start_index = 0;
328 user_data->reversed.statinfo.bw_start_index = 0;
329 user_data->forward.statinfo.bw_index = 0;
330 user_data->reversed.statinfo.bw_index = 0;
331 user_data->forward.statinfo.timestamp = 0;
332 user_data->reversed.statinfo.timestamp = 0;
333 user_data->forward.statinfo.max_nr = 0;
334 user_data->reversed.statinfo.max_nr = 0;
335 user_data->forward.statinfo.total_nr = 0;
336 user_data->reversed.statinfo.total_nr = 0;
337 user_data->forward.statinfo.sequence = 0;
338 user_data->reversed.statinfo.sequence = 0;
339 user_data->forward.statinfo.start_seq_nr = 0;
340 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
341 user_data->forward.statinfo.stop_seq_nr = 0;
342 user_data->reversed.statinfo.stop_seq_nr = 0;
343 user_data->forward.statinfo.cycles = 0;
344 user_data->reversed.statinfo.cycles = 0;
345 user_data->forward.statinfo.under = FALSE;
346 user_data->reversed.statinfo.under = FALSE;
347 user_data->forward.statinfo.start_time = 0;
348 user_data->reversed.statinfo.start_time = 0;
349 user_data->forward.statinfo.time = 0;
350 user_data->reversed.statinfo.time = 0;
351 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
352 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
354 user_data->forward.saveinfo.count = 0;
355 user_data->reversed.saveinfo.count = 0;
356 user_data->forward.saveinfo.saved = FALSE;
357 user_data->reversed.saveinfo.saved = FALSE;
359 /* clear the dialog box lists */
360 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
361 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
363 /* reset graph info */
364 dialog_graph_reset(user_data);
366 #ifdef HAVE_LIBPORTAUDIO
367 /* reset the RTP player */
370 /* XXX check for error at fclose? */
371 if (user_data->forward.saveinfo.fp != NULL)
372 fclose(user_data->forward.saveinfo.fp);
373 if (user_data->reversed.saveinfo.fp != NULL)
374 fclose(user_data->reversed.saveinfo.fp);
375 user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
376 if (user_data->forward.saveinfo.fp == NULL)
377 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
378 user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
379 if (user_data->reversed.saveinfo.fp == NULL)
380 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
384 /****************************************************************************/
385 static gboolean rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
387 dialog_graph_graph_item_t *it;
392 * We sometimes get called when dgg is disabled.
393 * This is a bug since the tap listener should be removed first
399 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
402 * Find which interval this is supposed to go in and store the
403 * interval index as idx
405 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
406 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
408 rtp_time = nstime_to_msec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
412 idx = (guint32)(rtp_time)/dgg->ud->dlg.dialog_graph.interval;
414 /* some sanity checks */
415 if(idx>=NUM_GRAPH_ITEMS){
419 /* update num_items */
420 if(idx > dgg->ud->dlg.dialog_graph.num_items){
421 dgg->ud->dlg.dialog_graph.num_items=idx;
422 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
426 * Find the appropriate dialog_graph_graph_item_t structure
431 * Use the max value to highlight RTP problems
433 if (value > it->value) {
436 it->flags = it->flags | statinfo->flags;
441 /****************************************************************************/
442 /* here we can redraw the output */
444 static void rtp_draw(void *prs _U_)
449 /* forward declarations */
450 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
451 double delta, double jitter, double skew ,double bandwidth, gchar *status, gboolean marker,
452 gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
454 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
455 tap_rtp_stat_t *statinfo, packet_info *pinfo,
456 const struct _rtp_info *rtpinfo);
458 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
459 tap_rtp_stat_t *statinfo,
461 const struct _rtp_info *rtpinfo);
464 /****************************************************************************/
465 /* whenever a RTP packet is seen by the tap listener */
466 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
468 user_data_t *user_data = user_data_arg;
469 const struct _rtp_info *rtpinfo = rtpinfo_arg;
470 gboolean rtp_selected = FALSE;
472 /* we ignore packets that are not displayed */
473 if (pinfo->fd->flags.passed_dfilter == 0)
475 /* also ignore RTP Version != 2 */
476 else if (rtpinfo->info_version !=2)
478 /* is it the forward direction? */
479 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
480 && CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
481 && user_data->port_src_fwd == pinfo->srcport
482 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
483 && user_data->port_dst_fwd == pinfo->destport) {
484 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
485 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]),
486 &(user_data->forward.statinfo), pinfo,
487 (guint32)(user_data->forward.statinfo.jitter*1000));
488 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]),
489 &(user_data->forward.statinfo), pinfo,
490 (guint32)(user_data->forward.statinfo.diff*1000));
491 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DELTA]),
492 &(user_data->forward.statinfo), pinfo,
493 (guint32)(user_data->forward.statinfo.delta*1000));
494 rtp_packet_add_info(user_data->dlg.list_fwd, user_data,
495 &(user_data->forward.statinfo), pinfo, rtpinfo);
496 rtp_packet_save_payload(&(user_data->forward.saveinfo),
497 &(user_data->forward.statinfo), pinfo, rtpinfo);
500 /* is it the reversed direction? */
501 else if (user_data->ssrc_rev == rtpinfo->info_sync_src
502 && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
503 && user_data->port_src_rev == pinfo->srcport
504 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
505 && user_data->port_dst_rev == pinfo->destport) {
506 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
507 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]),
508 &(user_data->reversed.statinfo), pinfo,
509 (guint32)(user_data->reversed.statinfo.jitter*1000));
510 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]),
511 &(user_data->reversed.statinfo), pinfo,
512 (guint32)(user_data->reversed.statinfo.diff*1000));
513 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DELTA]),
514 &(user_data->reversed.statinfo), pinfo,
515 (guint32)(user_data->reversed.statinfo.delta*1000));
516 rtp_packet_add_info(user_data->dlg.list_rev, user_data,
517 &(user_data->reversed.statinfo), pinfo, rtpinfo);
518 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
519 &(user_data->reversed.statinfo), pinfo, rtpinfo);
522 /* add this RTP for future listening using the RTP Player*/
523 #ifdef HAVE_LIBPORTAUDIO
525 add_rtp_packet(rtpinfo, pinfo);
532 Replaced by using the strings instead.
533 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
534 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
535 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
536 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
537 COLOR_T_EVENT g_snprintf(color_str,sizeof(color_str),"#ef8c bfff ffff");
538 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
540 /****************************************************************************/
541 /* adds statistics information from the packet to the list */
542 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
543 tap_rtp_stat_t *statinfo, packet_info *pinfo,
544 const struct _rtp_info *rtpinfo)
552 then = pinfo->fd->abs_ts.secs;
553 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
554 tm_tmp = localtime(&then);
555 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
558 tm_tmp->tm_year + 1900,
564 /* Default to using black on white text if nothing below overrides it */
565 g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
567 if (statinfo->pt == PT_CN) {
568 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
569 /* color = COLOR_CN; */
570 g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
572 else if (statinfo->pt == PT_CN_OLD) {
573 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
574 /* color = COLOR_CN; */
575 g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
577 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
578 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
579 /* color = COLOR_ERROR; */
580 g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
582 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
583 if (statinfo->flags & STAT_FLAG_PT_T_EVENT){
584 g_snprintf(status,sizeof(status),"Payload changed to PT=%u telephone/event", statinfo->pt);
586 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
588 /* color = COLOR_WARNING; */
589 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
591 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
592 g_snprintf(status,sizeof(status),"Incorrect timestamp");
593 /* color = COLOR_WARNING; */
594 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
596 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
597 && !(statinfo->flags & STAT_FLAG_FIRST)
598 && !(statinfo->flags & STAT_FLAG_PT_CN)
599 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
600 && !(statinfo->flags & STAT_FLAG_MARKER)) {
601 g_snprintf(status,sizeof(status),"Marker missing?");
602 /* color = COLOR_WARNING; */
603 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
604 }else if (statinfo->flags & STAT_FLAG_PT_T_EVENT){
605 g_snprintf(status,sizeof(status),"PT=%u telephone/event", statinfo->pt);
607 /* color = COLOR_T_EVENT; */
608 g_snprintf(color_str,sizeof(color_str),"#ef8cbfffffff");
610 if (statinfo->flags & STAT_FLAG_MARKER) {
611 /* color = COLOR_WARNING; */
612 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
614 g_snprintf(status,sizeof(status),OK_TEXT);
616 /* is this the first packet we got in this direction? */
617 if (statinfo->flags & STAT_FLAG_FIRST) {
618 add_to_list(list, user_data,
619 pinfo->fd->num, rtpinfo->info_seq_num,
626 rtpinfo->info_marker_set,
627 timeStr, pinfo->fd->pkt_len,
632 add_to_list(list, user_data,
633 pinfo->fd->num, rtpinfo->info_seq_num,
640 rtpinfo->info_marker_set,
641 timeStr, pinfo->fd->pkt_len,
648 #define MAX_SILENCE_TICKS 1000000
649 /****************************************************************************/
650 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
651 tap_rtp_stat_t *statinfo,
653 const struct _rtp_info *rtpinfo)
660 /* is this the first packet we got in this direction? */
661 if (statinfo->flags & STAT_FLAG_FIRST) {
662 if (saveinfo->fp == NULL) {
663 saveinfo->saved = FALSE;
664 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
667 saveinfo->saved = TRUE;
670 /* save the voice information */
671 /* if there was already an error, we quit */
672 if (saveinfo->saved == FALSE)
675 /* if the captured length and packet length aren't equal, we quit
676 * if also the RTP dissector thinks there is some information missing */
677 if ((pinfo->fd->pkt_len != pinfo->fd->cap_len) &&
678 (!rtpinfo->info_all_data_present)) {
679 saveinfo->saved = FALSE;
680 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
684 /* if padding bit is set, but the padding count is bigger
685 * then the whole RTP data - error with padding count */
686 if ( (rtpinfo->info_padding_set != FALSE) &&
687 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
688 saveinfo->saved = FALSE;
689 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
693 /* do we need to insert some silence? */
694 if ((rtpinfo->info_marker_set) &&
695 !(statinfo->flags & STAT_FLAG_FIRST) &&
696 !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) &&
697 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
698 /* the amount of silence should be the difference between
699 * the last timestamp and the current one minus x
700 * x should equal the amount of information in the last frame
701 * XXX not done yet */
702 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
703 rtpinfo->info_padding_count) && i < MAX_SILENCE_TICKS; i++) {
704 switch (statinfo->reg_pt) {
715 nchars=fwrite(&tmp, 1, 1, saveinfo->fp);
718 fflush(saveinfo->fp);
722 if (rtpinfo->info_payload_type == PT_CN
723 || rtpinfo->info_payload_type == PT_CN_OLD) {
725 /*all other payloads*/
727 if (!rtpinfo->info_all_data_present) {
728 /* Not all the data was captured. */
729 saveinfo->saved = FALSE;
730 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
734 /* we put the pointer at the beginning of the RTP
735 * payload, that is, at the beginning of the RTP data
736 * plus the offset of the payload from the beginning
738 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
739 nchars=fwrite(data, sizeof(unsigned char), (rtpinfo->info_payload_len - rtpinfo->info_padding_count), saveinfo->fp);
740 saveinfo->count+=(rtpinfo->info_payload_len - rtpinfo->info_padding_count);
742 fflush(saveinfo->fp);
743 saveinfo->saved = TRUE;
751 /****************************************************************************/
754 /****************************************************************************/
756 /****************************************************************************/
757 /* close the dialog window and remove the tap listener */
758 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data)
760 /* remove tap listener */
761 protect_thread_critical_region();
762 remove_tap_listener(user_data);
763 unprotect_thread_critical_region();
765 /* close and remove temporary files */
766 if (user_data->forward.saveinfo.fp != NULL)
767 fclose(user_data->forward.saveinfo.fp);
768 if (user_data->reversed.saveinfo.fp != NULL)
769 fclose(user_data->reversed.saveinfo.fp);
770 /*XXX: test for error **/
771 ws_remove(user_data->f_tempname);
772 ws_remove(user_data->r_tempname);
774 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
775 /* destroy save_voice_as window if open */
776 if (user_data->dlg.save_voice_as_w != NULL)
777 window_destroy(user_data->dlg.save_voice_as_w);
779 /* destroy graph window if open */
780 if (user_data->dlg.dialog_graph.window != NULL)
781 window_destroy(user_data->dlg.dialog_graph.window);
783 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
784 g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
786 g_free(user_data->f_tempname);
787 g_free(user_data->r_tempname);
792 /****************************************************************************/
793 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
794 GtkNotebookPage *page _U_,
796 user_data_t *user_data _U_)
798 user_data->dlg.selected_list =
799 (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
801 user_data->dlg.selected_list_row = 0;
804 /****************************************************************************/
806 static void on_list_select_row(GtkTreeSelection *selection,
807 user_data_t *user_data/*gpointer data */)
809 user_data->dlg.selected_list_sel = selection;
813 /****************************************************************************/
814 static void dialog_graph_set_title(user_data_t* user_data)
817 if (!user_data->dlg.dialog_graph.window){
820 title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
821 get_addr_name(&(user_data->ip_src_fwd)),
822 user_data->port_src_fwd,
823 get_addr_name(&(user_data->ip_dst_fwd)),
824 user_data->port_dst_fwd,
825 get_addr_name(&(user_data->ip_src_rev)),
826 user_data->port_src_rev,
827 get_addr_name(&(user_data->ip_dst_rev)),
828 user_data->port_dst_rev);
830 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
836 /****************************************************************************/
837 static void dialog_graph_reset(user_data_t* user_data)
841 user_data->dlg.dialog_graph.needs_redraw=TRUE;
842 for(i=0;i<MAX_GRAPHS;i++){
843 for(j=0;j<NUM_GRAPH_ITEMS;j++){
844 dialog_graph_graph_item_t *dggi;
845 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
850 user_data->dlg.dialog_graph.last_interval=0xffffffff;
851 user_data->dlg.dialog_graph.max_interval=0;
852 user_data->dlg.dialog_graph.num_items=0;
854 /* create the color titles near the filter buttons */
855 for(i=0;i<MAX_GRAPHS;i++){
857 if (i<(MAX_GRAPHS/2)){
858 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
859 sizeof(user_data->dlg.dialog_graph.graph[0].title),
860 "%s: %s:%u to %s:%u (SSRC=0x%X)",
862 get_addr_name(&(user_data->ip_src_fwd)),
863 user_data->port_src_fwd,
864 get_addr_name(&(user_data->ip_dst_fwd)),
865 user_data->port_dst_fwd,
866 user_data->ssrc_fwd);
869 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
870 sizeof(user_data->dlg.dialog_graph.graph[0].title),
871 "%s: %s:%u to %s:%u (SSRC=0x%X)",
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,
877 user_data->ssrc_rev);
881 dialog_graph_set_title(user_data);
884 /****************************************************************************/
885 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
887 dialog_graph_graph_item_t *it;
894 /****************************************************************************/
895 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
898 g_snprintf(buf, buf_len, "%ds",t/1000000);
899 } else if(t>=1000000){
900 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
902 g_snprintf(buf, buf_len, "%dms",t/1000);
904 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
906 g_snprintf(buf, buf_len, "%dus",t);
910 /****************************************************************************/
911 static void dialog_graph_draw(user_data_t* user_data)
914 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
915 gint32 current_interval;
916 guint32 left_x_border;
917 guint32 right_x_border;
918 guint32 top_y_border;
919 guint32 bottom_y_border;
921 int label_width, label_height;
922 int label_width_mid, label_height_mid;
923 guint32 draw_width, draw_height;
924 char label_string[15];
927 guint32 num_time_intervals;
928 guint32 max_value; /* max value of seen data */
929 guint32 max_y; /* max value of the Y scale */
931 if(!user_data->dlg.dialog_graph.needs_redraw){
934 user_data->dlg.dialog_graph.needs_redraw=FALSE;
937 * Find the length of the intervals we have data for
938 * so we know how large arrays we need to malloc()
940 num_time_intervals=user_data->dlg.dialog_graph.num_items;
941 /* if there isnt anything to do, just return */
942 if(num_time_intervals==0){
945 num_time_intervals+=1;
946 /* XXX move this check to _packet() */
947 if(num_time_intervals>NUM_GRAPH_ITEMS){
948 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
953 * find the max value so we can autoscale the y axis
956 for(i=0;i<MAX_GRAPHS;i++){
959 if(!user_data->dlg.dialog_graph.graph[i].display){
962 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
965 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
967 /* keep track of the max value we have encountered */
977 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
978 user_data->dlg.dialog_graph.draw_area->style->white_gc,
981 user_data->dlg.dialog_graph.draw_area->allocation.width,
982 user_data->dlg.dialog_graph.draw_area->allocation.height);
986 * Calculate the y scale we should use
988 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
989 max_y=yscale_max[MAX_YSCALE-1];
990 for(i=MAX_YSCALE-1;i>0;i--){
991 if(max_value<yscale_max[i]){
996 /* the user had specified an explicit y scale to use */
997 max_y=user_data->dlg.dialog_graph.max_y_units;
1001 * Calculate size of borders surrounding the plot
1002 * The border on the right side needs to be adjusted depending
1003 * on the width of the text labels.
1005 print_time_scale_string(label_string, sizeof(label_string), max_y);
1006 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1007 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1008 print_time_scale_string(label_string, sizeof(label_string), max_y*5/10);
1009 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1010 pango_layout_get_pixel_size(layout, &label_width_mid, &label_height_mid);
1011 if (label_width_mid > label_width) {
1012 label_width = label_width_mid;
1013 label_height = label_height_mid;
1017 right_x_border=label_width+20;
1019 bottom_y_border=label_height+20;
1023 * Calculate the size of the drawing area for the actual plot
1025 draw_width=user_data->dlg.dialog_graph.pixmap_width-right_x_border-left_x_border;
1026 draw_height=user_data->dlg.dialog_graph.pixmap_height-top_y_border-bottom_y_border;
1030 * Draw the y axis and labels
1031 * (we always draw the y scale with 11 ticks along the axis)
1033 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1034 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1036 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1037 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border);
1043 /* first, middle and last tick are slightly longer */
1047 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1048 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1,
1049 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10,
1050 user_data->dlg.dialog_graph.pixmap_width-right_x_border+1+xwidth,
1051 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10);
1052 /* draw the labels */
1054 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1055 pango_layout_set_text(layout, label_string, -1);
1056 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1057 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1058 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1059 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1060 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1064 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1065 pango_layout_set_text(layout, label_string, -1);
1066 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1067 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1068 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1069 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1070 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1074 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1075 pango_layout_set_text(layout, label_string, -1);
1076 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1077 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1078 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1079 user_data->dlg.dialog_graph.pixmap_width-right_x_border+15+label_width-lwidth,
1080 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border-draw_height*i/10-label_height/2,
1088 * if we have not specified the last_interval via the gui,
1089 * then just pick the current end of the capture so that is scrolls
1090 * nicely when doing live captures
1092 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1093 last_interval=user_data->dlg.dialog_graph.max_interval;
1095 last_interval=user_data->dlg.dialog_graph.last_interval;
1102 /* plot the x-scale */
1103 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);
1105 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1106 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1107 first_interval*=user_data->dlg.dialog_graph.interval;
1114 while(interval_delta<((last_interval-first_interval)/10)){
1115 interval_delta*=delta_multiplier;
1116 if(delta_multiplier==5){
1123 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1126 /* if pixels_per_tick is <5, only draw every 10 ticks */
1127 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1131 if(current_interval%interval_delta){
1137 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1138 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.draw_area->style->black_gc,
1139 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1140 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+1,
1141 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2,
1142 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+xlen+1);
1145 if(user_data->dlg.dialog_graph.interval>=1000){
1146 g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
1147 } else if(user_data->dlg.dialog_graph.interval>=100){
1148 g_snprintf(label_string, sizeof(label_string), "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1149 } else if(user_data->dlg.dialog_graph.interval>=10){
1150 g_snprintf(label_string, sizeof(label_string), "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1152 g_snprintf(label_string, sizeof(label_string), "%d.%3ds", current_interval/1000,current_interval%1000);
1154 pango_layout_set_text(layout, label_string, -1);
1155 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1156 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1157 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1158 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1159 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+20,
1171 * Draw "x" for Sequence Errors and "m" for Marks
1173 /* Draw the labels Fwd and Rev */
1174 g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Fwd",sizeof(label_string));
1175 pango_layout_set_text(layout, label_string, -1);
1176 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1177 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1178 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1179 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1180 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3,
1182 g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Rev",sizeof(label_string));
1183 pango_layout_set_text(layout, label_string, -1);
1184 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1185 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1186 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1187 user_data->dlg.dialog_graph.pixmap_width-right_x_border+33-lwidth,
1188 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+9,
1191 /* Draw the marks */
1192 for(i=MAX_GRAPHS-1;i>=0;i--){
1194 guint32 x_pos, prev_x_pos;
1196 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1197 if (!user_data->dlg.dialog_graph.graph[i].display){
1200 /* initialize prev x/y to the low left corner of the graph */
1201 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;
1203 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1204 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;
1206 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1207 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1208 g_strlcpy(label_string,"x",sizeof(label_string));
1210 g_strlcpy(label_string,"m",sizeof(label_string));
1213 pango_layout_set_text(layout, label_string, -1);
1214 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1215 gdk_draw_layout(user_data->dlg.dialog_graph.pixmap,
1216 user_data->dlg.dialog_graph.draw_area->style->black_gc,
1218 user_data->dlg.dialog_graph.pixmap_height-bottom_y_border+3+7*(i/2),
1226 g_object_unref(G_OBJECT(layout));
1229 * Loop over all graphs and draw them
1231 for(i=MAX_GRAPHS-1;i>=0;i--){
1233 guint32 x_pos, y_pos, prev_x_pos, prev_y_pos;
1234 if (!user_data->dlg.dialog_graph.graph[i].display){
1237 /* initialize prev x/y to the low left corner of the graph */
1238 prev_x_pos=draw_width-1-user_data->dlg.dialog_graph.pixels_per_tick*((last_interval-first_interval)/user_data->dlg.dialog_graph.interval+1)+left_x_border;
1239 prev_y_pos=draw_height-1+top_y_border;
1241 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1243 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;
1244 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1248 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1251 /* dont need to draw anything if the segment
1252 * is entirely above the top of the graph
1254 if( (prev_y_pos==0) && (y_pos==0) ){
1261 gdk_draw_line(user_data->dlg.dialog_graph.pixmap, user_data->dlg.dialog_graph.graph[i].gc,
1262 x_pos, draw_height-1+top_y_border,
1272 gdk_draw_pixmap(user_data->dlg.dialog_graph.draw_area->window,
1273 user_data->dlg.dialog_graph.draw_area->style->fg_gc[GTK_WIDGET_STATE(user_data->dlg.dialog_graph.draw_area)],
1274 user_data->dlg.dialog_graph.pixmap,
1277 user_data->dlg.dialog_graph.pixmap_width, user_data->dlg.dialog_graph.pixmap_height);
1280 /* update the scrollbar */
1281 user_data->dlg.dialog_graph.scrollbar_adjustment->upper=(gfloat) user_data->dlg.dialog_graph.max_interval;
1282 user_data->dlg.dialog_graph.scrollbar_adjustment->step_increment=(gfloat) ((last_interval-first_interval)/10);
1283 user_data->dlg.dialog_graph.scrollbar_adjustment->page_increment=(gfloat) (last_interval-first_interval);
1284 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1285 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (user_data->dlg.dialog_graph.max_interval/100);
1287 user_data->dlg.dialog_graph.scrollbar_adjustment->page_size=(gfloat) (last_interval-first_interval);
1289 user_data->dlg.dialog_graph.scrollbar_adjustment->value=last_interval-user_data->dlg.dialog_graph.scrollbar_adjustment->page_size;
1290 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1291 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1295 /****************************************************************************/
1296 static void dialog_graph_redraw(user_data_t* user_data)
1298 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1299 dialog_graph_draw(user_data);
1302 /****************************************************************************/
1303 static void quit(GtkWidget *widget _U_, user_data_t *user_data)
1305 user_data->dlg.dialog_graph.window = NULL;
1308 /****************************************************************************/
1309 static gint expose_event(GtkWidget *widget, GdkEventExpose *event)
1311 user_data_t *user_data;
1313 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1319 gdk_draw_pixmap(widget->window,
1320 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
1321 user_data->dlg.dialog_graph.pixmap,
1322 event->area.x, event->area.y,
1323 event->area.x, event->area.y,
1324 event->area.width, event->area.height);
1329 /****************************************************************************/
1330 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1332 user_data_t *user_data;
1334 #if GTK_CHECK_VERSION(2,6,0)
1338 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1344 if(user_data->dlg.dialog_graph.pixmap){
1345 gdk_pixmap_unref(user_data->dlg.dialog_graph.pixmap);
1346 user_data->dlg.dialog_graph.pixmap=NULL;
1349 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(widget->window,
1350 widget->allocation.width,
1351 widget->allocation.height,
1353 user_data->dlg.dialog_graph.pixmap_width=widget->allocation.width;
1354 user_data->dlg.dialog_graph.pixmap_height=widget->allocation.height;
1356 #if GTK_CHECK_VERSION(2,6,0)
1357 bt_save = g_object_get_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save");
1358 g_object_set_data(G_OBJECT(bt_save), "pixmap", user_data->dlg.dialog_graph.pixmap);
1359 gtk_widget_set_sensitive(bt_save, TRUE);
1362 gdk_draw_rectangle(user_data->dlg.dialog_graph.pixmap,
1363 widget->style->white_gc,
1366 widget->allocation.width,
1367 widget->allocation.height);
1369 /* set up the colors and the GC structs for this pixmap */
1370 for(i=0;i<MAX_GRAPHS;i++){
1371 user_data->dlg.dialog_graph.graph[i].gc=gdk_gc_new(user_data->dlg.dialog_graph.pixmap);
1372 gdk_gc_set_rgb_fg_color(user_data->dlg.dialog_graph.graph[i].gc, &user_data->dlg.dialog_graph.graph[i].color);
1375 dialog_graph_redraw(user_data);
1379 /****************************************************************************/
1380 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1382 user_data_t *user_data=(user_data_t *)data;
1385 mi=(guint32) (user_data->dlg.dialog_graph.scrollbar_adjustment->value+user_data->dlg.dialog_graph.scrollbar_adjustment->page_size);
1386 if(user_data->dlg.dialog_graph.last_interval==mi){
1389 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1390 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1394 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1396 dialog_graph_redraw(user_data);
1400 /****************************************************************************/
1401 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1403 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1404 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1405 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.draw_area), "user_data_t", user_data);
1407 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);
1409 /* signals needed to handle backing pixmap */
1410 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), NULL);
1411 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1413 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1414 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1416 /* create the associated scrollbar */
1417 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1418 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1419 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1420 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1421 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1424 /****************************************************************************/
1425 static void disable_graph(dialog_graph_graph_t *dgg)
1429 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1434 /****************************************************************************/
1435 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1437 /* this graph is not active, just update display and redraw */
1438 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1440 dialog_graph_redraw(dgg->ud);
1445 cf_retap_packets(&cfile);
1446 dialog_graph_redraw(dgg->ud);
1451 /****************************************************************************/
1452 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1458 hbox=gtk_hbox_new(FALSE, 3);
1459 gtk_container_add(GTK_CONTAINER(box), hbox);
1460 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1461 gtk_widget_show(hbox);
1463 g_snprintf(str, sizeof(str), "Graph %d", num);
1464 dgg->display_button=gtk_toggle_button_new_with_label(str);
1465 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1466 gtk_widget_show(dgg->display_button);
1467 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1468 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1470 label=gtk_label_new(dgg->title);
1471 gtk_widget_show(label);
1472 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1474 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1475 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1476 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1477 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1478 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1483 /****************************************************************************/
1484 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1491 frame=gtk_frame_new("Graphs");
1492 gtk_container_add(GTK_CONTAINER(box), frame);
1493 gtk_widget_show(frame);
1495 vbox=gtk_vbox_new(FALSE, 1);
1496 gtk_container_add(GTK_CONTAINER(frame), vbox);
1497 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1498 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1499 gtk_widget_show(vbox);
1501 for(i=0;i<MAX_GRAPHS;i++){
1502 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1505 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1506 gtk_widget_show(label);
1507 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1512 /****************************************************************************/
1513 static void yscale_select(GtkWidget *item, gpointer key)
1516 user_data_t *user_data;
1518 user_data=(user_data_t *)key;
1519 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1521 user_data->dlg.dialog_graph.max_y_units_index=i;
1522 user_data->dlg.dialog_graph.max_y_units=yscale_max[i];
1523 dialog_graph_redraw(user_data);
1526 /****************************************************************************/
1527 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1530 user_data_t *user_data;
1532 user_data=(user_data_t *)key;
1533 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1535 user_data->dlg.dialog_graph.pixels_per_tick_index=i;
1536 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[i];
1537 dialog_graph_redraw(user_data);
1540 /****************************************************************************/
1541 static void tick_interval_select(GtkWidget *item, gpointer key)
1544 user_data_t *user_data;
1546 user_data=(user_data_t *)key;
1547 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1549 user_data->dlg.dialog_graph.interval_index=i;
1550 user_data->dlg.dialog_graph.interval=tick_interval_values[i];
1551 cf_retap_packets(&cfile);
1552 dialog_graph_redraw(user_data);
1555 /****************************************************************************/
1557 create_yscale_max_menu_items(user_data_t* user_data)
1560 GtkWidget *combo_box;
1563 combo_box = gtk_combo_box_new_text ();
1565 for(i=0;i<MAX_YSCALE;i++){
1566 if(yscale_max[i]==AUTO_MAX_YSCALE){
1567 g_strlcpy(str,"Auto",sizeof(str));
1568 } else if (yscale_max[i] < 1000000) {
1569 g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1571 g_snprintf(str, sizeof(str), "%u s", yscale_max[i]/1000000);
1573 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1575 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.max_y_units_index);
1576 g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), (gpointer)user_data);
1581 /****************************************************************************/
1583 create_pixels_per_tick_menu_items(user_data_t *user_data)
1586 GtkWidget *combo_box;
1589 combo_box = gtk_combo_box_new_text ();
1591 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1592 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1593 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1595 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.pixels_per_tick_index);
1597 g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), (gpointer)user_data);
1602 /****************************************************************************/
1604 create_tick_interval_menu_items(user_data_t *user_data)
1606 GtkWidget *combo_box;
1610 combo_box = gtk_combo_box_new_text ();
1612 for(i=0;i<MAX_TICK_VALUES;i++){
1613 if(tick_interval_values[i]>=1000){
1614 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1615 } else if(tick_interval_values[i]>=100){
1616 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1617 } else if(tick_interval_values[i]>=10){
1618 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1620 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1622 gtk_combo_box_append_text (GTK_COMBO_BOX (combo_box), str);
1624 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.interval_index);
1625 g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1630 /****************************************************************************/
1631 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, GtkWidget *(*func)(user_data_t* user_data))
1635 GtkWidget *combo_box;
1637 hbox=gtk_hbox_new(FALSE, 0);
1638 gtk_container_add(GTK_CONTAINER(box), hbox);
1639 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1640 gtk_widget_show(hbox);
1642 label=gtk_label_new(name);
1643 gtk_widget_show(label);
1644 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1646 combo_box = (*func)(user_data);
1647 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1648 gtk_widget_show(combo_box);
1651 /****************************************************************************/
1652 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1654 GtkWidget *frame_vbox;
1658 frame_vbox=gtk_vbox_new(FALSE, 0);
1659 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1660 gtk_widget_show(frame_vbox);
1662 frame = gtk_frame_new("X Axis");
1663 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1664 gtk_widget_show(frame);
1666 vbox=gtk_vbox_new(FALSE, 0);
1667 gtk_container_add(GTK_CONTAINER(frame), vbox);
1668 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1669 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1670 gtk_widget_show(vbox);
1672 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1673 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1675 frame = gtk_frame_new("Y Axis");
1676 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1677 gtk_widget_show(frame);
1679 vbox=gtk_vbox_new(FALSE, 0);
1680 gtk_container_add(GTK_CONTAINER(frame), vbox);
1681 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1682 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1683 gtk_widget_show(vbox);
1685 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1690 /****************************************************************************/
1691 static void dialog_graph_init_window(user_data_t* user_data)
1695 GtkWidget *bt_close;
1696 #if GTK_CHECK_VERSION(2,6,0)
1698 GtkTooltips *tooltips = gtk_tooltips_new();
1701 /* create the main window */
1702 user_data->dlg.dialog_graph.window=dlg_window_new("I/O Graphs"); /* transient_for top_level */
1704 vbox=gtk_vbox_new(FALSE, 0);
1705 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1706 gtk_widget_show(vbox);
1708 create_draw_area(user_data, vbox);
1710 hbox=gtk_hbox_new(FALSE, 3);
1711 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1712 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1713 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1714 gtk_widget_show(hbox);
1716 create_filter_area(user_data, hbox);
1717 create_ctrl_area(user_data, hbox);
1719 dialog_graph_set_title(user_data);
1721 #if GTK_CHECK_VERSION(2,6,0)
1722 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE, NULL);
1724 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, NULL);
1726 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1727 gtk_widget_show(hbox);
1729 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1730 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1732 #if GTK_CHECK_VERSION(2,6,0)
1733 bt_save = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_SAVE);
1734 gtk_widget_set_sensitive(bt_save, FALSE);
1735 gtk_tooltips_set_tip(tooltips, bt_save, "Save the displayed graph to a file", NULL);
1736 g_signal_connect(bt_save, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
1737 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save", bt_save);
1740 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1742 gtk_widget_show(user_data->dlg.dialog_graph.window);
1743 window_present(user_data->dlg.dialog_graph.window);
1748 /****************************************************************************/
1749 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1751 if (user_data->dlg.dialog_graph.window != NULL) {
1752 /* There's already a graph window; reactivate it. */
1753 reactivate_window(user_data->dlg.dialog_graph.window);
1757 dialog_graph_init_window(user_data);
1761 /****************************************************************************/
1763 static void on_goto_bt_clicked_lst(GtkWidget *bt _U_, user_data_t *user_data _U_)
1766 GtkTreeModel *model;
1767 GtkTreeSelection *selection;
1770 selection = user_data->dlg.selected_list_sel;
1772 if (selection==NULL)
1775 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1776 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1777 cf_goto_frame(&cfile, fnumber);
1782 static void draw_stat(user_data_t *user_data);
1784 /****************************************************************************/
1785 /* re-dissects all packets */
1786 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1788 GString *error_string;
1790 /* remove tap listener */
1791 protect_thread_critical_region();
1792 remove_tap_listener(user_data);
1793 unprotect_thread_critical_region();
1795 /* register tap listener */
1796 error_string = register_tap_listener("rtp", user_data, NULL, 0,
1797 rtp_reset, rtp_packet, rtp_draw);
1798 if (error_string != NULL) {
1799 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1800 g_string_free(error_string, TRUE);
1804 /* retap all packets */
1805 cf_retap_packets(&cfile);
1807 /* draw statistics info */
1808 draw_stat(user_data);
1812 #ifdef HAVE_LIBPORTAUDIO
1813 /****************************************************************************/
1815 on_player_bt_clicked(GtkButton *button _U_, gpointer user_data _U_)
1817 /*rtp_player_init(voip_calls_get_info());*/
1818 rtp_player_init(NULL);
1820 #endif /* HAVE_LIBPORTAUDIO */
1822 static void on_next_bt_clicked_list(GtkWidget *bt _U_, user_data_t *user_data _U_)
1825 GtkTreeModel *model;
1827 GtkTreeSelection *selection;
1830 selection = user_data->dlg.selected_list_sel;
1832 if (selection==NULL)
1836 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1837 while (gtk_tree_model_iter_next (model,&iter)) {
1838 gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1839 if (strcmp(text, OK_TEXT) != 0) {
1840 gtk_tree_selection_select_iter (selection, &iter);
1841 path = gtk_tree_model_get_path(model, &iter);
1842 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1845 gtk_tree_path_free(path);
1852 if (user_data->dlg.number_of_nok>1){
1853 /* Get the first iter and select it before starting over */
1854 gtk_tree_model_get_iter_first(model, &iter);
1855 gtk_tree_selection_select_iter (selection, &iter);
1862 /****************************************************************************/
1863 /* when we want to save the information */
1864 static gboolean save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
1867 GtkWidget *rev, *forw, *both;
1868 user_data_t *user_data;
1870 GtkListStore *store;
1872 GtkTreeModel *model;
1873 gboolean more_items = TRUE;
1875 /* To Hold data from the list row */
1876 guint32 packet; /* Packet */
1877 guint16 sequence; /* Sequence */
1878 guint32 timestamp; /* timestamp */
1879 gfloat delta; /* Delta(ms) */
1880 gfloat jitter; /* Jitter(ms) */
1881 gfloat skew; /* Skew(ms) */
1882 gfloat ipbw; /* IP BW(kbps) */
1883 gboolean marker; /* Marker */
1884 char * status_str; /* Status */
1885 char * date_str; /* Date */
1886 guint length; /* Length */
1892 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
1894 /* Perhaps the user specified a directory instead of a file.
1895 * Check whether they did.
1897 if (test_for_directory(g_dest) == EISDIR) {
1898 /* It's a directory - set the file selection box to display it. */
1899 set_last_open_dir(g_dest);
1901 file_selection_set_current_folder(fc, get_last_open_dir());
1902 gtk_file_chooser_set_current_name(fc, "");
1903 return FALSE; /* run the dialog again */
1906 rev = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
1907 forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
1908 both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
1909 user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
1911 if (GTK_TOGGLE_BUTTON(forw)->active || GTK_TOGGLE_BUTTON(both)->active) {
1912 fp = ws_fopen(g_dest, "w");
1914 open_failure_alert_box(g_dest, errno, TRUE);
1916 return TRUE; /* we're done */
1919 if (GTK_TOGGLE_BUTTON(both)->active) {
1920 fprintf(fp, "Forward\n");
1922 write_failure_alert_box(g_dest, errno);
1925 return TRUE; /* we're done */
1929 for(j = 0; j < NUM_COLS; j++) {
1931 fprintf(fp,"\"%s\"",titles[j]);
1933 fprintf(fp,",\"%s\"",titles[j]);
1938 write_failure_alert_box(g_dest, errno);
1943 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
1944 store = GTK_LIST_STORE(model);
1945 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
1948 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
1949 PACKET_COLUMN, &packet,
1950 SEQUENCE_COLUMN, &sequence,
1951 TIMESTAMP_COLUMN, ×tamp,
1952 DELTA_COLUMN, &delta,
1953 JITTER_COLUMN, &jitter,
1956 MARKER_COLUMN, &marker,
1957 STATUS_COLUMN, &status_str,
1958 DATE_COLUMN, &date_str,
1959 LENGTH_COLUMN, &length,
1961 fprintf(fp, "\"%u\"", packet);
1962 fprintf(fp, ",\"%u\"", sequence);
1963 fprintf(fp, ",\"%u\"", timestamp);
1964 fprintf(fp, ",\"%.2f\"", delta);
1965 fprintf(fp, ",\"%.2f\"", jitter);
1966 fprintf(fp, ",\"%.2f\"", skew);
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,
2039 PACKET_COLUMN, &packet,
2040 SEQUENCE_COLUMN, &sequence,
2041 TIMESTAMP_COLUMN, ×tamp,
2042 DELTA_COLUMN, &delta,
2043 JITTER_COLUMN, &jitter,
2046 MARKER_COLUMN, &marker,
2047 STATUS_COLUMN, &status_str,
2048 DATE_COLUMN, &date_str,
2049 LENGTH_COLUMN, &length,
2051 fprintf(fp, "\"%u\"", packet);
2052 fprintf(fp, ",\"%u\"", sequence);
2053 fprintf(fp, ",\"%u\"", timestamp);
2054 fprintf(fp, ",\"%.2f\"", delta);
2055 fprintf(fp, ",\"%.2f\"", jitter);
2056 fprintf(fp, ",\"%.2f\"", skew);
2057 fprintf(fp, ",\"%.2f\"", ipbw);
2058 fprintf(fp, ",\"%s\"", marker? "SET" : "");
2059 fprintf(fp, ",\"%s\"", status_str);
2060 fprintf(fp, ",\"%s\"", date_str);
2061 fprintf(fp, ",\"%u\"", length);
2066 write_failure_alert_box(g_dest, errno);
2069 return TRUE; /* we're done */
2072 more_items = gtk_tree_model_iter_next (model,&iter);
2075 if (fclose(fp) == EOF) {
2076 write_failure_alert_box(g_dest, errno);
2078 return TRUE; /* we're done */
2083 return TRUE; /* we're done */
2086 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2088 user_data->dlg.save_csv_as_w = NULL;
2091 /* when the user wants to save the csv information in a file */
2092 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
2096 GtkWidget *label_format;
2097 GtkWidget *channels_label;
2098 GSList *channels_group = NULL;
2099 GtkWidget *forward_rb;
2100 GtkWidget *reversed_rb;
2103 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2104 if (user_data->dlg.save_csv_as_w != NULL) {
2105 /* There's already a Save CSV info dialog box; reactivate it. */
2106 reactivate_window(user_data->dlg.save_csv_as_w);
2110 user_data->dlg.save_csv_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2111 GTK_WINDOW(user_data->dlg.notebook),
2112 GTK_FILE_CHOOSER_ACTION_SAVE,
2113 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2114 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2116 #if GTK_CHECK_VERSION(2,8,0)
2117 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
2119 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_csv_as_w),GTK_WINDOW(user_data->dlg.window));
2121 /* Container for each row of widgets */
2122 vertb = gtk_vbox_new(FALSE, 0);
2123 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2124 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2125 gtk_widget_show (vertb);
2127 table1 = gtk_table_new (2, 4, FALSE);
2128 gtk_widget_show (table1);
2129 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2130 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2131 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2133 label_format = gtk_label_new ("Format: Comma Separated Values");
2134 gtk_widget_show (label_format);
2135 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2136 (GtkAttachOptions) (GTK_FILL),
2137 (GtkAttachOptions) (0), 0, 0);
2139 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2142 channels_label = gtk_label_new ("Channels: ");
2143 gtk_widget_show (channels_label);
2144 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2145 (GtkAttachOptions) (GTK_FILL),
2146 (GtkAttachOptions) (0), 0, 0);
2147 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2149 forward_rb = gtk_radio_button_new_with_label (NULL, "forward ");
2150 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2151 gtk_widget_show (forward_rb);
2152 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2153 (GtkAttachOptions) (GTK_FILL),
2154 (GtkAttachOptions) (0), 0, 0);
2156 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed ");
2157 gtk_widget_show (reversed_rb);
2158 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2159 (GtkAttachOptions) (GTK_FILL),
2160 (GtkAttachOptions) (0), 0, 0);
2162 both_rb = gtk_radio_button_new_with_label (channels_group, "both");
2163 gtk_widget_show (both_rb);
2164 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2165 (GtkAttachOptions) (GTK_FILL),
2166 (GtkAttachOptions) (0), 0, 0);
2168 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2170 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2171 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2172 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2173 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2175 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2176 G_CALLBACK(window_delete_event_cb), NULL);
2177 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2178 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2180 gtk_widget_show(user_data->dlg.save_csv_as_w);
2181 window_present(user_data->dlg.save_csv_as_w);
2183 /* "Run" the GtkFileChooserDialog. */
2184 /* Upon exit: If "Accept" run the OK callback. */
2185 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2186 /* Destroy the window. */
2187 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2188 /* return with a TRUE status so that the dialog window will be destroyed. */
2189 /* Trying to re-run the dialog after popping up an alert box will not work */
2190 /* since the user will not be able to dismiss the alert box. */
2191 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2192 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2194 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2195 /* GtkFileChooserDialog. */
2196 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
2197 if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
2198 break; /* we're done */
2201 window_destroy(user_data->dlg.save_csv_as_w);
2205 /****************************************************************************/
2206 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2208 /* Note that we no longer have a Save voice info dialog box. */
2209 user_data->dlg.save_voice_as_w = NULL;
2212 /****************************************************************************/
2213 /* here we save it into a file that user specified */
2214 /* XXX what about endians here? could go something wrong? */
2215 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2217 FILE *to_stream, *forw_stream, *rev_stream;
2218 size_t fwritten, rwritten;
2219 int f_rawvalue, r_rawvalue, rawvalue;
2222 guint32 f_write_silence = 0;
2223 guint32 r_write_silence = 0;
2225 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2226 gboolean stop_flag = FALSE;
2229 forw_stream = ws_fopen(user_data->f_tempname, "rb");
2230 if (forw_stream == NULL)
2232 rev_stream = ws_fopen(user_data->r_tempname, "rb");
2233 if (rev_stream == NULL) {
2234 fclose(forw_stream);
2238 /* open file for saving */
2239 to_stream = ws_fopen(dest, "wb");
2240 if (to_stream == NULL) {
2241 fclose(forw_stream);
2246 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2248 if (format == SAVE_AU_FORMAT) /* au format */
2250 /* First we write the .au header. XXX Hope this is endian independant */
2251 /* the magic word 0x2e736e64 == .snd */
2252 phtonl(pd, 0x2e736e64);
2253 nchars=fwrite(pd, 1, 4, to_stream);
2254 /* header offset == 24 bytes */
2256 nchars=fwrite(pd, 1, 4, to_stream);
2257 /* total length; it is permitted to set this to 0xffffffff */
2259 nchars=fwrite(pd, 1, 4, to_stream);
2260 /* encoding format == 16-bit linear PCM */
2262 nchars=fwrite(pd, 1, 4, to_stream);
2263 /* sample rate == 8000 Hz */
2265 nchars=fwrite(pd, 1, 4, to_stream);
2268 nchars=fwrite(pd, 1, 4, to_stream);
2272 /* only forward direction */
2273 case SAVE_FORWARD_DIRECTION_MASK: {
2274 progbar_count = user_data->forward.saveinfo.count;
2275 progbar_quantum = user_data->forward.saveinfo.count/100;
2276 while ((f_rawvalue = getc(forw_stream)) != EOF) {
2279 if((count > progbar_nextstep) && (count <= progbar_count)) {
2280 update_progress_dlg(progbar,
2281 (gfloat) count/progbar_count, "Saving");
2282 progbar_nextstep = progbar_nextstep + progbar_quantum;
2286 if (user_data->forward.statinfo.pt == PT_PCMU){
2287 sample = ulaw2linear((unsigned char)f_rawvalue);
2290 else if(user_data->forward.statinfo.pt == PT_PCMA){
2291 sample = alaw2linear((unsigned char)f_rawvalue);
2295 fclose(forw_stream);
2298 destroy_progress_dlg(progbar);
2302 fwritten = fwrite(pd, 1, 2, to_stream);
2304 fclose(forw_stream);
2307 destroy_progress_dlg(progbar);
2313 /* only reversed direction */
2314 case SAVE_REVERSE_DIRECTION_MASK: {
2315 progbar_count = user_data->reversed.saveinfo.count;
2316 progbar_quantum = user_data->reversed.saveinfo.count/100;
2317 while ((r_rawvalue = getc(rev_stream)) != EOF) {
2320 if((count > progbar_nextstep) && (count <= progbar_count)) {
2321 update_progress_dlg(progbar,
2322 (gfloat) count/progbar_count, "Saving");
2323 progbar_nextstep = progbar_nextstep + progbar_quantum;
2327 if (user_data->reversed.statinfo.pt == PT_PCMU){
2328 sample = ulaw2linear((unsigned char)r_rawvalue);
2331 else if(user_data->reversed.statinfo.pt == PT_PCMA){
2332 sample = alaw2linear((unsigned char)r_rawvalue);
2336 fclose(forw_stream);
2339 destroy_progress_dlg(progbar);
2343 rwritten = fwrite(pd, 1, 2, to_stream);
2345 fclose(forw_stream);
2348 destroy_progress_dlg(progbar);
2354 /* both directions */
2355 case SAVE_BOTH_DIRECTION_MASK: {
2356 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2357 (progbar_count = user_data->forward.saveinfo.count) :
2358 (progbar_count = user_data->reversed.saveinfo.count);
2359 progbar_quantum = progbar_count/100;
2360 /* since conversation in one way can start later than in the other one,
2361 * we have to write some silence information for one channel */
2362 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2363 f_write_silence = (guint32)
2364 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*(8000/1000));
2366 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2367 r_write_silence = (guint32)
2368 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*(8000/1000));
2373 if((count > progbar_nextstep) && (count <= progbar_count)) {
2374 update_progress_dlg(progbar,
2375 (gfloat) count/progbar_count, "Saving");
2376 progbar_nextstep = progbar_nextstep + progbar_quantum;
2379 if(f_write_silence > 0) {
2380 r_rawvalue = getc(rev_stream);
2381 switch (user_data->forward.statinfo.reg_pt) {
2383 f_rawvalue = SILENCE_PCMU;
2386 f_rawvalue = SILENCE_PCMA;
2394 else if(r_write_silence > 0) {
2395 f_rawvalue = getc(forw_stream);
2396 switch (user_data->reversed.statinfo.reg_pt) {
2398 r_rawvalue = SILENCE_PCMU;
2401 r_rawvalue = SILENCE_PCMA;
2410 f_rawvalue = getc(forw_stream);
2411 r_rawvalue = getc(rev_stream);
2413 if ((r_rawvalue == EOF) && (f_rawvalue == EOF))
2415 if ((user_data->forward.statinfo.pt == PT_PCMU) && (user_data->reversed.statinfo.pt == PT_PCMU)){
2416 sample = (ulaw2linear((unsigned char)r_rawvalue) + ulaw2linear((unsigned char)f_rawvalue)) / 2;
2419 else if((user_data->forward.statinfo.pt == PT_PCMA) && (user_data->reversed.statinfo.pt == PT_PCMA)){
2420 sample = (alaw2linear((unsigned char)r_rawvalue) + alaw2linear((unsigned char)f_rawvalue)) / 2;
2425 fclose(forw_stream);
2428 destroy_progress_dlg(progbar);
2433 rwritten = fwrite(pd, 1, 2, to_stream);
2435 fclose(forw_stream);
2438 destroy_progress_dlg(progbar);
2445 else if (format == SAVE_RAW_FORMAT) /* raw format */
2449 /* only forward direction */
2450 case SAVE_FORWARD_DIRECTION_MASK: {
2451 progbar_count = user_data->forward.saveinfo.count;
2452 progbar_quantum = user_data->forward.saveinfo.count/100;
2453 stream = forw_stream;
2456 /* only reversed direction */
2457 case SAVE_REVERSE_DIRECTION_MASK: {
2458 progbar_count = user_data->reversed.saveinfo.count;
2459 progbar_quantum = user_data->reversed.saveinfo.count/100;
2460 stream = rev_stream;
2464 fclose(forw_stream);
2467 destroy_progress_dlg(progbar);
2474 /* XXX how do you just copy the file? */
2475 while ((rawvalue = getc(stream)) != EOF) {
2478 if((count > progbar_nextstep) && (count <= progbar_count)) {
2479 update_progress_dlg(progbar,
2480 (gfloat) count/progbar_count, "Saving");
2481 progbar_nextstep = progbar_nextstep + progbar_quantum;
2485 if (putc(rawvalue, to_stream) == EOF) {
2486 fclose(forw_stream);
2489 destroy_progress_dlg(progbar);
2495 destroy_progress_dlg(progbar);
2496 fclose(forw_stream);
2503 /****************************************************************************/
2504 /* the user wants to save in a file */
2505 /* XXX support for different formats is currently commented out */
2506 static gboolean save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2509 /*GtkWidget *wav, *sw;*/
2510 GtkWidget *au, *raw;
2511 GtkWidget *rev, *forw, *both;
2512 user_data_t *user_data;
2513 gint channels, format;
2515 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2517 /* Perhaps the user specified a directory instead of a file.
2518 * Check whether they did.
2520 if (test_for_directory(g_dest) == EISDIR) {
2521 /* It's a directory - set the file selection box to display it. */
2522 set_last_open_dir(g_dest);
2524 file_selection_set_current_folder(fc, get_last_open_dir());
2525 gtk_file_chooser_set_current_name(fc, "");
2526 return FALSE; /* run the dialog again */
2530 wav = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2531 sw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2533 au = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
2534 raw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
2535 rev = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2536 forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
2537 both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
2538 user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
2540 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2541 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2542 * disable the ok button or disable the buttons for direction if only one is not ok. The
2543 * problem is if we open the save voice dialog and then click the refresh button and maybe
2544 * the state changes, so we can't save anymore. In this case we should be able to update
2545 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2548 /* we can not save in both directions */
2549 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (GTK_TOGGLE_BUTTON (both)->active)) {
2550 /* there are many combinations here, we just exit when first matches */
2551 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2552 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2553 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2554 "Can't save in a file: Unsupported codec!");
2555 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2556 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2557 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2558 "Can't save in a file: Wrong length of captured packets!");
2559 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2560 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2561 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2562 "Can't save in a file: RTP data with padding!");
2563 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2564 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2565 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2566 "Can't save in a file: Not all data in all packets was captured!");
2568 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2569 "Can't save in a file: File I/O problem!");
2571 return TRUE; /* we're done */
2573 /* we can not save forward direction */
2574 else if ((user_data->forward.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (forw)->active) ||
2575 (GTK_TOGGLE_BUTTON (both)->active))) {
2576 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2577 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2578 "Can't save forward direction in a file: Unsupported codec!");
2579 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2580 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2581 "Can't save forward direction in a file: Wrong length of captured packets!");
2582 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2583 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2584 "Can't save forward direction in a file: RTP data with padding!");
2585 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2586 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2587 "Can't save forward direction in a file: Not all data in all packets was captured!");
2589 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2590 "Can't save forward direction in a file: File I/O problem!");
2592 return TRUE; /* we're done */
2594 /* we can not save reversed direction */
2595 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((GTK_TOGGLE_BUTTON (rev)->active) ||
2596 (GTK_TOGGLE_BUTTON (both)->active))) {
2597 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2598 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2599 "Can't save reversed direction in a file: Unsupported codec!");
2600 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2601 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2602 "Can't save reversed direction in a file: Wrong length of captured packets!");
2603 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2604 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2605 "Can't save reversed direction in a file: RTP data with padding!");
2606 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2607 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2608 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2609 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2610 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2611 "Can't save reversed direction in a file: No RTP data!");
2613 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2614 "Can't save reversed direction in a file: File I/O problem!");
2616 return TRUE; /* we're done */
2620 if (GTK_TOGGLE_BUTTON (wav)->active)
2621 format = SAVE_WAV_FORMAT;
2624 if (GTK_TOGGLE_BUTTON (au)->active)
2625 format = SAVE_AU_FORMAT;
2627 else if (GTK_TOGGLE_BUTTON (sw)->active)
2628 format = SAVE_SW_FORMAT;
2630 else if (GTK_TOGGLE_BUTTON (raw)->active)
2631 format = SAVE_RAW_FORMAT;
2633 format = SAVE_NONE_FORMAT;
2635 if (GTK_TOGGLE_BUTTON (rev)->active)
2636 channels = SAVE_REVERSE_DIRECTION_MASK;
2637 else if (GTK_TOGGLE_BUTTON (both)->active)
2638 channels = SAVE_BOTH_DIRECTION_MASK;
2640 channels = SAVE_FORWARD_DIRECTION_MASK;
2642 /* direction/format validity*/
2643 if (format == SAVE_AU_FORMAT)
2645 /* make sure streams are alaw/ulaw */
2646 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != PT_PCMA) && (user_data->forward.statinfo.pt != PT_PCMU)){
2647 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2648 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2650 return TRUE; /* we're done */
2652 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != PT_PCMA) && (user_data->reversed.statinfo.pt != PT_PCMU)){
2653 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2654 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2656 return TRUE; /* we're done */
2658 /* make sure pt's don't differ */
2659 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2660 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2661 "Can't save in a file: Forward and reverse direction differ in type");
2663 return TRUE; /* we're done */
2666 else if (format == SAVE_RAW_FORMAT)
2668 /* can't save raw in both directions */
2669 if (channels == SAVE_BOTH_DIRECTION_MASK){
2670 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2671 "Can't save in a file: Unable to save raw data in both directions");
2673 return TRUE; /* we're done */
2678 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2679 "Can't save in a file: Invalid save format");
2681 return TRUE; /* we're done */
2684 if(!copy_file(g_dest, channels, format, user_data)) {
2685 /* XXX - report the error type! */
2686 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2687 "An error occurred while saving voice in a file!");
2689 return TRUE; /* we're done */
2693 return TRUE; /* we're done */
2696 /****************************************************************************/
2697 /* when the user wants to save the voice information in a file */
2698 /* XXX support for different formats is currently commented out */
2699 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
2703 GtkWidget *label_format;
2704 GtkWidget *channels_label;
2705 GSList *format_group = NULL;
2706 GSList *channels_group = NULL;
2707 GtkWidget *forward_rb;
2708 GtkWidget *reversed_rb;
2710 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2714 /* if we can't save in a file: wrong codec, cut packets or other errors */
2715 /* Should the error arise here or later when you click ok button ?
2716 * if we do it here, then we must disable the refresh button, so we don't do it here
2719 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2720 if (user_data->dlg.save_voice_as_w != NULL) {
2721 /* There's already a Save voice info dialog box; reactivate it. */
2722 reactivate_window(user_data->dlg.save_voice_as_w);
2726 /* XXX - use file_selection from dlg_utils instead! */
2727 user_data->dlg.save_voice_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2728 GTK_WINDOW(user_data->dlg.notebook),
2729 GTK_FILE_CHOOSER_ACTION_SAVE,
2730 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2731 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2733 #if GTK_CHECK_VERSION(2,8,0)
2734 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
2736 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_voice_as_w),GTK_WINDOW(user_data->dlg.window));
2738 /* Container for each row of widgets */
2739 vertb = gtk_vbox_new(FALSE, 0);
2740 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2741 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2742 gtk_widget_show (vertb);
2744 table1 = gtk_table_new (2, 4, FALSE);
2745 gtk_widget_show (table1);
2746 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2747 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2748 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2750 /*label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2751 gtk_widget_show (label_format);
2752 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2753 (GtkAttachOptions) (GTK_FILL),
2754 (GtkAttachOptions) (0), 0, 0);*/
2756 label_format = gtk_label_new ("Format: ");
2757 gtk_widget_show (label_format);
2758 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2759 (GtkAttachOptions) (GTK_FILL),
2760 (GtkAttachOptions) (0), 0, 0);
2762 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2764 raw_rb = gtk_radio_button_new_with_label (NULL, ".raw");
2765 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (raw_rb));
2766 gtk_widget_show (raw_rb);
2767 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2768 (GtkAttachOptions) (GTK_FILL),
2769 (GtkAttachOptions) (0), 0, 0);
2772 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2773 gtk_widget_show (au_rb);
2774 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2775 (GtkAttachOptions) (GTK_FILL),
2776 (GtkAttachOptions) (0), 0, 0);
2779 /* we support .au - ulaw*/
2780 wav_rb = gtk_radio_button_new_with_label (format_group, ".wav");
2781 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (wav_rb));
2782 gtk_widget_show (wav_rb);
2783 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2784 (GtkAttachOptions) (GTK_FILL),
2785 (GtkAttachOptions) (0), 0, 0);
2787 sw_rb = gtk_radio_button_new_with_label (format_group, "8 kHz, 16 bit ");
2788 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (sw_rb));
2789 gtk_widget_show (sw_rb);
2790 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2791 (GtkAttachOptions) (GTK_FILL),
2792 (GtkAttachOptions) (0), 0, 0);
2793 au_rb = gtk_radio_button_new_with_label (format_group, ".au");
2794 format_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (au_rb));
2795 gtk_widget_show (au_rb);
2796 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2797 (GtkAttachOptions) (GTK_FILL),
2798 (GtkAttachOptions) (0), 0, 0);
2801 channels_label = gtk_label_new ("Channels: ");
2802 gtk_widget_show (channels_label);
2803 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2804 (GtkAttachOptions) (GTK_FILL),
2805 (GtkAttachOptions) (0), 0, 0);
2806 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2808 forward_rb = gtk_radio_button_new_with_label (NULL, "forward ");
2809 channels_group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (forward_rb));
2810 gtk_widget_show (forward_rb);
2811 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2812 (GtkAttachOptions) (GTK_FILL),
2813 (GtkAttachOptions) (0), 0, 0);
2815 reversed_rb = gtk_radio_button_new_with_label (channels_group, "reversed ");
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 gtk_widget_show (both_rb);
2823 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2824 (GtkAttachOptions) (GTK_FILL),
2825 (GtkAttachOptions) (0), 0, 0);
2828 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
2831 /* if one direction is nok we don't allow saving
2832 XXX this is not ok since the user can click the refresh button and cause changes
2833 but we can not update this window. So we move all the decision on the time the ok
2836 if (user_data->forward.saved == FALSE) {
2837 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2838 gtk_widget_set_sensitive(forward_rb, FALSE);
2839 gtk_widget_set_sensitive(both_rb, FALSE);
2841 else if (user_data->reversed.saved == FALSE) {
2842 gtk_widget_set_sensitive(reversed_rb, FALSE);
2843 gtk_widget_set_sensitive(both_rb, FALSE);
2847 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
2848 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2849 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
2850 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2851 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2852 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2853 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2854 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2856 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2857 G_CALLBACK(window_delete_event_cb), NULL);
2858 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2859 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2861 gtk_widget_show(user_data->dlg.save_voice_as_w);
2862 window_present(user_data->dlg.save_voice_as_w);
2864 /* "Run" the GtkFileChooserDialog. */
2865 /* Upon exit: If "Accept" run the OK callback. */
2866 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2867 /* Destroy the window. */
2868 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2869 /* return with a TRUE status so that the dialog window will be destroyed. */
2870 /* Trying to re-run the dialog after popping up an alert box will not work */
2871 /* since the user will not be able to dismiss the alert box. */
2872 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2873 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2875 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2876 /* GtkFileChooserDialog. */
2877 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
2878 if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
2879 break; /* we're done */
2882 window_destroy(user_data->dlg.save_voice_as_w);
2887 /****************************************************************************/
2888 /* when we are finished with redisection, we add the label for the statistic */
2889 static void draw_stat(user_data_t *user_data)
2891 gchar label_max[300];
2892 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
2893 - user_data->forward.statinfo.start_seq_nr + 1;
2894 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
2895 - user_data->reversed.statinfo.start_seq_nr + 1;
2896 guint32 f_total_nr = user_data->forward.statinfo.total_nr;
2897 guint32 r_total_nr = user_data->reversed.statinfo.total_nr;
2898 gint32 f_lost = f_expected - f_total_nr;
2899 gint32 r_lost = r_expected - r_total_nr;
2900 double f_sumt = user_data->forward.statinfo.sumt;
2901 double f_sumTS = user_data->forward.statinfo.sumTS;
2902 double f_sumt2 = user_data->forward.statinfo.sumt2;
2903 double f_sumtTS = user_data->forward.statinfo.sumtTS;
2905 double r_sumt = user_data->reversed.statinfo.sumt;
2906 double r_sumTS = user_data->reversed.statinfo.sumTS;
2907 double r_sumt2 = user_data->reversed.statinfo.sumt2;
2908 double r_sumtTS = user_data->reversed.statinfo.sumtTS;
2909 double f_perc, r_perc;
2910 double f_clock_drift = 1.0;
2911 double r_clock_drift = 1.0;
2912 double f_duration = user_data->forward.statinfo.time - user_data->forward.statinfo.start_time;
2913 double r_duration = user_data->reversed.statinfo.time - user_data->reversed.statinfo.start_time;
2914 guint32 f_clock_rate = user_data->forward.statinfo.clock_rate;
2915 guint32 r_clock_rate = user_data->reversed.statinfo.clock_rate;
2917 if (f_clock_rate == 0){
2921 if (r_clock_rate == 0){
2926 f_perc = (double)(f_lost*100)/(double)f_expected;
2931 r_perc = (double)(r_lost*100)/(double)r_expected;
2936 if ((f_total_nr >0)&&(f_sumt2 > 0)){
2937 f_clock_drift = (f_total_nr * f_sumtTS - f_sumt * f_sumTS) / (f_total_nr * f_sumt2 - f_sumt * f_sumt);
2939 if ((r_total_nr >0)&&(r_sumt2 > 0)){
2940 r_clock_drift = (r_total_nr * r_sumtTS - r_sumt * r_sumTS) / (r_total_nr * r_sumt2 - r_sumt * r_sumt);
2942 g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
2943 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
2944 "Max skew = %.2f ms.\n"
2945 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2946 " Sequence errors = %u \n"
2947 "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
2948 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
2949 user_data->forward.statinfo.max_jitter,user_data->forward.statinfo.mean_jitter,
2950 user_data->forward.statinfo.max_skew,
2951 f_expected, f_expected, f_lost, f_perc,
2952 user_data->forward.statinfo.sequence,
2953 f_duration/1000,f_duration*(f_clock_drift-1.0),f_clock_drift*f_clock_rate,100.0*(f_clock_drift-1.0));
2955 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
2956 gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_fwd),TRUE);
2958 g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
2959 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
2960 "Max skew = %.2f ms.\n"
2961 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
2962 " Sequence errors = %u \n"
2963 "Duration %.2f 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 user_data->reversed.statinfo.max_skew,
2967 r_expected, r_expected, r_lost, r_perc,
2968 user_data->reversed.statinfo.sequence,
2969 r_duration/1000,r_duration*(r_clock_drift-1.0),r_clock_drift*r_clock_rate,100.0*(r_clock_drift-1.0));
2971 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
2972 gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_rev),TRUE);
2979 /****************************************************************************/
2980 /* append a line to list */
2981 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
2982 double delta, double jitter,double skew, double bandwidth, gchar *status, gboolean marker,
2983 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
2985 GtkListStore *list_store;
2987 if (strcmp(status, OK_TEXT) != 0) {
2988 user_data->dlg.number_of_nok++;
2991 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
2993 /* Creates a new row at position. iter will be changed to point to this new row.
2994 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
2995 * The row will be filled with the values given to this function.
2997 * should generally be preferred when inserting rows in a sorted list store.
2999 #if GTK_CHECK_VERSION(2,6,0)
3000 gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
3002 gtk_list_store_append (list_store, &user_data->dlg.iter);
3003 gtk_list_store_set (list_store, &user_data->dlg.iter,
3005 PACKET_COLUMN, number,
3006 SEQUENCE_COLUMN, seq_num,
3007 TIMESTAMP_COLUMN, timestamp,
3008 DELTA_COLUMN, delta,
3009 JITTER_COLUMN, jitter,
3011 IPBW_COLUMN, bandwidth,
3012 MARKER_COLUMN, marker,
3013 STATUS_COLUMN, (char *)status,
3014 DATE_COLUMN, (char *)timeStr,
3015 LENGTH_COLUMN, pkt_len,
3016 FOREGROUND_COLOR_COL, NULL,
3017 BACKGROUND_COLOR_COL, (char *)color_str,
3020 if(flags & STAT_FLAG_FIRST){
3021 /* Set first row as active */
3022 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
3026 /****************************************************************************
3027 * Functions needed to present values from the list
3031 /* Present boolean value */
3033 rtp_boolean_data_func (GtkTreeViewColumn *column _U_,
3034 GtkCellRenderer *renderer,
3035 GtkTreeModel *model,
3041 /* the col to get data from is in userdata */
3042 gint bool_col = GPOINTER_TO_INT(user_data);
3044 gtk_tree_model_get(model, iter, bool_col, &bool_val, -1);
3048 g_strlcpy(buf, bool_val ? "SET" : "", sizeof(buf));
3051 g_assert_not_reached();
3054 g_object_set(renderer, "text", buf, NULL);
3059 GtkWidget* create_list(user_data_t* user_data)
3062 GtkListStore *list_store;
3064 GtkTreeViewColumn *column;
3065 GtkCellRenderer *renderer;
3066 GtkTreeSortable *sortable;
3067 GtkTreeView *list_view;
3068 GtkTreeSelection *selection;
3070 /* Create the store */
3071 list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX */
3072 G_TYPE_UINT, /* Packet */
3073 G_TYPE_UINT, /* Sequence */
3074 G_TYPE_UINT, /* Time stamp */
3075 G_TYPE_FLOAT, /* Delta(ms) */
3076 G_TYPE_FLOAT, /* Filtered Jitter(ms) */
3077 G_TYPE_FLOAT, /* Skew(ms) */
3078 G_TYPE_FLOAT, /* IP BW(kbps) */
3079 G_TYPE_BOOLEAN, /* Marker */
3080 G_TYPE_STRING, /* Status */
3081 G_TYPE_STRING, /* Date */
3082 G_TYPE_UINT, /* Length */
3083 G_TYPE_STRING, /* Foreground color */
3084 G_TYPE_STRING); /* Background color */
3087 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
3089 list_view = GTK_TREE_VIEW(list);
3090 sortable = GTK_TREE_SORTABLE(list_store);
3092 #if GTK_CHECK_VERSION(2,6,0)
3093 /* Speed up the list display */
3094 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
3097 /* Setup the sortable columns */
3098 gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
3099 gtk_tree_view_set_headers_clickable(list_view, FALSE);
3101 /* The view now holds a reference. We can get rid of our own reference */
3102 g_object_unref (G_OBJECT (list_store));
3105 * Create the first column packet, associating the "text" attribute of the
3106 * cell_renderer to the first column of the model
3108 renderer = gtk_cell_renderer_text_new ();
3109 column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
3110 "text", PACKET_COLUMN,
3111 "foreground", FOREGROUND_COLOR_COL,
3112 "background", BACKGROUND_COLOR_COL,
3114 gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
3115 gtk_tree_view_column_set_resizable(column, TRUE);
3116 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3117 gtk_tree_view_column_set_min_width(column, 55);
3119 /* Add the column to the view. */
3120 gtk_tree_view_append_column (list_view, column);
3123 renderer = gtk_cell_renderer_text_new ();
3124 column = gtk_tree_view_column_new_with_attributes ("Sequence", renderer,
3125 "text", SEQUENCE_COLUMN,
3126 "foreground", FOREGROUND_COLOR_COL,
3127 "background", BACKGROUND_COLOR_COL,
3129 gtk_tree_view_column_set_sort_column_id(column, SEQUENCE_COLUMN);
3130 gtk_tree_view_column_set_resizable(column, TRUE);
3131 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3132 gtk_tree_view_column_set_min_width(column, 75);
3133 gtk_tree_view_append_column (list_view, column);
3136 Currently not visible
3138 renderer = gtk_cell_renderer_text_new ();
3139 column = gtk_tree_view_column_new_with_attributes ("Time stamp", renderer,
3140 "text", TIMESTAMP_COLUMN,
3141 "foreground", FOREGROUND_COLOR_COL,
3142 "background", BACKGROUND_COLOR_COL,
3144 gtk_tree_view_column_set_sort_column_id(column, TIMESTAMP_COLUMN);
3145 gtk_tree_view_column_set_resizable(column, TRUE);
3146 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3147 gtk_tree_view_column_set_min_width(column, 75);
3148 gtk_tree_view_append_column (list_view, column);
3151 renderer = gtk_cell_renderer_text_new ();
3152 column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
3153 "text", DELTA_COLUMN,
3154 "foreground", FOREGROUND_COLOR_COL,
3155 "background", BACKGROUND_COLOR_COL,
3158 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3159 GINT_TO_POINTER(DELTA_COLUMN), NULL);
3161 gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
3162 gtk_tree_view_column_set_resizable(column, TRUE);
3163 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3164 gtk_tree_view_column_set_min_width(column, 75);
3165 gtk_tree_view_append_column (list_view, column);
3168 renderer = gtk_cell_renderer_text_new ();
3169 column = gtk_tree_view_column_new_with_attributes ("Filtered Jitter(ms)", renderer,
3170 "text", JITTER_COLUMN,
3171 "foreground", FOREGROUND_COLOR_COL,
3172 "background", BACKGROUND_COLOR_COL,
3175 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3176 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3178 gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3179 gtk_tree_view_column_set_resizable(column, TRUE);
3180 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3181 gtk_tree_view_column_set_min_width(column, 110);
3182 gtk_tree_view_append_column (list_view, column);
3185 renderer = gtk_cell_renderer_text_new ();
3186 column = gtk_tree_view_column_new_with_attributes ("Skew(ms)", renderer,
3187 "text", SKEW_COLUMN,
3188 "foreground", FOREGROUND_COLOR_COL,
3189 "background", BACKGROUND_COLOR_COL,
3192 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3193 GINT_TO_POINTER(SKEW_COLUMN), NULL);
3195 gtk_tree_view_column_set_sort_column_id(column, SKEW_COLUMN);
3196 gtk_tree_view_column_set_resizable(column, TRUE);
3197 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3198 gtk_tree_view_column_set_min_width(column, 110);
3199 gtk_tree_view_append_column (list_view, column);
3202 renderer = gtk_cell_renderer_text_new ();
3203 column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
3204 "text", IPBW_COLUMN,
3205 "foreground", FOREGROUND_COLOR_COL,
3206 "background", BACKGROUND_COLOR_COL,
3209 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3210 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3212 gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3213 gtk_tree_view_column_set_resizable(column, TRUE);
3214 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3215 gtk_tree_view_column_set_min_width(column, 80);
3216 gtk_tree_view_append_column (list_view, column);
3219 renderer = gtk_cell_renderer_text_new ();
3220 column = gtk_tree_view_column_new_with_attributes ("Marker", renderer,
3221 "text", MARKER_COLUMN,
3222 "foreground", FOREGROUND_COLOR_COL,
3223 "background", BACKGROUND_COLOR_COL,
3226 gtk_tree_view_column_set_cell_data_func(column, renderer, rtp_boolean_data_func,
3227 GINT_TO_POINTER(MARKER_COLUMN), NULL);
3229 gtk_tree_view_column_set_sort_column_id(column, MARKER_COLUMN);
3230 gtk_tree_view_column_set_resizable(column, TRUE);
3231 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3232 gtk_tree_view_column_set_min_width(column, 60);
3233 gtk_tree_view_append_column (list_view, column);
3236 renderer = gtk_cell_renderer_text_new ();
3237 column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3238 "text", STATUS_COLUMN,
3239 "foreground", FOREGROUND_COLOR_COL,
3240 "background", BACKGROUND_COLOR_COL,
3242 gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3243 gtk_tree_view_column_set_resizable(column, TRUE);
3244 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3245 gtk_tree_view_column_set_min_width(column, 100);
3246 gtk_tree_view_append_column (list_view, column);
3248 /* Now enable the sorting of each column */
3249 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3250 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3252 /* Setup the selection handler */
3253 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3254 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3256 g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3257 G_CALLBACK (on_list_select_row),
3262 /****************************************************************************/
3263 /* Create the dialog box with all widgets */
3264 static void create_rtp_dialog(user_data_t* user_data)
3266 GtkWidget *window = NULL;
3267 GtkWidget *list_fwd;
3268 GtkWidget *list_rev;
3269 GtkWidget *label_stats_fwd;
3270 GtkWidget *label_stats_rev;
3271 GtkWidget *notebook;
3273 GtkWidget *main_vb, *page, *page_r;
3275 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3276 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3277 #ifdef HAVE_LIBPORTAUDIO
3278 GtkWidget *player_bt = NULL;
3279 #endif /* HAVE_LIBPORTAUDIO */
3280 GtkWidget *graph_bt;
3281 gchar label_forward[150];
3282 gchar label_forward_tree[150];
3283 gchar label_reverse[150];
3285 gchar str_ip_src[16];
3286 gchar str_ip_dst[16];
3288 window = dlg_window_new("Wireshark: RTP Stream Analysis"); /* transient_for top_level */
3289 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3291 /* Container for each row of widgets */
3292 main_vb = gtk_vbox_new(FALSE, 2);
3293 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3294 gtk_container_add(GTK_CONTAINER(window), main_vb);
3295 gtk_widget_show(main_vb);
3298 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), sizeof(str_ip_src));
3299 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), sizeof(str_ip_dst));
3301 g_snprintf(label_forward, sizeof(label_forward),
3302 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3303 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3305 g_snprintf(label_forward_tree, sizeof(label_forward_tree),
3306 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3307 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3310 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), sizeof(str_ip_src));
3311 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), sizeof(str_ip_dst));
3313 g_snprintf(label_reverse, sizeof(label_reverse),
3314 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3315 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3317 /* Start a notebook for flipping between sets of changes */
3318 notebook = gtk_notebook_new();
3319 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3320 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3322 user_data->dlg.notebook_signal_id =
3323 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3325 /* page for forward connection */
3326 page = gtk_vbox_new(FALSE, 8);
3327 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3329 /* direction label */
3330 label = gtk_label_new(label_forward);
3331 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3333 /* place for some statistics */
3334 label_stats_fwd = gtk_label_new("\n");
3335 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3337 /* scrolled window */
3338 scrolled_window = scrolled_window_new(NULL, NULL);
3341 list_fwd = create_list(user_data);
3342 gtk_widget_show(list_fwd);
3343 gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3344 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3345 gtk_widget_show(scrolled_window);
3348 label = gtk_label_new(" Forward Direction ");
3349 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3351 /* same page for reversed connection */
3352 page_r = gtk_vbox_new(FALSE, 8);
3353 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3354 label = gtk_label_new(label_reverse);
3355 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3356 label_stats_rev = gtk_label_new("\n");
3357 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3359 scrolled_window_r = scrolled_window_new(NULL, NULL);
3361 list_rev = create_list(user_data);
3362 gtk_widget_show(list_rev);
3363 gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3364 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3365 gtk_widget_show(scrolled_window_r);
3367 label = gtk_label_new(" Reversed Direction ");
3368 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3370 /* page for help&about or future */
3372 page_help = gtk_hbox_new(FALSE, 5);
3373 label = gtk_label_new(" Future ");
3374 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3375 frame = gtk_frame_new("");
3376 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3377 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3378 gtk_container_add(GTK_CONTAINER(frame), text);
3379 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3380 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3383 /* show all notebooks */
3384 gtk_widget_show_all(notebook);
3387 box4 = gtk_hbutton_box_new();
3388 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3389 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3390 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3391 gtk_box_set_spacing(GTK_BOX (box4), 0);
3392 gtk_widget_show(box4);
3394 voice_bt = gtk_button_new_with_label("Save payload...");
3395 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3396 gtk_widget_show(voice_bt);
3397 g_signal_connect(voice_bt, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
3399 csv_bt = gtk_button_new_with_label("Save as CSV...");
3400 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3401 gtk_widget_show(csv_bt);
3402 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3404 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3405 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3406 gtk_widget_show(refresh_bt);
3407 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3409 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3410 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3411 gtk_widget_show(goto_bt);
3412 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked_lst), user_data);
3414 graph_bt = gtk_button_new_with_label("Graph");
3415 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3416 gtk_widget_show(graph_bt);
3417 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3419 #ifdef HAVE_LIBPORTAUDIO
3420 player_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_AUDIO_PLAYER);
3421 gtk_container_add(GTK_CONTAINER(box4), player_bt);
3422 gtk_widget_show(player_bt);
3423 g_signal_connect(player_bt, "clicked", G_CALLBACK(on_player_bt_clicked), NULL);
3424 /*gtk_tooltips_set_tip (tooltips, player_bt, "Launch the RTP player to listen the audio stream", NULL);*/
3425 #endif /* HAVE_LIBPORTAUDIO */
3427 next_bt = gtk_button_new_with_label("Next non-Ok");
3428 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3429 gtk_widget_show(next_bt);
3430 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked_list), user_data);
3432 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3433 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3434 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3435 gtk_widget_show(close_bt);
3436 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3438 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3439 g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3441 gtk_widget_show(window);
3442 window_present(window);
3445 /* some widget references need to be saved for outside use */
3446 user_data->dlg.window = window;
3447 user_data->dlg.list_fwd = list_fwd;
3448 user_data->dlg.list_rev = list_rev;
3449 user_data->dlg.label_stats_fwd = label_stats_fwd;
3450 user_data->dlg.label_stats_rev = label_stats_rev;
3451 user_data->dlg.notebook = notebook;
3452 user_data->dlg.selected_list = list_fwd;
3453 user_data->dlg.number_of_nok = 0;
3456 * select the initial row
3458 gtk_widget_grab_focus(list_fwd);
3463 /****************************************************************************/
3464 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3465 const gchar* proto_field, guint32* p_result)
3468 proto_node *proto_sibling_node;
3469 header_field_info *hfssrc;
3472 finfo = PNODE_FINFO(ptree_node);
3474 g_assert(finfo && "Caller passed top of the protocol tree. Expected child node");
3476 if (hfinformation==(finfo->hfinfo)) {
3477 hfssrc = proto_registrar_get_byname(proto_field);
3480 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3481 ptree_node=ptree_node->next) {
3482 finfo=PNODE_FINFO(ptree_node);
3483 if (hfssrc==finfo->hfinfo) {
3484 if (hfinformation->type==FT_IPv4) {
3485 ipv4 = fvalue_get(&finfo->value);
3486 *p_result = ipv4_get_net_order_addr(ipv4);
3489 *p_result = fvalue_get_uinteger(&finfo->value);
3498 proto_sibling_node = ptree_node->next;
3500 if (proto_sibling_node) {
3501 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3507 /****************************************************************************/
3508 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3509 const gchar* proto_name,
3510 const gchar* proto_field,
3513 proto_node *ptree_node;
3514 header_field_info *hfinformation;
3516 hfinformation = proto_registrar_get_byname(proto_name);
3517 if (hfinformation == NULL)
3520 ptree_node = ((proto_node *)protocol_tree)->first_child;
3524 return process_node(ptree_node, hfinformation, proto_field, p_result);
3528 /****************************************************************************/
3530 address *ip_src_fwd,
3531 guint16 port_src_fwd,
3532 address *ip_dst_fwd,
3533 guint16 port_dst_fwd,
3535 address *ip_src_rev,
3536 guint16 port_src_rev,
3537 address *ip_dst_rev,
3538 guint16 port_dst_rev,
3542 user_data_t *user_data;
3545 static color_t col[MAX_GRAPHS] = {
3546 {0, 0x0000, 0x0000, 0x0000},
3547 {0, 0xffff, 0x0000, 0x0000},
3548 {0, 0x0000, 0xffff, 0x0000},
3549 {0, 0xdddd, 0xcccc, 0x6666},
3550 {0, 0x6666, 0xcccc, 0xdddd},
3551 {0, 0x0000, 0x0000, 0xffff}
3556 user_data = g_malloc(sizeof(user_data_t));
3558 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3559 user_data->port_src_fwd = port_src_fwd;
3560 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3561 user_data->port_dst_fwd = port_dst_fwd;
3562 user_data->ssrc_fwd = ssrc_fwd;
3563 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3564 user_data->port_src_rev = port_src_rev;
3565 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3566 user_data->port_dst_rev = port_dst_rev;
3567 user_data->ssrc_rev = ssrc_rev;
3570 /* file names for storing sound data */
3571 /*XXX: check for errors*/
3572 fd = create_tempfile(&tempname, "wireshark_rtp_f");
3573 user_data->f_tempname = g_strdup(tempname);
3575 fd = create_tempfile(&tempname, "wireshark_rtp_r");
3576 user_data->r_tempname = g_strdup(tempname);
3578 user_data->forward.saveinfo.fp = NULL;
3579 user_data->reversed.saveinfo.fp = NULL;
3580 user_data->dlg.save_voice_as_w = NULL;
3581 user_data->dlg.save_csv_as_w = NULL;
3582 user_data->dlg.dialog_graph.window = NULL;
3584 /* init dialog_graph */
3585 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3586 user_data->dlg.dialog_graph.interval_index=DEFAULT_TICK_INTERVAL_VALUES_INDEX;
3587 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_INTERVAL_VALUES_INDEX];
3588 user_data->dlg.dialog_graph.draw_area=NULL;
3589 user_data->dlg.dialog_graph.pixmap=NULL;
3590 user_data->dlg.dialog_graph.scrollbar=NULL;
3591 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3592 user_data->dlg.dialog_graph.pixmap_width=500;
3593 user_data->dlg.dialog_graph.pixmap_height=200;
3594 user_data->dlg.dialog_graph.pixels_per_tick_index=DEFAULT_PIXELS_PER_TICK_INDEX;
3595 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
3596 user_data->dlg.dialog_graph.max_y_units_index=AUTO_MAX_YSCALE_INDEX;
3597 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3598 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3599 user_data->dlg.dialog_graph.max_interval=0;
3600 user_data->dlg.dialog_graph.num_items=0;
3601 user_data->dlg.dialog_graph.start_time = -1;
3603 for(i=0;i<MAX_GRAPHS;i++){
3604 user_data->dlg.dialog_graph.graph[i].gc=NULL;
3605 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3606 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3607 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3608 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3609 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3610 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3611 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3614 /* create the dialog box */
3615 create_rtp_dialog(user_data);
3617 /* proceed as if the Refresh button would have been pressed */
3618 on_refresh_bt_clicked(NULL, user_data);
3621 /****************************************************************************/
3622 /* entry point from main menu */
3623 static void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3626 guint16 port_src_fwd;
3628 guint16 port_dst_fwd;
3629 guint32 ssrc_fwd = 0;
3631 guint16 port_src_rev;
3633 guint16 port_dst_rev;
3634 guint32 ssrc_rev = 0;
3635 unsigned int version_fwd;
3637 gchar filter_text[256];
3641 gboolean frame_matched;
3643 GList *strinfo_list;
3644 GList *filtered_list = NULL;
3645 rtp_stream_info_t *strinfo;
3648 /* Try to compile the filter. */
3649 g_strlcpy(filter_text,"rtp && rtp.version && rtp.ssrc && (ip || ipv6)",sizeof(filter_text));
3650 if (!dfilter_compile(filter_text, &sfcode)) {
3651 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3654 /* we load the current file into cf variable */
3656 fdata = cf->current_frame;
3658 /* we are on the selected frame now */
3660 return; /* if we exit here it's an error */
3662 /* dissect the current frame */
3663 if (!cf_read_frame(cf, fdata))
3664 return; /* error reading the frame */
3665 epan_dissect_init(&edt, TRUE, FALSE);
3666 epan_dissect_prime_dfilter(&edt, sfcode);
3667 epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3668 frame_matched = dfilter_apply_edt(sfcode, &edt);
3670 /* if it is not an rtp frame, show the rtpstream dialog */
3671 frame_matched = dfilter_apply_edt(sfcode, &edt);
3672 if (frame_matched != 1) {
3673 epan_dissect_cleanup(&edt);
3674 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3675 "You didn't choose a RTP packet!");
3679 /* ok, it is a RTP frame, so let's get the ip and port values */
3680 COPY_ADDRESS(&(ip_src_fwd), &(edt.pi.src))
3681 COPY_ADDRESS(&(ip_dst_fwd), &(edt.pi.dst))
3682 port_src_fwd = edt.pi.srcport;
3683 port_dst_fwd = edt.pi.destport;
3685 /* assume the inverse ip/port combination for the reverse direction */
3686 COPY_ADDRESS(&(ip_src_rev), &(edt.pi.dst))
3687 COPY_ADDRESS(&(ip_dst_rev), &(edt.pi.src))
3688 port_src_rev = edt.pi.destport;
3689 port_dst_rev = edt.pi.srcport;
3691 /* check if it is RTP Version 2 */
3692 if (!get_int_value_from_proto_tree(edt.tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3693 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3694 "RTP Version != 2 isn't supported!");
3698 /* now we need the SSRC value of the current frame */
3699 if (!get_int_value_from_proto_tree(edt.tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3700 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3701 "SSRC value couldn't be found!");
3705 /* Scan for rtpstream */
3707 /* search for reversed direction in the global rtp streams list */
3709 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3710 while (strinfo_list)
3712 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3713 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3714 && strinfo->src_port==port_src_fwd
3715 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3716 && strinfo->dest_port==port_dst_fwd)
3718 filtered_list = g_list_prepend(filtered_list, strinfo);
3721 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3722 && strinfo->src_port==port_src_rev
3723 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3724 && strinfo->dest_port==port_dst_rev)
3727 filtered_list = g_list_append(filtered_list, strinfo);
3729 ssrc_rev = strinfo->ssrc;
3732 strinfo_list = g_list_next(strinfo_list);
3735 /* if more than one reverse streams found, we let the user choose the right one */
3737 rtpstream_dlg_show(filtered_list);
3756 /****************************************************************************/
3758 rtp_analysis_init(const char *dummy _U_,void* userdata _U_)
3760 rtp_analysis_cb(NULL, NULL);
3763 /****************************************************************************/
3765 register_tap_listener_rtp_analysis(void)
3767 register_stat_cmd_arg("rtp", rtp_analysis_init,NULL);
3769 register_stat_menu_item("_RTP/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3770 rtp_analysis_cb, NULL, NULL, NULL);