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"
71 #include "../tempfile.h"
72 #include <wsutil/file_util.h>
74 #include "gtk/gtkglobals.h"
75 #include "gtk/dlg_utils.h"
76 #include "gtk/file_dlg.h"
77 #include "gtk/gui_utils.h"
78 #include "gtk/gui_stat_menu.h"
79 #include "gtk/pixmap_save.h"
81 #include "gtk/rtp_analysis.h"
82 #include "gtk/rtp_stream.h"
83 #include "gtk/rtp_stream_dlg.h"
84 #include "gtk/stock_icons.h"
85 #include "gtk/utf8_entities.h"
87 #ifdef HAVE_LIBPORTAUDIO
88 #include "gtk/graph_analysis.h"
89 #include "gtk/voip_calls.h"
90 #include "gtk/rtp_player.h"
91 #endif /* HAVE_LIBPORTAUDIO */
93 #include "gtk/old-gtk-compat.h"
108 FOREGROUND_COLOR_COL,
109 BACKGROUND_COLOR_COL,
110 N_COLUMN /* The number of columns */
112 /****************************************************************************/
115 #define NUM_GRAPH_ITEMS 100000
116 #define MAX_YSCALE 16
117 #define AUTO_MAX_YSCALE_INDEX 0
118 #define AUTO_MAX_YSCALE 0
120 #define GRAPH_FWD_JITTER 0
121 #define GRAPH_FWD_DIFF 1
122 #define GRAPH_FWD_DELTA 2
123 #define GRAPH_REV_JITTER 3
124 #define GRAPH_REV_DIFF 4
125 #define GRAPH_REV_DELTA 5
126 static guint32 yscale_max[MAX_YSCALE] = {AUTO_MAX_YSCALE, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000, 1000000, 2000000, 5000000, 10000000, 20000000, 50000000};
128 #define MAX_PIXELS_PER_TICK 4
129 #define DEFAULT_PIXELS_PER_TICK_INDEX 2
130 static guint32 pixels_per_tick[MAX_PIXELS_PER_TICK] = {1, 2, 5, 10};
131 static const char *graph_descr[MAX_GRAPHS] = {"Fwd Jitter", "Fwd Difference", "Fwd Delta", "Rvr Jitter", "Rvr Difference", "Rvr Delta"};
133 #define MAX_TICK_VALUES 5
134 #define DEFAULT_TICK_INTERVAL_VALUES_INDEX 1
135 static guint tick_interval_values[MAX_TICK_VALUES] = { 1, 10, 100, 1000, 10000 };
136 typedef struct _dialog_graph_graph_item_t {
139 } dialog_graph_graph_item_t;
141 typedef struct _dialog_graph_graph_t {
142 struct _user_data_t *ud;
143 dialog_graph_graph_item_t items[NUM_GRAPH_ITEMS];
146 GtkWidget *display_button;
148 #if GTK_CHECK_VERSION(3,0,0)
153 } dialog_graph_graph_t;
156 typedef struct _dialog_graph_t {
157 gboolean needs_redraw;
158 gint32 interval_index; /* index into tick_interval_values_array */
159 gint32 interval; /* measurement interval in ms */
160 guint32 last_interval;
161 guint32 max_interval; /* XXX max_interval and num_items are redundant */
163 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
165 GtkWidget *draw_area;
166 #if GTK_CHECK_VERSION(2,22,0)
167 cairo_surface_t *surface;
171 GtkAdjustment *scrollbar_adjustment;
172 GtkWidget *scrollbar;
175 int pixels_per_tick_index; /* index into pixels_per_tick array */
177 int max_y_units_index; /* index into yscale_max array */
182 typedef struct _dialog_data_t {
187 GtkWidget *label_stats_fwd;
188 GtkWidget *label_stats_rev;
189 GtkWidget *selected_list;
191 GtkTreeSelection *selected_list_sel;
192 gint selected_list_row;
194 GtkWidget *save_voice_as_w;
195 GtkWidget *save_csv_as_w;
196 gint notebook_signal_id;
197 dialog_graph_t dialog_graph;
200 #define OK_TEXT "[ Ok ]"
202 /* type of error when saving voice in a file didn't succeed */
205 TAP_RTP_WRONG_LENGTH,
206 TAP_RTP_PADDING_ERROR,
208 TAP_RTP_FILE_OPEN_ERROR,
209 TAP_RTP_FILE_WRITE_ERROR,
213 typedef struct _tap_rtp_save_info_t {
216 error_type_t error_type;
218 } tap_rtp_save_info_t;
221 /* structure that holds the information about the forward and reversed direction */
222 struct _info_direction {
223 tap_rtp_stat_t statinfo;
224 tap_rtp_save_info_t saveinfo;
227 #define SILENCE_PCMU (guint8)0xFF
228 #define SILENCE_PCMA (guint8)0x55
230 /* structure that holds general information about the connection
231 * and structures for both directions */
232 typedef struct _user_data_t {
233 /* tap associated data*/
235 guint16 port_src_fwd;
237 guint16 port_dst_fwd;
240 guint16 port_src_rev;
242 guint16 port_dst_rev;
245 struct _info_direction forward;
246 struct _info_direction reversed;
251 /* dialog associated data */
258 static const gchar *titles[11] = {
272 #define SAVE_FORWARD_DIRECTION_MASK 0x01
273 #define SAVE_REVERSE_DIRECTION_MASK 0x02
274 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
276 #define SAVE_NONE_FORMAT 0
277 #define SAVE_WAV_FORMAT 1
278 #define SAVE_AU_FORMAT 2
279 #define SAVE_SW_FORMAT 3
280 #define SAVE_RAW_FORMAT 4
283 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data);
284 /****************************************************************************/
285 static void enable_graph(dialog_graph_graph_t *dgg)
292 static void dialog_graph_reset(user_data_t* user_data);
296 /****************************************************************************/
299 /****************************************************************************/
300 /* when there is a [re]reading of packet's */
302 rtp_reset(void *user_data_arg)
304 user_data_t *user_data = user_data_arg;
305 user_data->forward.statinfo.first_packet = TRUE;
306 user_data->reversed.statinfo.first_packet = TRUE;
307 user_data->forward.statinfo.max_delta = 0;
308 user_data->reversed.statinfo.max_delta = 0;
309 user_data->forward.statinfo.max_jitter = 0;
310 user_data->reversed.statinfo.max_jitter = 0;
311 user_data->forward.statinfo.max_skew = 0;
312 user_data->reversed.statinfo.max_skew = 0;
313 user_data->forward.statinfo.mean_jitter = 0;
314 user_data->reversed.statinfo.mean_jitter = 0;
315 user_data->forward.statinfo.delta = 0;
316 user_data->reversed.statinfo.delta = 0;
317 user_data->forward.statinfo.diff = 0;
318 user_data->reversed.statinfo.diff = 0;
319 user_data->forward.statinfo.jitter = 0;
320 user_data->reversed.statinfo.jitter = 0;
321 user_data->forward.statinfo.skew = 0;
322 user_data->reversed.statinfo.skew = 0;
323 user_data->forward.statinfo.sumt = 0;
324 user_data->reversed.statinfo.sumt = 0;
325 user_data->forward.statinfo.sumTS = 0;
326 user_data->reversed.statinfo.sumTS = 0;
327 user_data->forward.statinfo.sumt2 = 0;
328 user_data->reversed.statinfo.sumt2 = 0;
329 user_data->forward.statinfo.sumtTS = 0;
330 user_data->reversed.statinfo.sumtTS = 0;
331 user_data->forward.statinfo.bandwidth = 0;
332 user_data->reversed.statinfo.bandwidth = 0;
333 user_data->forward.statinfo.total_bytes = 0;
334 user_data->reversed.statinfo.total_bytes = 0;
335 user_data->forward.statinfo.bw_start_index = 0;
336 user_data->reversed.statinfo.bw_start_index = 0;
337 user_data->forward.statinfo.bw_index = 0;
338 user_data->reversed.statinfo.bw_index = 0;
339 user_data->forward.statinfo.timestamp = 0;
340 user_data->reversed.statinfo.timestamp = 0;
341 user_data->forward.statinfo.max_nr = 0;
342 user_data->reversed.statinfo.max_nr = 0;
343 user_data->forward.statinfo.total_nr = 0;
344 user_data->reversed.statinfo.total_nr = 0;
345 user_data->forward.statinfo.sequence = 0;
346 user_data->reversed.statinfo.sequence = 0;
347 user_data->forward.statinfo.start_seq_nr = 0;
348 user_data->reversed.statinfo.start_seq_nr = 1; /* 1 is ok (for statistics in reversed direction) */
349 user_data->forward.statinfo.stop_seq_nr = 0;
350 user_data->reversed.statinfo.stop_seq_nr = 0;
351 user_data->forward.statinfo.cycles = 0;
352 user_data->reversed.statinfo.cycles = 0;
353 user_data->forward.statinfo.under = FALSE;
354 user_data->reversed.statinfo.under = FALSE;
355 user_data->forward.statinfo.start_time = 0;
356 user_data->reversed.statinfo.start_time = 0;
357 user_data->forward.statinfo.time = 0;
358 user_data->reversed.statinfo.time = 0;
359 user_data->forward.statinfo.reg_pt = PT_UNDEFINED;
360 user_data->reversed.statinfo.reg_pt = PT_UNDEFINED;
362 user_data->forward.saveinfo.count = 0;
363 user_data->reversed.saveinfo.count = 0;
364 user_data->forward.saveinfo.saved = FALSE;
365 user_data->reversed.saveinfo.saved = FALSE;
367 /* clear the dialog box lists */
368 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd))));
369 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev))));
371 /* reset graph info */
372 dialog_graph_reset(user_data);
374 #ifdef HAVE_LIBPORTAUDIO
375 /* reset the RTP player */
378 /* XXX check for error at fclose? */
379 if (user_data->forward.saveinfo.fp != NULL)
380 fclose(user_data->forward.saveinfo.fp);
381 if (user_data->reversed.saveinfo.fp != NULL)
382 fclose(user_data->reversed.saveinfo.fp);
383 user_data->forward.saveinfo.fp = ws_fopen(user_data->f_tempname, "wb");
384 if (user_data->forward.saveinfo.fp == NULL)
385 user_data->forward.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
386 user_data->reversed.saveinfo.fp = ws_fopen(user_data->r_tempname, "wb");
387 if (user_data->reversed.saveinfo.fp == NULL)
388 user_data->reversed.saveinfo.error_type = TAP_RTP_FILE_OPEN_ERROR;
392 /****************************************************************************/
393 static gboolean rtp_packet_add_graph(dialog_graph_graph_t *dgg, tap_rtp_stat_t *statinfo, packet_info *pinfo, guint32 value)
395 dialog_graph_graph_item_t *it;
400 * We sometimes get called when dgg is disabled.
401 * This is a bug since the tap listener should be removed first
407 dgg->ud->dlg.dialog_graph.needs_redraw=TRUE;
410 * Find which interval this is supposed to go in and store the
411 * interval index as idx
413 if (dgg->ud->dlg.dialog_graph.start_time == -1){ /* it is the first */
414 dgg->ud->dlg.dialog_graph.start_time = statinfo->start_time;
416 rtp_time = nstime_to_msec(&pinfo->fd->rel_ts) - dgg->ud->dlg.dialog_graph.start_time;
420 idx = (guint32)(rtp_time)/dgg->ud->dlg.dialog_graph.interval;
422 /* some sanity checks */
423 if(idx>=NUM_GRAPH_ITEMS){
427 /* update num_items */
428 if(idx > dgg->ud->dlg.dialog_graph.num_items){
429 dgg->ud->dlg.dialog_graph.num_items=idx;
430 dgg->ud->dlg.dialog_graph.max_interval=idx*dgg->ud->dlg.dialog_graph.interval;
434 * Find the appropriate dialog_graph_graph_item_t structure
439 * Use the max value to highlight RTP problems
441 if (value > it->value) {
444 it->flags = it->flags | statinfo->flags;
449 /****************************************************************************/
450 /* here we can redraw the output */
452 static void rtp_draw(void *prs _U_)
457 /* forward declarations */
458 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
459 double delta, double jitter, double skew ,double bandwidth, gchar *status, gboolean marker,
460 gchar *timeStr, guint32 pkt_len,gchar *color_str, guint32 flags);
462 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
463 tap_rtp_stat_t *statinfo, packet_info *pinfo,
464 const struct _rtp_info *rtpinfo);
466 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
467 tap_rtp_stat_t *statinfo,
469 const struct _rtp_info *rtpinfo);
472 /****************************************************************************/
473 /* whenever a RTP packet is seen by the tap listener */
474 static int rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg)
476 user_data_t *user_data = user_data_arg;
477 const struct _rtp_info *rtpinfo = rtpinfo_arg;
478 gboolean rtp_selected = FALSE;
480 /* we ignore packets that are not displayed */
481 if (pinfo->fd->flags.passed_dfilter == 0)
483 /* also ignore RTP Version != 2 */
484 else if (rtpinfo->info_version !=2)
486 /* is it the forward direction? */
487 else if (user_data->ssrc_fwd == rtpinfo->info_sync_src
488 && CMP_ADDRESS(&(user_data->ip_src_fwd), &(pinfo->net_src)) == 0
489 && user_data->port_src_fwd == pinfo->srcport
490 && CMP_ADDRESS(&(user_data->ip_dst_fwd), &(pinfo->net_dst)) == 0
491 && user_data->port_dst_fwd == pinfo->destport) {
492 rtp_packet_analyse(&(user_data->forward.statinfo), pinfo, rtpinfo);
493 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_JITTER]),
494 &(user_data->forward.statinfo), pinfo,
495 (guint32)(user_data->forward.statinfo.jitter*1000));
496 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DIFF]),
497 &(user_data->forward.statinfo), pinfo,
498 (guint32)(user_data->forward.statinfo.diff*1000));
499 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_FWD_DELTA]),
500 &(user_data->forward.statinfo), pinfo,
501 (guint32)(user_data->forward.statinfo.delta*1000));
502 rtp_packet_add_info(user_data->dlg.list_fwd, user_data,
503 &(user_data->forward.statinfo), pinfo, rtpinfo);
504 rtp_packet_save_payload(&(user_data->forward.saveinfo),
505 &(user_data->forward.statinfo), pinfo, rtpinfo);
508 /* is it the reversed direction? */
509 else if (user_data->ssrc_rev == rtpinfo->info_sync_src
510 && CMP_ADDRESS(&(user_data->ip_src_rev), &(pinfo->net_src)) == 0
511 && user_data->port_src_rev == pinfo->srcport
512 && CMP_ADDRESS(&(user_data->ip_dst_rev), &(pinfo->net_dst)) == 0
513 && user_data->port_dst_rev == pinfo->destport) {
514 rtp_packet_analyse(&(user_data->reversed.statinfo), pinfo, rtpinfo);
515 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_JITTER]),
516 &(user_data->reversed.statinfo), pinfo,
517 (guint32)(user_data->reversed.statinfo.jitter*1000));
518 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DIFF]),
519 &(user_data->reversed.statinfo), pinfo,
520 (guint32)(user_data->reversed.statinfo.diff*1000));
521 rtp_packet_add_graph(&(user_data->dlg.dialog_graph.graph[GRAPH_REV_DELTA]),
522 &(user_data->reversed.statinfo), pinfo,
523 (guint32)(user_data->reversed.statinfo.delta*1000));
524 rtp_packet_add_info(user_data->dlg.list_rev, user_data,
525 &(user_data->reversed.statinfo), pinfo, rtpinfo);
526 rtp_packet_save_payload(&(user_data->reversed.saveinfo),
527 &(user_data->reversed.statinfo), pinfo, rtpinfo);
530 /* add this RTP for future listening using the RTP Player*/
531 #ifdef HAVE_LIBPORTAUDIO
533 add_rtp_packet(rtpinfo, pinfo);
540 Replaced by using the strings instead.
541 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
542 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
543 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
544 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
545 COLOR_T_EVENT g_snprintf(color_str,sizeof(color_str),"#ef8c bfff ffff");
546 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
548 /****************************************************************************/
549 /* adds statistics information from the packet to the list */
550 static int rtp_packet_add_info(GtkWidget *list, user_data_t * user_data,
551 tap_rtp_stat_t *statinfo, packet_info *pinfo,
552 const struct _rtp_info *rtpinfo)
560 then = pinfo->fd->abs_ts.secs;
561 msecs = (guint16)(pinfo->fd->abs_ts.nsecs/1000000);
562 tm_tmp = localtime(&then);
563 g_snprintf(timeStr,sizeof(timeStr),"%02d/%02d/%04d %02d:%02d:%02d.%03d",
566 tm_tmp->tm_year + 1900,
572 /* Default to using black on white text if nothing below overrides it */
573 g_snprintf(color_str,sizeof(color_str),"#ffffffffffff");
575 if (statinfo->pt == PT_CN) {
576 g_snprintf(status,sizeof(status),"Comfort noise (PT=13, RFC 3389)");
577 /* color = COLOR_CN; */
578 g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
580 else if (statinfo->pt == PT_CN_OLD) {
581 g_snprintf(status,sizeof(status),"Comfort noise (PT=19, reserved)");
582 /* color = COLOR_CN; */
583 g_snprintf(color_str,sizeof(color_str),"#bfffbfffffff");
585 else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) {
586 g_snprintf(status,sizeof(status),"Wrong sequence nr.");
587 /* color = COLOR_ERROR; */
588 g_snprintf(color_str,sizeof(color_str),"#ffffbfffbfff");
590 else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) {
591 if (statinfo->flags & STAT_FLAG_PT_T_EVENT){
592 g_snprintf(status,sizeof(status),"Payload changed to PT=%u telephone/event", statinfo->pt);
594 g_snprintf(status,sizeof(status),"Payload changed to PT=%u", statinfo->pt);
596 /* color = COLOR_WARNING; */
597 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
599 else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) {
600 g_snprintf(status,sizeof(status),"Incorrect timestamp");
601 /* color = COLOR_WARNING; */
602 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
604 else if ((statinfo->flags & STAT_FLAG_PT_CHANGE)
605 && !(statinfo->flags & STAT_FLAG_FIRST)
606 && !(statinfo->flags & STAT_FLAG_PT_CN)
607 && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN)
608 && !(statinfo->flags & STAT_FLAG_MARKER)) {
609 g_snprintf(status,sizeof(status),"Marker missing?");
610 /* color = COLOR_WARNING; */
611 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
612 }else if (statinfo->flags & STAT_FLAG_PT_T_EVENT){
613 g_snprintf(status,sizeof(status),"PT=%u telephone/event", statinfo->pt);
615 /* color = COLOR_T_EVENT; */
616 g_snprintf(color_str,sizeof(color_str),"#ef8cbfffffff");
618 if (statinfo->flags & STAT_FLAG_MARKER) {
619 /* color = COLOR_WARNING; */
620 g_snprintf(color_str,sizeof(color_str),"#ffffdfffbfff");
622 g_snprintf(status,sizeof(status),OK_TEXT);
624 /* is this the first packet we got in this direction? */
625 if (statinfo->flags & STAT_FLAG_FIRST) {
626 add_to_list(list, user_data,
627 pinfo->fd->num, rtpinfo->info_seq_num,
634 rtpinfo->info_marker_set,
635 timeStr, pinfo->fd->pkt_len,
640 add_to_list(list, user_data,
641 pinfo->fd->num, rtpinfo->info_seq_num,
648 rtpinfo->info_marker_set,
649 timeStr, pinfo->fd->pkt_len,
656 #define MAX_SILENCE_TICKS 1000000
657 /****************************************************************************/
658 static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
659 tap_rtp_stat_t *statinfo,
661 const struct _rtp_info *rtpinfo)
668 /* is this the first packet we got in this direction? */
669 if (statinfo->flags & STAT_FLAG_FIRST) {
670 if (saveinfo->fp == NULL) {
671 saveinfo->saved = FALSE;
672 saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR;
675 saveinfo->saved = TRUE;
678 /* save the voice information */
679 /* if there was already an error, we quit */
680 if (saveinfo->saved == FALSE)
683 /* if the captured length and packet length aren't equal, we quit
684 * if also the RTP dissector thinks there is some information missing */
685 if ((pinfo->fd->pkt_len != pinfo->fd->cap_len) &&
686 (!rtpinfo->info_all_data_present)) {
687 saveinfo->saved = FALSE;
688 saveinfo->error_type = TAP_RTP_WRONG_LENGTH;
692 /* if padding bit is set, but the padding count is bigger
693 * then the whole RTP data - error with padding count */
694 if ( (rtpinfo->info_padding_set != FALSE) &&
695 (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) {
696 saveinfo->saved = FALSE;
697 saveinfo->error_type = TAP_RTP_PADDING_ERROR;
701 /* do we need to insert some silence? */
702 if ((rtpinfo->info_marker_set) &&
703 !(statinfo->flags & STAT_FLAG_FIRST) &&
704 !(statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) &&
705 (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) {
706 /* the amount of silence should be the difference between
707 * the last timestamp and the current one minus x
708 * x should equal the amount of information in the last frame
709 * XXX not done yet */
710 for(i=0; i < (statinfo->delta_timestamp - rtpinfo->info_payload_len -
711 rtpinfo->info_padding_count) && i < MAX_SILENCE_TICKS; i++) {
712 switch (statinfo->reg_pt) {
723 nchars = fwrite(&tmp, 1, 1, saveinfo->fp);
725 /* Write error or short write */
726 saveinfo->saved = FALSE;
727 saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
732 fflush(saveinfo->fp);
736 if (rtpinfo->info_payload_type == PT_CN
737 || rtpinfo->info_payload_type == PT_CN_OLD) {
739 /*all other payloads*/
741 if (!rtpinfo->info_all_data_present) {
742 /* Not all the data was captured. */
743 saveinfo->saved = FALSE;
744 saveinfo->error_type = TAP_RTP_SHORT_FRAME;
748 /* we put the pointer at the beginning of the RTP
749 * payload, that is, at the beginning of the RTP data
750 * plus the offset of the payload from the beginning
752 data = rtpinfo->info_data + rtpinfo->info_payload_offset;
753 nchars = fwrite(data, sizeof(unsigned char), (rtpinfo->info_payload_len - rtpinfo->info_padding_count), saveinfo->fp);
754 if (nchars != (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) {
755 /* Write error or short write */
756 saveinfo->saved = FALSE;
757 saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
760 saveinfo->count+=(rtpinfo->info_payload_len - rtpinfo->info_padding_count);
762 fflush(saveinfo->fp);
763 saveinfo->saved = TRUE;
771 /****************************************************************************/
774 /****************************************************************************/
776 /****************************************************************************/
777 /* close the dialog window and remove the tap listener */
778 static void on_destroy(GtkWidget *win _U_, user_data_t *user_data)
780 /* remove tap listener */
781 protect_thread_critical_region();
782 remove_tap_listener(user_data);
783 unprotect_thread_critical_region();
785 /* close and remove temporary files */
786 if (user_data->forward.saveinfo.fp != NULL)
787 fclose(user_data->forward.saveinfo.fp);
788 if (user_data->reversed.saveinfo.fp != NULL)
789 fclose(user_data->reversed.saveinfo.fp);
790 /*XXX: test for error **/
791 ws_remove(user_data->f_tempname);
792 ws_remove(user_data->r_tempname);
794 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
795 /* destroy save_voice_as window if open */
796 if (user_data->dlg.save_voice_as_w != NULL)
797 window_destroy(user_data->dlg.save_voice_as_w);
799 /* destroy graph window if open */
800 if (user_data->dlg.dialog_graph.window != NULL)
801 window_destroy(user_data->dlg.dialog_graph.window);
803 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows is destroy and cause an exception using GTK1*/
804 g_signal_handler_disconnect(user_data->dlg.notebook, user_data->dlg.notebook_signal_id);
806 g_free(user_data->f_tempname);
807 g_free(user_data->r_tempname);
812 /****************************************************************************/
813 static void on_notebook_switch_page(GtkNotebook *notebook _U_,
816 user_data_t *user_data _U_)
818 user_data->dlg.selected_list =
819 (page_num==0) ? user_data->dlg.list_fwd : user_data->dlg.list_rev ;
821 user_data->dlg.selected_list_row = 0;
824 /****************************************************************************/
826 static void on_list_select_row(GtkTreeSelection *selection,
827 user_data_t *user_data/*gpointer data */)
829 user_data->dlg.selected_list_sel = selection;
833 /****************************************************************************/
834 static void dialog_graph_set_title(user_data_t* user_data)
837 if (!user_data->dlg.dialog_graph.window){
840 title = g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
841 get_addr_name(&(user_data->ip_src_fwd)),
842 user_data->port_src_fwd,
843 get_addr_name(&(user_data->ip_dst_fwd)),
844 user_data->port_dst_fwd,
845 get_addr_name(&(user_data->ip_src_rev)),
846 user_data->port_src_rev,
847 get_addr_name(&(user_data->ip_dst_rev)),
848 user_data->port_dst_rev);
850 gtk_window_set_title(GTK_WINDOW(user_data->dlg.dialog_graph.window), title);
856 /****************************************************************************/
857 static void dialog_graph_reset(user_data_t* user_data)
861 user_data->dlg.dialog_graph.needs_redraw=TRUE;
862 for(i=0;i<MAX_GRAPHS;i++){
863 for(j=0;j<NUM_GRAPH_ITEMS;j++){
864 dialog_graph_graph_item_t *dggi;
865 dggi=&user_data->dlg.dialog_graph.graph[i].items[j];
870 user_data->dlg.dialog_graph.last_interval=0xffffffff;
871 user_data->dlg.dialog_graph.max_interval=0;
872 user_data->dlg.dialog_graph.num_items=0;
874 /* create the color titles near the filter buttons */
875 for(i=0;i<MAX_GRAPHS;i++){
877 if (i<(MAX_GRAPHS/2)){
878 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
879 sizeof(user_data->dlg.dialog_graph.graph[0].title),
880 "%s: %s:%u to %s:%u (SSRC=0x%X)",
882 get_addr_name(&(user_data->ip_src_fwd)),
883 user_data->port_src_fwd,
884 get_addr_name(&(user_data->ip_dst_fwd)),
885 user_data->port_dst_fwd,
886 user_data->ssrc_fwd);
889 g_snprintf(user_data->dlg.dialog_graph.graph[i].title,
890 sizeof(user_data->dlg.dialog_graph.graph[0].title),
891 "%s: %s:%u to %s:%u (SSRC=0x%X)",
893 get_addr_name(&(user_data->ip_src_rev)),
894 user_data->port_src_rev,
895 get_addr_name(&(user_data->ip_dst_rev)),
896 user_data->port_dst_rev,
897 user_data->ssrc_rev);
901 dialog_graph_set_title(user_data);
904 /****************************************************************************/
905 static guint32 get_it_value(dialog_graph_graph_t *dgg, int idx)
907 dialog_graph_graph_item_t *it;
914 /****************************************************************************/
915 static void print_time_scale_string(char *buf, int buf_len, guint32 t)
918 g_snprintf(buf, buf_len, "%ds",t/1000000);
919 } else if(t>=1000000){
920 g_snprintf(buf, buf_len, "%d.%03ds",t/1000000,(t%1000000)/1000);
922 g_snprintf(buf, buf_len, "%dms",t/1000);
924 g_snprintf(buf, buf_len, "%d.%03dms",t/1000,t%1000);
926 g_snprintf(buf, buf_len, "%dus",t);
930 /****************************************************************************/
931 static void dialog_graph_draw(user_data_t* user_data)
934 guint32 last_interval, first_interval, interval_delta, delta_multiplier;
935 gint32 current_interval;
936 guint32 left_x_border;
937 guint32 right_x_border;
938 guint32 top_y_border;
939 guint32 bottom_y_border;
941 int label_width, label_height;
942 int label_width_mid, label_height_mid;
943 guint32 draw_width, draw_height;
944 char label_string[15];
945 GtkAllocation widget_alloc;
949 guint32 num_time_intervals;
950 guint32 max_value; /* max value of seen data */
951 guint32 max_y; /* max value of the Y scale */
953 if(!user_data->dlg.dialog_graph.needs_redraw){
956 user_data->dlg.dialog_graph.needs_redraw=FALSE;
959 * Find the length of the intervals we have data for
960 * so we know how large arrays we need to malloc()
962 num_time_intervals=user_data->dlg.dialog_graph.num_items;
963 /* if there isnt anything to do, just return */
964 if(num_time_intervals==0){
967 num_time_intervals+=1;
968 /* XXX move this check to _packet() */
969 if(num_time_intervals>NUM_GRAPH_ITEMS){
970 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "RTP Graph error. There are too many entries, bailing out");
975 * find the max value so we can autoscale the y axis
978 for(i=0;i<MAX_GRAPHS;i++){
981 if(!user_data->dlg.dialog_graph.graph[i].display){
984 for(idx=0;(guint32) (idx) < num_time_intervals;idx++){
987 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], idx);
989 /* keep track of the max value we have encountered */
999 #if GTK_CHECK_VERSION(2,22,0)
1000 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1002 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1004 cairo_set_source_rgb (cr, 1, 1, 1);
1005 gtk_widget_get_allocation(user_data->dlg.dialog_graph.draw_area, &widget_alloc);
1006 cairo_rectangle (cr,
1010 widget_alloc.height);
1015 * Calculate the y scale we should use
1017 if(user_data->dlg.dialog_graph.max_y_units==AUTO_MAX_YSCALE){
1018 max_y=yscale_max[MAX_YSCALE-1];
1019 for(i=MAX_YSCALE-1;i>0;i--){
1020 if(max_value<yscale_max[i]){
1021 max_y=yscale_max[i];
1025 /* the user had specified an explicit y scale to use */
1026 max_y=user_data->dlg.dialog_graph.max_y_units;
1030 * Calculate size of borders surrounding the plot
1031 * The border on the right side needs to be adjusted depending
1032 * on the width of the text labels.
1034 print_time_scale_string(label_string, sizeof(label_string), max_y);
1035 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1036 pango_layout_get_pixel_size(layout, &label_width, &label_height);
1037 print_time_scale_string(label_string, sizeof(label_string), max_y*5/10);
1038 layout = gtk_widget_create_pango_layout(user_data->dlg.dialog_graph.draw_area, label_string);
1039 pango_layout_get_pixel_size(layout, &label_width_mid, &label_height_mid);
1040 if (label_width_mid > label_width) {
1041 label_width = label_width_mid;
1042 label_height = label_height_mid;
1046 right_x_border=label_width+20;
1048 bottom_y_border=label_height+20;
1052 * Calculate the size of the drawing area for the actual plot
1054 draw_width=user_data->dlg.dialog_graph.surface_width-right_x_border-left_x_border;
1055 draw_height=user_data->dlg.dialog_graph.surface_height-top_y_border-bottom_y_border;
1059 * Draw the y axis and labels
1060 * (we always draw the y scale with 11 ticks along the axis)
1062 #if GTK_CHECK_VERSION(2,22,0)
1063 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1065 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1067 cairo_set_line_width (cr, 1.0);
1068 cairo_move_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5, top_y_border+0.5);
1069 cairo_line_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+0.5);
1078 /* first, middle and last tick are slightly longer */
1082 #if GTK_CHECK_VERSION(2,22,0)
1083 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1085 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1087 cairo_set_line_width (cr, 1.0);
1089 user_data->dlg.dialog_graph.surface_width-right_x_border+1.5,
1090 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10+0.5);
1093 user_data->dlg.dialog_graph.surface_width-right_x_border+1.5+xwidth,
1094 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10+0.5);
1097 /* draw the labels */
1099 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1100 pango_layout_set_text(layout, label_string, -1);
1101 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1102 #if GTK_CHECK_VERSION(2,22,0)
1103 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1105 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1108 user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
1109 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
1110 pango_cairo_show_layout (cr, layout);
1115 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1116 pango_layout_set_text(layout, label_string, -1);
1117 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1118 #if GTK_CHECK_VERSION(2,22,0)
1119 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1121 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1124 user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
1125 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
1126 pango_cairo_show_layout (cr, layout);
1131 print_time_scale_string(label_string, sizeof(label_string), (max_y*i/10));
1132 pango_layout_set_text(layout, label_string, -1);
1133 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1134 #if GTK_CHECK_VERSION(2,22,0)
1135 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1137 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1140 user_data->dlg.dialog_graph.surface_width-right_x_border+15+label_width-lwidth,
1141 user_data->dlg.dialog_graph.surface_height-bottom_y_border-draw_height*i/10-label_height/2);
1142 pango_cairo_show_layout (cr, layout);
1151 * if we have not specified the last_interval via the gui,
1152 * then just pick the current end of the capture so that is scrolls
1153 * nicely when doing live captures
1155 if(user_data->dlg.dialog_graph.last_interval==0xffffffff){
1156 last_interval=user_data->dlg.dialog_graph.max_interval;
1158 last_interval=user_data->dlg.dialog_graph.last_interval;
1165 /* plot the x-scale */
1166 #if GTK_CHECK_VERSION(2,22,0)
1167 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1169 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1171 cairo_set_line_width (cr, 1.0);
1172 cairo_move_to(cr, left_x_border+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
1173 cairo_line_to(cr, user_data->dlg.dialog_graph.surface_width-right_x_border+1.5,user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
1177 if((last_interval/user_data->dlg.dialog_graph.interval)>draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1){
1178 first_interval=(last_interval/user_data->dlg.dialog_graph.interval)-draw_width/user_data->dlg.dialog_graph.pixels_per_tick+1;
1179 first_interval*=user_data->dlg.dialog_graph.interval;
1186 while(interval_delta<((last_interval-first_interval)/10)){
1187 interval_delta*=delta_multiplier;
1188 if(delta_multiplier==5){
1195 for(current_interval=last_interval;current_interval>(gint32)first_interval;current_interval=current_interval-user_data->dlg.dialog_graph.interval){
1198 /* if pixels_per_tick is <5, only draw every 10 ticks */
1199 if((user_data->dlg.dialog_graph.pixels_per_tick<10) && (current_interval%(10*user_data->dlg.dialog_graph.interval))){
1203 if(current_interval%interval_delta){
1209 x=draw_width+left_x_border-((last_interval-current_interval)/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.pixels_per_tick;
1210 #if GTK_CHECK_VERSION(2,22,0)
1211 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1213 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1215 cairo_set_line_width (cr, 1.0);
1216 cairo_move_to(cr, x-1-user_data->dlg.dialog_graph.pixels_per_tick/2+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+1.5);
1217 cairo_line_to(cr, x-1-user_data->dlg.dialog_graph.pixels_per_tick/2+0.5, user_data->dlg.dialog_graph.surface_height-bottom_y_border+xlen+1.5);
1222 if(user_data->dlg.dialog_graph.interval>=1000){
1223 g_snprintf(label_string, sizeof(label_string), "%ds", current_interval/1000);
1224 } else if(user_data->dlg.dialog_graph.interval>=100){
1225 g_snprintf(label_string, sizeof(label_string), "%d.%1ds", current_interval/1000,(current_interval/100)%10);
1226 } else if(user_data->dlg.dialog_graph.interval>=10){
1227 g_snprintf(label_string, sizeof(label_string), "%d.%2ds", current_interval/1000,(current_interval/10)%100);
1229 g_snprintf(label_string, sizeof(label_string), "%d.%3ds", current_interval/1000,current_interval%1000);
1231 pango_layout_set_text(layout, label_string, -1);
1232 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1233 #if GTK_CHECK_VERSION(2,22,0)
1234 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1236 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1239 x-1-user_data->dlg.dialog_graph.pixels_per_tick/2-lwidth/2,
1240 user_data->dlg.dialog_graph.surface_height-bottom_y_border+20);
1241 pango_cairo_show_layout (cr, layout);
1254 * Draw "x" for Sequence Errors and "m" for Marks
1256 /* Draw the labels Fwd and Rev */
1257 g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Fwd",sizeof(label_string));
1258 pango_layout_set_text(layout, label_string, -1);
1259 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1260 #if GTK_CHECK_VERSION(2,22,0)
1261 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1263 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1266 user_data->dlg.dialog_graph.surface_width-right_x_border+33-lwidth,
1267 user_data->dlg.dialog_graph.surface_height-bottom_y_border+3);
1268 pango_cairo_show_layout (cr, layout);
1272 g_strlcpy(label_string, UTF8_LEFTWARDS_ARROW "Rev",sizeof(label_string));
1273 pango_layout_set_text(layout, label_string, -1);
1274 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1275 #if GTK_CHECK_VERSION(2,22,0)
1276 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1278 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1281 user_data->dlg.dialog_graph.surface_width-right_x_border+33-lwidth,
1282 user_data->dlg.dialog_graph.surface_height-bottom_y_border+3+9);
1283 pango_cairo_show_layout (cr, layout);
1287 /* Draw the marks */
1288 for(i=MAX_GRAPHS-1;i>=0;i--){
1290 guint32 x_pos/*, prev_x_pos*/;
1292 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1293 if (!user_data->dlg.dialog_graph.graph[i].display){
1296 /* initialize prev x/y to the low left corner of the graph */
1297 /*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;*/
1299 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1300 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;
1302 if(user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & (STAT_FLAG_WRONG_SEQ|STAT_FLAG_MARKER)){
1303 if (user_data->dlg.dialog_graph.graph[i].items[interval/user_data->dlg.dialog_graph.interval].flags & STAT_FLAG_WRONG_SEQ){
1304 g_strlcpy(label_string,"x",sizeof(label_string));
1306 g_strlcpy(label_string,"m",sizeof(label_string));
1309 pango_layout_set_text(layout, label_string, -1);
1310 pango_layout_get_pixel_size(layout, &lwidth, NULL);
1311 #if GTK_CHECK_VERSION(2,22,0)
1312 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1314 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1318 user_data->dlg.dialog_graph.surface_height-bottom_y_border+3+7*(i/2));
1319 pango_cairo_show_layout (cr, layout);
1325 /*prev_x_pos=x_pos;*/
1329 g_object_unref(G_OBJECT(layout));
1332 * Loop over all graphs and draw them
1334 for(i=MAX_GRAPHS-1;i>=0;i--){
1336 guint32 x_pos, y_pos, /*prev_x_pos,*/ prev_y_pos;
1337 if (!user_data->dlg.dialog_graph.graph[i].display){
1340 /* initialize prev x/y to the low left corner of the graph */
1341 /*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;*/
1342 prev_y_pos=draw_height-1+top_y_border;
1344 for(interval=first_interval+user_data->dlg.dialog_graph.interval;interval<=last_interval;interval+=user_data->dlg.dialog_graph.interval){
1346 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;
1347 val=get_it_value(&user_data->dlg.dialog_graph.graph[i], interval/user_data->dlg.dialog_graph.interval);
1351 y_pos=draw_height-1-(val*draw_height)/max_y+top_y_border;
1354 /* dont need to draw anything if the segment
1355 * is entirely above the top of the graph
1357 if( (prev_y_pos==0) && (y_pos==0) ){
1359 /*prev_x_pos=x_pos;*/
1364 #if GTK_CHECK_VERSION(2,22,0)
1365 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1367 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1369 gdk_cairo_set_source_color (cr, &user_data->dlg.dialog_graph.graph[i].color);
1370 cairo_set_line_width (cr, 1.0);
1371 cairo_move_to(cr, x_pos+0.5, draw_height-1+top_y_border+0.5);
1372 cairo_line_to(cr, x_pos+0.5, y_pos+0.5);
1378 /*prev_x_pos=x_pos;*/
1382 cr = gdk_cairo_create (gtk_widget_get_window(user_data->dlg.dialog_graph.draw_area));
1384 #if GTK_CHECK_VERSION(2,22,0)
1385 cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1387 gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1389 cairo_rectangle (cr, 0, 0, user_data->dlg.dialog_graph.surface_width, user_data->dlg.dialog_graph.surface_height);
1394 /* update the scrollbar */
1395 gtk_adjustment_set_upper(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) user_data->dlg.dialog_graph.max_interval);
1396 gtk_adjustment_set_step_increment(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) ((last_interval-first_interval)/10));
1397 gtk_adjustment_set_page_increment(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (last_interval-first_interval));
1398 if((last_interval-first_interval)*100 < user_data->dlg.dialog_graph.max_interval){
1399 gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (user_data->dlg.dialog_graph.max_interval/100));
1401 gtk_adjustment_set_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment, (gfloat) (last_interval-first_interval));
1403 gtk_adjustment_set_value(user_data->dlg.dialog_graph.scrollbar_adjustment, last_interval - gtk_adjustment_get_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment));
1404 gtk_adjustment_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1405 gtk_adjustment_value_changed(user_data->dlg.dialog_graph.scrollbar_adjustment);
1409 /****************************************************************************/
1410 static void dialog_graph_redraw(user_data_t* user_data)
1412 user_data->dlg.dialog_graph.needs_redraw=TRUE;
1413 dialog_graph_draw(user_data);
1416 /****************************************************************************/
1417 static void quit(GtkWidget *widget _U_, user_data_t *user_data)
1419 GtkWidget *bt_save = g_object_get_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save");
1420 surface_info_t *surface_info = g_object_get_data(G_OBJECT(bt_save), "surface-info");
1422 g_free(surface_info);
1423 user_data->dlg.dialog_graph.window = NULL;
1426 /****************************************************************************/
1427 #if GTK_CHECK_VERSION(3,0,0)
1428 static gboolean draw_area_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
1430 user_data_t *user_data = data;
1431 GtkAllocation allocation;
1433 gtk_widget_get_allocation (widget, &allocation);
1435 cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1436 cairo_rectangle (cr, 0, 0, allocation.width, allocation.height);
1442 static gint expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer data)
1444 user_data_t *user_data = data;
1445 cairo_t *cr = gdk_cairo_create (gtk_widget_get_window(widget));
1448 #if GTK_CHECK_VERSION(2,22,0)
1449 cairo_set_source_surface (cr, user_data->dlg.dialog_graph.surface, 0, 0);
1451 gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1453 cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
1461 /****************************************************************************/
1462 static gint configure_event(GtkWidget *widget, GdkEventConfigure *event _U_)
1464 user_data_t *user_data;
1466 GtkAllocation widget_alloc;
1468 #if GTK_CHECK_VERSION(2,22,0)
1469 surface_info_t *surface_info = g_new(surface_info_t, 1);
1472 user_data=(user_data_t *)g_object_get_data(G_OBJECT(widget), "user_data_t");
1478 #if GTK_CHECK_VERSION(2,22,0)
1479 if(user_data->dlg.dialog_graph.surface){
1480 g_object_unref(user_data->dlg.dialog_graph.surface);
1481 user_data->dlg.dialog_graph.surface=NULL;
1483 gtk_widget_get_allocation(widget, &widget_alloc);
1484 user_data->dlg.dialog_graph.surface = gdk_window_create_similar_surface (gtk_widget_get_window(widget),
1485 CAIRO_CONTENT_COLOR,
1487 widget_alloc.height);
1489 if(user_data->dlg.dialog_graph.pixmap){
1490 g_object_unref(user_data->dlg.dialog_graph.pixmap);
1491 user_data->dlg.dialog_graph.pixmap=NULL;
1494 gtk_widget_get_allocation(widget, &widget_alloc);
1495 user_data->dlg.dialog_graph.pixmap=gdk_pixmap_new(gtk_widget_get_window(widget),
1497 widget_alloc.height,
1500 user_data->dlg.dialog_graph.surface_width=widget_alloc.width;
1501 user_data->dlg.dialog_graph.surface_height=widget_alloc.height;
1503 bt_save = g_object_get_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save");
1504 #if GTK_CHECK_VERSION(2,22,0)
1505 surface_info->surface = user_data->dlg.dialog_graph.surface;
1506 surface_info->width = widget_alloc.width;
1507 surface_info->height = widget_alloc.height;
1508 g_object_set_data(G_OBJECT(bt_save), "surface-info", surface_info);
1509 gtk_widget_set_sensitive(bt_save, TRUE);
1511 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1513 g_object_set_data(G_OBJECT(bt_save), "pixmap", user_data->dlg.dialog_graph.pixmap);
1514 gtk_widget_set_sensitive(bt_save, TRUE);
1516 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1518 cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
1519 cairo_set_source_rgb (cr, 1, 1, 1);
1523 dialog_graph_redraw(user_data);
1527 /****************************************************************************/
1528 static gint scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1530 user_data_t *user_data=(user_data_t *)data;
1533 mi=(guint32) (gtk_adjustment_get_value(user_data->dlg.dialog_graph.scrollbar_adjustment) + gtk_adjustment_get_page_size(user_data->dlg.dialog_graph.scrollbar_adjustment));
1534 if(user_data->dlg.dialog_graph.last_interval==mi){
1537 if( (user_data->dlg.dialog_graph.last_interval==0xffffffff)
1538 && (mi==user_data->dlg.dialog_graph.max_interval) ){
1542 user_data->dlg.dialog_graph.last_interval=(mi/user_data->dlg.dialog_graph.interval)*user_data->dlg.dialog_graph.interval;
1544 dialog_graph_redraw(user_data);
1548 /****************************************************************************/
1549 static void create_draw_area(user_data_t* user_data, GtkWidget *box)
1551 user_data->dlg.dialog_graph.draw_area=gtk_drawing_area_new();
1552 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "destroy", G_CALLBACK(quit), user_data);
1554 gtk_widget_set_size_request(user_data->dlg.dialog_graph.draw_area, user_data->dlg.dialog_graph.surface_width, user_data->dlg.dialog_graph.surface_height);
1556 /* signals needed to handle backing pixmap */
1557 #if GTK_CHECK_VERSION(3,0,0)
1558 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "draw", G_CALLBACK(draw_area_draw), user_data);
1560 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), user_data);
1562 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "configure_event", G_CALLBACK(configure_event), user_data);
1564 gtk_widget_show(user_data->dlg.dialog_graph.draw_area);
1565 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.draw_area, TRUE, TRUE, 0);
1567 /* create the associated scrollbar */
1568 user_data->dlg.dialog_graph.scrollbar_adjustment=(GtkAdjustment *)gtk_adjustment_new(0,0,0,0,0,0);
1569 user_data->dlg.dialog_graph.scrollbar=gtk_hscrollbar_new(user_data->dlg.dialog_graph.scrollbar_adjustment);
1570 gtk_widget_show(user_data->dlg.dialog_graph.scrollbar);
1571 gtk_box_pack_start(GTK_BOX(box), user_data->dlg.dialog_graph.scrollbar, FALSE, FALSE, 0);
1572 g_signal_connect(user_data->dlg.dialog_graph.scrollbar_adjustment, "value_changed", G_CALLBACK(scrollbar_changed), user_data);
1575 /****************************************************************************/
1576 static void disable_graph(dialog_graph_graph_t *dgg)
1580 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1585 /****************************************************************************/
1586 static gint filter_callback(GtkWidget *widget _U_, dialog_graph_graph_t *dgg)
1588 /* this graph is not active, just update display and redraw */
1589 if(!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg->display_button))){
1591 dialog_graph_redraw(dgg->ud);
1596 cf_retap_packets(&cfile);
1597 dialog_graph_redraw(dgg->ud);
1602 /****************************************************************************/
1603 static void create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *box, int num)
1609 hbox=gtk_hbox_new(FALSE, 3);
1610 gtk_container_add(GTK_CONTAINER(box), hbox);
1611 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1612 gtk_widget_show(hbox);
1614 g_snprintf(str, sizeof(str), "Graph %d", num);
1615 dgg->display_button=gtk_toggle_button_new_with_label(str);
1616 gtk_box_pack_start(GTK_BOX(hbox), dgg->display_button, FALSE, FALSE, 0);
1617 gtk_widget_show(dgg->display_button);
1618 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button), dgg->display);
1619 g_signal_connect(dgg->display_button, "toggled", G_CALLBACK(filter_callback), dgg);
1621 label=gtk_label_new(dgg->title);
1622 gtk_widget_show(label);
1623 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1625 #if GTK_CHECK_VERSION(3,0,0)
1626 gtk_widget_override_color(label, GTK_STATE_NORMAL, &dgg->rgba_color);
1627 gtk_widget_override_color(label, GTK_STATE_ACTIVE, &dgg->rgba_color);
1628 gtk_widget_override_color(label, GTK_STATE_PRELIGHT, &dgg->rgba_color);
1629 gtk_widget_override_color(label, GTK_STATE_SELECTED, &dgg->rgba_color);
1630 gtk_widget_override_color(label, GTK_STATE_INSENSITIVE, &dgg->rgba_color);
1632 gtk_widget_modify_fg(label, GTK_STATE_NORMAL, &dgg->color);
1633 gtk_widget_modify_fg(label, GTK_STATE_ACTIVE, &dgg->color);
1634 gtk_widget_modify_fg(label, GTK_STATE_PRELIGHT, &dgg->color);
1635 gtk_widget_modify_fg(label, GTK_STATE_SELECTED, &dgg->color);
1636 gtk_widget_modify_fg(label, GTK_STATE_INSENSITIVE, &dgg->color);
1641 /****************************************************************************/
1642 static void create_filter_area(user_data_t* user_data, GtkWidget *box)
1649 frame=gtk_frame_new("Graphs");
1650 gtk_container_add(GTK_CONTAINER(box), frame);
1651 gtk_widget_show(frame);
1653 vbox=gtk_vbox_new(FALSE, 1);
1654 gtk_container_add(GTK_CONTAINER(frame), vbox);
1655 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1656 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_START);
1657 gtk_widget_show(vbox);
1659 for(i=0;i<MAX_GRAPHS;i++){
1660 create_filter_box(&user_data->dlg.dialog_graph.graph[i], vbox, i+1);
1663 label=gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1664 gtk_widget_show(label);
1665 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
1670 /****************************************************************************/
1671 static void yscale_select(GtkWidget *item, gpointer key)
1674 user_data_t *user_data;
1676 user_data=(user_data_t *)key;
1677 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1679 user_data->dlg.dialog_graph.max_y_units_index=i;
1680 user_data->dlg.dialog_graph.max_y_units=yscale_max[i];
1681 dialog_graph_redraw(user_data);
1684 /****************************************************************************/
1685 static void pixels_per_tick_select(GtkWidget *item, gpointer key)
1688 user_data_t *user_data;
1690 user_data=(user_data_t *)key;
1691 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1693 user_data->dlg.dialog_graph.pixels_per_tick_index=i;
1694 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[i];
1695 dialog_graph_redraw(user_data);
1698 /****************************************************************************/
1699 static void tick_interval_select(GtkWidget *item, gpointer key)
1702 user_data_t *user_data;
1704 user_data=(user_data_t *)key;
1705 i = gtk_combo_box_get_active (GTK_COMBO_BOX(item));
1707 user_data->dlg.dialog_graph.interval_index=i;
1708 user_data->dlg.dialog_graph.interval=tick_interval_values[i];
1709 cf_retap_packets(&cfile);
1710 dialog_graph_redraw(user_data);
1713 /****************************************************************************/
1715 create_yscale_max_menu_items(user_data_t* user_data)
1718 GtkWidget *combo_box;
1721 combo_box = gtk_combo_box_text_new();
1723 for(i=0;i<MAX_YSCALE;i++){
1724 if(yscale_max[i]==AUTO_MAX_YSCALE){
1725 g_strlcpy(str,"Auto",sizeof(str));
1726 } else if (yscale_max[i] < 1000000) {
1727 g_snprintf(str, sizeof(str), "%u ms", yscale_max[i]/1000);
1729 g_snprintf(str, sizeof(str), "%u s", yscale_max[i]/1000000);
1731 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1733 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.max_y_units_index);
1734 g_signal_connect(combo_box, "changed", G_CALLBACK(yscale_select), (gpointer)user_data);
1739 /****************************************************************************/
1741 create_pixels_per_tick_menu_items(user_data_t *user_data)
1744 GtkWidget *combo_box;
1747 combo_box = gtk_combo_box_text_new();
1749 for(i=0;i<MAX_PIXELS_PER_TICK;i++){
1750 g_snprintf(str, sizeof(str), "%u", pixels_per_tick[i]);
1751 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1753 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.pixels_per_tick_index);
1755 g_signal_connect(combo_box, "changed", G_CALLBACK(pixels_per_tick_select), (gpointer)user_data);
1760 /****************************************************************************/
1762 create_tick_interval_menu_items(user_data_t *user_data)
1764 GtkWidget *combo_box;
1768 combo_box = gtk_combo_box_text_new();
1770 for(i=0;i<MAX_TICK_VALUES;i++){
1771 if(tick_interval_values[i]>=1000){
1772 g_snprintf(str, sizeof(str), "%u sec", tick_interval_values[i]/1000);
1773 } else if(tick_interval_values[i]>=100){
1774 g_snprintf(str, sizeof(str), "0.%1u sec", (tick_interval_values[i]/100)%10);
1775 } else if(tick_interval_values[i]>=10){
1776 g_snprintf(str, sizeof(str), "0.%02u sec", (tick_interval_values[i]/10)%10);
1778 g_snprintf(str, sizeof(str), "0.%03u sec", (tick_interval_values[i])%10);
1780 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box), str);
1782 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), user_data->dlg.dialog_graph.interval_index);
1783 g_signal_connect(combo_box, "changed", G_CALLBACK(tick_interval_select), (gpointer)user_data);
1788 /****************************************************************************/
1789 static void create_ctrl_menu(user_data_t* user_data, GtkWidget *box, const char *name, GtkWidget *(*func)(user_data_t* user_data))
1793 GtkWidget *combo_box;
1795 hbox=gtk_hbox_new(FALSE, 0);
1796 gtk_container_add(GTK_CONTAINER(box), hbox);
1797 gtk_box_set_child_packing(GTK_BOX(box), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1798 gtk_widget_show(hbox);
1800 label=gtk_label_new(name);
1801 gtk_widget_show(label);
1802 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
1804 combo_box = (*func)(user_data);
1805 gtk_box_pack_end(GTK_BOX(hbox), combo_box, FALSE, FALSE, 0);
1806 gtk_widget_show(combo_box);
1809 /****************************************************************************/
1810 static void create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1812 GtkWidget *frame_vbox;
1816 frame_vbox=gtk_vbox_new(FALSE, 0);
1817 gtk_container_add(GTK_CONTAINER(box), frame_vbox);
1818 gtk_widget_show(frame_vbox);
1820 frame = gtk_frame_new("X Axis");
1821 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1822 gtk_widget_show(frame);
1824 vbox=gtk_vbox_new(FALSE, 0);
1825 gtk_container_add(GTK_CONTAINER(frame), vbox);
1826 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1827 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1828 gtk_widget_show(vbox);
1830 create_ctrl_menu(user_data, vbox, "Tick interval:", create_tick_interval_menu_items);
1831 create_ctrl_menu(user_data, vbox, "Pixels per tick:", create_pixels_per_tick_menu_items);
1833 frame = gtk_frame_new("Y Axis");
1834 gtk_container_add(GTK_CONTAINER(frame_vbox), frame);
1835 gtk_widget_show(frame);
1837 vbox=gtk_vbox_new(FALSE, 0);
1838 gtk_container_add(GTK_CONTAINER(frame), vbox);
1839 gtk_container_set_border_width(GTK_CONTAINER(vbox), 3);
1840 gtk_box_set_child_packing(GTK_BOX(box), vbox, FALSE, FALSE, 0, GTK_PACK_END);
1841 gtk_widget_show(vbox);
1843 create_ctrl_menu(user_data, vbox, "Scale:", create_yscale_max_menu_items);
1848 /****************************************************************************/
1849 static void dialog_graph_init_window(user_data_t* user_data)
1853 GtkWidget *bt_close;
1856 /* create the main window */
1857 user_data->dlg.dialog_graph.window=dlg_window_new("I/O Graphs"); /* transient_for top_level */
1859 vbox=gtk_vbox_new(FALSE, 0);
1860 gtk_container_add(GTK_CONTAINER(user_data->dlg.dialog_graph.window), vbox);
1861 gtk_widget_show(vbox);
1863 create_draw_area(user_data, vbox);
1865 hbox=gtk_hbox_new(FALSE, 3);
1866 gtk_box_pack_end(GTK_BOX(vbox), hbox, FALSE, FALSE, 5);
1867 gtk_container_set_border_width(GTK_CONTAINER(hbox), 3);
1868 gtk_box_set_child_packing(GTK_BOX(vbox), hbox, FALSE, FALSE, 0, GTK_PACK_START);
1869 gtk_widget_show(hbox);
1871 create_filter_area(user_data, hbox);
1872 create_ctrl_area(user_data, hbox);
1874 dialog_graph_set_title(user_data);
1876 hbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_SAVE, NULL);
1877 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
1878 gtk_widget_show(hbox);
1880 bt_close = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_CLOSE);
1881 window_set_cancel_button(user_data->dlg.dialog_graph.window, bt_close, window_cancel_button_cb);
1883 bt_save = g_object_get_data(G_OBJECT(hbox), GTK_STOCK_SAVE);
1884 gtk_widget_set_sensitive(bt_save, FALSE);
1885 gtk_widget_set_tooltip_text(bt_save, "Save the displayed graph to a file");
1886 g_signal_connect(bt_save, "clicked", G_CALLBACK(pixmap_save_cb), NULL);
1887 g_object_set_data(G_OBJECT(user_data->dlg.dialog_graph.window), "bt_save", bt_save);
1889 g_signal_connect(user_data->dlg.dialog_graph.window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1891 gtk_widget_show(user_data->dlg.dialog_graph.window);
1892 window_present(user_data->dlg.dialog_graph.window);
1897 /****************************************************************************/
1898 static void on_graph_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1900 if (user_data->dlg.dialog_graph.window != NULL) {
1901 /* There's already a graph window; reactivate it. */
1902 reactivate_window(user_data->dlg.dialog_graph.window);
1906 dialog_graph_init_window(user_data);
1910 /****************************************************************************/
1912 static void on_goto_bt_clicked_lst(GtkWidget *bt _U_, user_data_t *user_data _U_)
1915 GtkTreeModel *model;
1916 GtkTreeSelection *selection;
1919 selection = user_data->dlg.selected_list_sel;
1921 if (selection==NULL)
1924 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1925 gtk_tree_model_get (model, &iter, PACKET_COLUMN, &fnumber, -1);
1926 cf_goto_frame(&cfile, fnumber);
1931 static void draw_stat(user_data_t *user_data);
1933 /****************************************************************************/
1934 /* re-dissects all packets */
1935 static void on_refresh_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
1937 GString *error_string;
1939 /* remove tap listener */
1940 protect_thread_critical_region();
1941 remove_tap_listener(user_data);
1942 unprotect_thread_critical_region();
1944 /* register tap listener */
1945 error_string = register_tap_listener("rtp", user_data, NULL, 0,
1946 rtp_reset, rtp_packet, rtp_draw);
1947 if (error_string != NULL) {
1948 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1949 g_string_free(error_string, TRUE);
1953 /* retap all packets */
1954 cf_retap_packets(&cfile);
1956 /* draw statistics info */
1957 draw_stat(user_data);
1961 #ifdef HAVE_LIBPORTAUDIO
1962 /****************************************************************************/
1964 on_player_bt_clicked(GtkButton *button _U_, gpointer user_data _U_)
1966 /*rtp_player_init(voip_calls_get_info());*/
1967 rtp_player_init(NULL);
1969 #endif /* HAVE_LIBPORTAUDIO */
1971 static void on_next_bt_clicked_list(GtkWidget *bt _U_, user_data_t *user_data _U_)
1974 GtkTreeModel *model;
1976 GtkTreeSelection *selection;
1979 selection = user_data->dlg.selected_list_sel;
1981 if (selection==NULL)
1985 if (gtk_tree_selection_get_selected (selection, &model, &iter)){
1986 while (gtk_tree_model_iter_next (model,&iter)) {
1987 gtk_tree_model_get (model, &iter, STATUS_COLUMN, &text, -1);
1988 if (strcmp(text, OK_TEXT) != 0) {
1989 gtk_tree_selection_select_iter (selection, &iter);
1990 path = gtk_tree_model_get_path(model, &iter);
1991 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data->dlg.selected_list),
1994 gtk_tree_path_free(path);
2001 if (user_data->dlg.number_of_nok>1){
2002 /* Get the first iter and select it before starting over */
2003 gtk_tree_model_get_iter_first(model, &iter);
2004 gtk_tree_selection_select_iter (selection, &iter);
2011 /****************************************************************************/
2012 /* when we want to save the information */
2013 static gboolean save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
2016 GtkWidget *rev, *forw, *both;
2017 user_data_t *user_data;
2019 GtkListStore *store;
2021 GtkTreeModel *model;
2022 gboolean more_items = TRUE;
2024 /* To Hold data from the list row */
2025 guint32 packet; /* Packet */
2026 guint16 sequence; /* Sequence */
2027 guint32 timestamp; /* timestamp */
2028 gfloat delta; /* Delta(ms) */
2029 gfloat jitter; /* Jitter(ms) */
2030 gfloat skew; /* Skew(ms) */
2031 gfloat ipbw; /* IP BW(kbps) */
2032 gboolean marker; /* Marker */
2033 char * status_str; /* Status */
2034 char * date_str; /* Date */
2035 guint length; /* Length */
2041 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2043 /* Perhaps the user specified a directory instead of a file.
2044 * Check whether they did.
2046 if (test_for_directory(g_dest) == EISDIR) {
2047 /* It's a directory - set the file selection box to display it. */
2048 set_last_open_dir(g_dest);
2050 file_selection_set_current_folder(fc, get_last_open_dir());
2051 gtk_file_chooser_set_current_name(fc, "");
2052 return FALSE; /* run the dialog again */
2055 rev = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2056 forw = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "forward_rb");
2057 both = (GtkWidget*)g_object_get_data(G_OBJECT(fc), "both_rb");
2058 user_data = (user_data_t*)g_object_get_data(G_OBJECT(fc), "user_data");
2060 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(forw)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2061 fp = ws_fopen(g_dest, "w");
2063 open_failure_alert_box(g_dest, errno, TRUE);
2065 return TRUE; /* we're done */
2068 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2069 fprintf(fp, "Forward\n");
2071 write_failure_alert_box(g_dest, errno);
2074 return TRUE; /* we're done */
2078 for(j = 0; j < NUM_COLS; j++) {
2080 fprintf(fp,"\"%s\"",titles[j]);
2082 fprintf(fp,",\"%s\"",titles[j]);
2087 write_failure_alert_box(g_dest, errno);
2092 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_fwd));
2093 store = GTK_LIST_STORE(model);
2094 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2097 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2098 PACKET_COLUMN, &packet,
2099 SEQUENCE_COLUMN, &sequence,
2100 TIMESTAMP_COLUMN, ×tamp,
2101 DELTA_COLUMN, &delta,
2102 JITTER_COLUMN, &jitter,
2105 MARKER_COLUMN, &marker,
2106 STATUS_COLUMN, &status_str,
2107 DATE_COLUMN, &date_str,
2108 LENGTH_COLUMN, &length,
2110 fprintf(fp, "\"%u\"", packet);
2111 fprintf(fp, ",\"%u\"", sequence);
2112 fprintf(fp, ",\"%u\"", timestamp);
2113 fprintf(fp, ",\"%.2f\"", delta);
2114 fprintf(fp, ",\"%.2f\"", jitter);
2115 fprintf(fp, ",\"%.2f\"", skew);
2116 fprintf(fp, ",\"%.2f\"", ipbw);
2117 fprintf(fp, ",\"%s\"", marker? "SET" : "");
2118 fprintf(fp, ",\"%s\"", status_str);
2119 fprintf(fp, ",\"%s\"", date_str);
2120 fprintf(fp, ",\"%u\"", length);
2125 write_failure_alert_box(g_dest, errno);
2128 return TRUE; /* we're done */
2131 more_items = gtk_tree_model_iter_next (model,&iter);
2135 if (fclose(fp) == EOF) {
2136 write_failure_alert_box(g_dest, errno);
2138 return TRUE; /* we're done */
2142 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rev)) || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2144 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2145 fp = ws_fopen(g_dest, "a");
2147 open_failure_alert_box(g_dest, errno, TRUE);
2149 return TRUE; /* we're done */
2151 fprintf(fp, "\nReverse\n");
2153 write_failure_alert_box(g_dest, errno);
2156 return TRUE; /* we're done */
2159 fp = ws_fopen(g_dest, "w");
2161 open_failure_alert_box(g_dest, errno, TRUE);
2163 return TRUE; /* we're done */
2166 for(j = 0; j < NUM_COLS; j++) {
2168 fprintf(fp,"\"%s\"",titles[j]);
2170 fprintf(fp,",\"%s\"",titles[j]);
2175 write_failure_alert_box(g_dest, errno);
2178 return TRUE; /* we're done */
2180 model = gtk_tree_view_get_model(GTK_TREE_VIEW(user_data->dlg.list_rev));
2181 store = GTK_LIST_STORE(model);
2182 if( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter) ) {
2187 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
2188 PACKET_COLUMN, &packet,
2189 SEQUENCE_COLUMN, &sequence,
2190 TIMESTAMP_COLUMN, ×tamp,
2191 DELTA_COLUMN, &delta,
2192 JITTER_COLUMN, &jitter,
2195 MARKER_COLUMN, &marker,
2196 STATUS_COLUMN, &status_str,
2197 DATE_COLUMN, &date_str,
2198 LENGTH_COLUMN, &length,
2200 fprintf(fp, "\"%u\"", packet);
2201 fprintf(fp, ",\"%u\"", sequence);
2202 fprintf(fp, ",\"%u\"", timestamp);
2203 fprintf(fp, ",\"%.2f\"", delta);
2204 fprintf(fp, ",\"%.2f\"", jitter);
2205 fprintf(fp, ",\"%.2f\"", skew);
2206 fprintf(fp, ",\"%.2f\"", ipbw);
2207 fprintf(fp, ",\"%s\"", marker? "SET" : "");
2208 fprintf(fp, ",\"%s\"", status_str);
2209 fprintf(fp, ",\"%s\"", date_str);
2210 fprintf(fp, ",\"%u\"", length);
2215 write_failure_alert_box(g_dest, errno);
2218 return TRUE; /* we're done */
2221 more_items = gtk_tree_model_iter_next (model,&iter);
2224 if (fclose(fp) == EOF) {
2225 write_failure_alert_box(g_dest, errno);
2227 return TRUE; /* we're done */
2232 return TRUE; /* we're done */
2235 static void save_csv_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2237 user_data->dlg.save_csv_as_w = NULL;
2240 /* when the user wants to save the csv information in a file */
2241 static void save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
2245 GtkWidget *label_format;
2246 GtkWidget *channels_label;
2247 GtkWidget *forward_rb;
2248 GtkWidget *reversed_rb;
2251 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2252 if (user_data->dlg.save_csv_as_w != NULL) {
2253 /* There's already a Save CSV info dialog box; reactivate it. */
2254 reactivate_window(user_data->dlg.save_csv_as_w);
2258 user_data->dlg.save_csv_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2259 GTK_WINDOW(user_data->dlg.notebook),
2260 GTK_FILE_CHOOSER_ACTION_SAVE,
2261 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2262 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2264 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), TRUE);
2265 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_csv_as_w),GTK_WINDOW(user_data->dlg.window));
2267 /* Container for each row of widgets */
2268 vertb = gtk_vbox_new(FALSE, 0);
2269 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2270 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_csv_as_w), vertb);
2271 gtk_widget_show (vertb);
2273 table1 = gtk_table_new (2, 4, FALSE);
2274 gtk_widget_show (table1);
2275 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2276 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2277 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2279 label_format = gtk_label_new ("Format: Comma Separated Values");
2280 gtk_widget_show (label_format);
2281 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2282 (GtkAttachOptions) (GTK_FILL),
2283 (GtkAttachOptions) (0), 0, 0);
2285 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2288 channels_label = gtk_label_new ("Channels: ");
2289 gtk_widget_show (channels_label);
2290 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2291 (GtkAttachOptions) (GTK_FILL),
2292 (GtkAttachOptions) (0), 0, 0);
2293 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2295 forward_rb = gtk_radio_button_new_with_label (NULL, "forward ");
2296 gtk_widget_show (forward_rb);
2297 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2298 (GtkAttachOptions) (GTK_FILL),
2299 (GtkAttachOptions) (0), 0, 0);
2301 reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed ");
2302 gtk_widget_show (reversed_rb);
2303 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2304 (GtkAttachOptions) (GTK_FILL),
2305 (GtkAttachOptions) (0), 0, 0);
2307 both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
2308 gtk_widget_show (both_rb);
2309 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2310 (GtkAttachOptions) (GTK_FILL),
2311 (GtkAttachOptions) (0), 0, 0);
2313 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb), TRUE);
2315 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "forward_rb", forward_rb);
2316 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "reversed_rb", reversed_rb);
2317 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "both_rb", both_rb);
2318 g_object_set_data(G_OBJECT(user_data->dlg.save_csv_as_w), "user_data", user_data);
2320 g_signal_connect(user_data->dlg.save_csv_as_w, "delete_event",
2321 G_CALLBACK(window_delete_event_cb), NULL);
2322 g_signal_connect(user_data->dlg.save_csv_as_w, "destroy",
2323 G_CALLBACK(save_csv_as_destroy_cb), user_data);
2325 gtk_widget_show(user_data->dlg.save_csv_as_w);
2326 window_present(user_data->dlg.save_csv_as_w);
2328 /* "Run" the GtkFileChooserDialog. */
2329 /* Upon exit: If "Accept" run the OK callback. */
2330 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2331 /* Destroy the window. */
2332 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2333 /* return with a TRUE status so that the dialog window will be destroyed. */
2334 /* Trying to re-run the dialog after popping up an alert box will not work */
2335 /* since the user will not be able to dismiss the alert box. */
2336 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2337 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2339 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2340 /* GtkFileChooserDialog. */
2341 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_csv_as_w)) == GTK_RESPONSE_ACCEPT) {
2342 if (save_csv_as_ok_cb(NULL, user_data->dlg.save_csv_as_w)) {
2343 break; /* we're done */
2346 window_destroy(user_data->dlg.save_csv_as_w);
2350 /****************************************************************************/
2351 static void save_voice_as_destroy_cb(GtkWidget *win _U_, user_data_t *user_data)
2353 /* Note that we no longer have a Save voice info dialog box. */
2354 user_data->dlg.save_voice_as_w = NULL;
2357 /****************************************************************************/
2358 /* here we save it into a file that user specified */
2359 /* XXX what about endians here? could go something wrong? */
2361 static gboolean copy_file(gchar *dest, gint channels, gint format, user_data_t *user_data)
2363 FILE *to_stream, *forw_stream, *rev_stream;
2364 size_t fwritten, rwritten;
2365 int f_rawvalue, r_rawvalue, rawvalue;
2368 guint32 f_write_silence = 0;
2369 guint32 r_write_silence = 0;
2371 guint32 progbar_count, progbar_quantum, progbar_nextstep = 0, count = 0;
2372 gboolean stop_flag = FALSE;
2376 forw_stream = ws_fopen(user_data->f_tempname, "rb");
2377 if (forw_stream == NULL)
2379 rev_stream = ws_fopen(user_data->r_tempname, "rb");
2380 if (rev_stream == NULL) {
2381 fclose(forw_stream);
2385 /* open file for saving */
2386 to_stream = ws_fopen(dest, "wb");
2387 if (to_stream == NULL) {
2388 fclose(forw_stream);
2393 progbar = create_progress_dlg("Saving voice in a file", dest, TRUE, &stop_flag);
2395 if (format == SAVE_AU_FORMAT) /* au format */
2397 /* First we write the .au header. XXX Hope this is endian independent */
2398 /* the magic word 0x2e736e64 == .snd */
2399 phtonl(pd, 0x2e736e64);
2400 nchars = fwrite(pd, 1, 4, to_stream);
2403 /* header offset == 24 bytes */
2405 nchars = fwrite(pd, 1, 4, to_stream);
2408 /* total length; it is permitted to set this to 0xffffffff */
2410 nchars = fwrite(pd, 1, 4, to_stream);
2413 /* encoding format == 16-bit linear PCM */
2415 nchars = fwrite(pd, 1, 4, to_stream);
2418 /* sample rate == 8000 Hz */
2420 nchars = fwrite(pd, 1, 4, to_stream);
2425 nchars = fwrite(pd, 1, 4, to_stream);
2431 /* only forward direction */
2432 case SAVE_FORWARD_DIRECTION_MASK: {
2433 progbar_count = user_data->forward.saveinfo.count;
2434 progbar_quantum = user_data->forward.saveinfo.count/100;
2435 while ((f_rawvalue = getc(forw_stream)) != EOF) {
2438 if((count > progbar_nextstep) && (count <= progbar_count)) {
2439 update_progress_dlg(progbar,
2440 (gfloat) count/progbar_count, "Saving");
2441 progbar_nextstep = progbar_nextstep + progbar_quantum;
2445 if (user_data->forward.statinfo.pt == PT_PCMU){
2446 sample = ulaw2linear((unsigned char)f_rawvalue);
2449 else if(user_data->forward.statinfo.pt == PT_PCMA){
2450 sample = alaw2linear((unsigned char)f_rawvalue);
2457 fwritten = fwrite(pd, 1, 2, to_stream);
2464 /* only reversed direction */
2465 case SAVE_REVERSE_DIRECTION_MASK: {
2466 progbar_count = user_data->reversed.saveinfo.count;
2467 progbar_quantum = user_data->reversed.saveinfo.count/100;
2468 while ((r_rawvalue = getc(rev_stream)) != EOF) {
2471 if((count > progbar_nextstep) && (count <= progbar_count)) {
2472 update_progress_dlg(progbar,
2473 (gfloat) count/progbar_count, "Saving");
2474 progbar_nextstep = progbar_nextstep + progbar_quantum;
2478 if (user_data->reversed.statinfo.pt == PT_PCMU){
2479 sample = ulaw2linear((unsigned char)r_rawvalue);
2482 else if(user_data->reversed.statinfo.pt == PT_PCMA){
2483 sample = alaw2linear((unsigned char)r_rawvalue);
2490 rwritten = fwrite(pd, 1, 2, to_stream);
2497 /* both directions */
2498 case SAVE_BOTH_DIRECTION_MASK: {
2499 (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) ?
2500 (progbar_count = user_data->forward.saveinfo.count) :
2501 (progbar_count = user_data->reversed.saveinfo.count);
2502 progbar_quantum = progbar_count/100;
2503 /* since conversation in one way can start later than in the other one,
2504 * we have to write some silence information for one channel */
2505 if (user_data->forward.statinfo.start_time > user_data->reversed.statinfo.start_time) {
2506 f_write_silence = (guint32)
2507 ((user_data->forward.statinfo.start_time-user_data->reversed.statinfo.start_time)*(8000/1000));
2509 else if (user_data->forward.statinfo.start_time < user_data->reversed.statinfo.start_time) {
2510 r_write_silence = (guint32)
2511 ((user_data->reversed.statinfo.start_time-user_data->forward.statinfo.start_time)*(8000/1000));
2516 if((count > progbar_nextstep) && (count <= progbar_count)) {
2517 update_progress_dlg(progbar,
2518 (gfloat) count/progbar_count, "Saving");
2519 progbar_nextstep = progbar_nextstep + progbar_quantum;
2522 if(f_write_silence > 0) {
2523 r_rawvalue = getc(rev_stream);
2524 switch (user_data->forward.statinfo.reg_pt) {
2526 f_rawvalue = SILENCE_PCMU;
2529 f_rawvalue = SILENCE_PCMA;
2537 else if(r_write_silence > 0) {
2538 f_rawvalue = getc(forw_stream);
2539 switch (user_data->reversed.statinfo.reg_pt) {
2541 r_rawvalue = SILENCE_PCMU;
2544 r_rawvalue = SILENCE_PCMA;
2553 f_rawvalue = getc(forw_stream);
2554 r_rawvalue = getc(rev_stream);
2556 if ((r_rawvalue == EOF) && (f_rawvalue == EOF))
2558 if ((user_data->forward.statinfo.pt == PT_PCMU) && (user_data->reversed.statinfo.pt == PT_PCMU)){
2559 sample = (ulaw2linear((unsigned char)r_rawvalue) + ulaw2linear((unsigned char)f_rawvalue)) / 2;
2562 else if((user_data->forward.statinfo.pt == PT_PCMA) && (user_data->reversed.statinfo.pt == PT_PCMA)){
2563 sample = (alaw2linear((unsigned char)r_rawvalue) + alaw2linear((unsigned char)f_rawvalue)) / 2;
2572 rwritten = fwrite(pd, 1, 2, to_stream);
2580 else if (format == SAVE_RAW_FORMAT) /* raw format */
2584 /* only forward direction */
2585 case SAVE_FORWARD_DIRECTION_MASK: {
2586 progbar_count = user_data->forward.saveinfo.count;
2587 progbar_quantum = user_data->forward.saveinfo.count/100;
2588 stream = forw_stream;
2591 /* only reversed direction */
2592 case SAVE_REVERSE_DIRECTION_MASK: {
2593 progbar_count = user_data->reversed.saveinfo.count;
2594 progbar_quantum = user_data->reversed.saveinfo.count/100;
2595 stream = rev_stream;
2605 /* XXX how do you just copy the file? */
2606 while ((rawvalue = getc(stream)) != EOF) {
2609 if((count > progbar_nextstep) && (count <= progbar_count)) {
2610 update_progress_dlg(progbar,
2611 (gfloat) count/progbar_count, "Saving");
2612 progbar_nextstep = progbar_nextstep + progbar_quantum;
2616 if (putc(rawvalue, to_stream) == EOF) {
2630 destroy_progress_dlg(progbar);
2631 fclose(forw_stream);
2638 /****************************************************************************/
2639 /* the user wants to save in a file */
2640 /* XXX support for different formats is currently commented out */
2641 static gboolean save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2644 /*GtkWidget *wav, *sw;*/
2645 GtkWidget *au, *raw;
2646 GtkWidget *rev, *forw, *both;
2647 user_data_t *user_data;
2648 gint channels, format;
2650 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc));
2652 /* Perhaps the user specified a directory instead of a file.
2653 * Check whether they did.
2655 if (test_for_directory(g_dest) == EISDIR) {
2656 /* It's a directory - set the file selection box to display it. */
2657 set_last_open_dir(g_dest);
2659 file_selection_set_current_folder(fc, get_last_open_dir());
2660 gtk_file_chooser_set_current_name(fc, "");
2661 return FALSE; /* run the dialog again */
2665 wav = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2666 sw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2668 au = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "au_rb");
2669 raw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "raw_rb");
2670 rev = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "reversed_rb");
2671 forw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "forward_rb");
2672 both = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "both_rb");
2673 user_data = (user_data_t *)g_object_get_data(G_OBJECT(fc), "user_data");
2675 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2676 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2677 * disable the ok button or disable the buttons for direction if only one is not ok. The
2678 * problem is if we open the save voice dialog and then click the refresh button and maybe
2679 * the state changes, so we can't save anymore. In this case we should be able to update
2680 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2683 /* we can not save in both directions */
2684 if ((user_data->forward.saveinfo.saved == FALSE) && (user_data->reversed.saveinfo.saved == FALSE) && (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))) {
2685 /* there are many combinations here, we just exit when first matches */
2686 if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC) ||
2687 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC))
2688 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2689 "Can't save in a file: Unsupported codec!");
2690 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH) ||
2691 (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH))
2692 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2693 "Can't save in a file: Wrong length of captured packets!");
2694 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR) ||
2695 (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR))
2696 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2697 "Can't save in a file: RTP data with padding!");
2698 else if ((user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME) ||
2699 (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME))
2700 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2701 "Can't save in a file: Not all data in all packets was captured!");
2703 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2704 "Can't save in a file: File I/O problem!");
2706 return TRUE; /* we're done */
2708 /* we can not save forward direction */
2709 else if ((user_data->forward.saveinfo.saved == FALSE) && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (forw))) ||
2710 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
2711 if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2712 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2713 "Can't save forward direction in a file: Unsupported codec!");
2714 else if (user_data->forward.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2715 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2716 "Can't save forward direction in a file: Wrong length of captured packets!");
2717 else if (user_data->forward.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2718 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2719 "Can't save forward direction in a file: RTP data with padding!");
2720 else if (user_data->forward.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2721 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2722 "Can't save forward direction in a file: Not all data in all packets was captured!");
2724 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2725 "Can't save forward direction in a file: File I/O problem!");
2727 return TRUE; /* we're done */
2729 /* we can not save reversed direction */
2730 else if ((user_data->reversed.saveinfo.saved == FALSE) && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev))) ||
2731 (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both))))) {
2732 if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_CODEC)
2733 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2734 "Can't save reversed direction in a file: Unsupported codec!");
2735 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_WRONG_LENGTH)
2736 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2737 "Can't save reversed direction in a file: Wrong length of captured packets!");
2738 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_PADDING_ERROR)
2739 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2740 "Can't save reversed direction in a file: RTP data with padding!");
2741 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_SHORT_FRAME)
2742 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2743 "Can't save reversed direction in a file: Not all data in all packets was captured!");
2744 else if (user_data->reversed.saveinfo.error_type == TAP_RTP_NO_DATA)
2745 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2746 "Can't save reversed direction in a file: No RTP data!");
2748 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2749 "Can't save reversed direction in a file: File I/O problem!");
2751 return TRUE; /* we're done */
2755 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (wav)))
2756 format = SAVE_WAV_FORMAT;
2759 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (au)))
2760 format = SAVE_AU_FORMAT;
2762 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (sw)))
2763 format = SAVE_SW_FORMAT;
2765 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (raw)))
2766 format = SAVE_RAW_FORMAT;
2768 format = SAVE_NONE_FORMAT;
2770 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev)))
2771 channels = SAVE_REVERSE_DIRECTION_MASK;
2772 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both)))
2773 channels = SAVE_BOTH_DIRECTION_MASK;
2775 channels = SAVE_FORWARD_DIRECTION_MASK;
2777 /* direction/format validity*/
2778 if (format == SAVE_AU_FORMAT)
2780 /* make sure streams are alaw/ulaw */
2781 if ((channels & SAVE_FORWARD_DIRECTION_MASK) && (user_data->forward.statinfo.pt != PT_PCMA) && (user_data->forward.statinfo.pt != PT_PCMU)){
2782 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2783 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2785 return TRUE; /* we're done */
2787 if ((channels & SAVE_REVERSE_DIRECTION_MASK) && (user_data->reversed.statinfo.pt != PT_PCMA) && (user_data->reversed.statinfo.pt != PT_PCMU)){
2788 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2789 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2791 return TRUE; /* we're done */
2793 /* make sure pt's don't differ */
2794 if ((channels == SAVE_BOTH_DIRECTION_MASK) && (user_data->forward.statinfo.pt != user_data->reversed.statinfo.pt)){
2795 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2796 "Can't save in a file: Forward and reverse direction differ in type");
2798 return TRUE; /* we're done */
2801 else if (format == SAVE_RAW_FORMAT)
2803 /* can't save raw in both directions */
2804 if (channels == SAVE_BOTH_DIRECTION_MASK){
2805 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2806 "Can't save in a file: Unable to save raw data in both directions");
2808 return TRUE; /* we're done */
2813 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2814 "Can't save in a file: Invalid save format");
2816 return TRUE; /* we're done */
2819 if(!copy_file(g_dest, channels, format, user_data)) {
2820 /* XXX - report the error type! */
2821 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2822 "An error occurred while saving voice in a file!");
2824 return TRUE; /* we're done */
2828 return TRUE; /* we're done */
2831 /****************************************************************************/
2832 /* when the user wants to save the voice information in a file */
2833 /* XXX support for different formats is currently commented out */
2834 static void on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
2838 GtkWidget *label_format;
2839 GtkWidget *channels_label;
2840 GtkWidget *forward_rb;
2841 GtkWidget *reversed_rb;
2843 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2847 /* if we can't save in a file: wrong codec, cut packets or other errors */
2848 /* Should the error arise here or later when you click ok button ?
2849 * if we do it here, then we must disable the refresh button, so we don't do it here
2852 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2853 if (user_data->dlg.save_voice_as_w != NULL) {
2854 /* There's already a Save voice info dialog box; reactivate it. */
2855 reactivate_window(user_data->dlg.save_voice_as_w);
2859 /* XXX - use file_selection from dlg_utils instead! */
2860 user_data->dlg.save_voice_as_w = gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2861 GTK_WINDOW(user_data->dlg.notebook),
2862 GTK_FILE_CHOOSER_ACTION_SAVE,
2863 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
2864 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
2866 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), TRUE);
2867 gtk_window_set_transient_for(GTK_WINDOW(user_data->dlg.save_voice_as_w),GTK_WINDOW(user_data->dlg.window));
2869 /* Container for each row of widgets */
2870 vertb = gtk_vbox_new(FALSE, 0);
2871 gtk_container_set_border_width(GTK_CONTAINER(vertb), 5);
2872 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data->dlg.save_voice_as_w), vertb);
2873 gtk_widget_show (vertb);
2875 table1 = gtk_table_new (2, 4, FALSE);
2876 gtk_widget_show (table1);
2877 gtk_box_pack_start (GTK_BOX (vertb), table1, FALSE, FALSE, 0);
2878 gtk_container_set_border_width (GTK_CONTAINER (table1), 10);
2879 gtk_table_set_row_spacings (GTK_TABLE (table1), 20);
2881 /*label_format = gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
2882 gtk_widget_show (label_format);
2883 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2884 (GtkAttachOptions) (GTK_FILL),
2885 (GtkAttachOptions) (0), 0, 0);*/
2887 label_format = gtk_label_new ("Format: ");
2888 gtk_widget_show (label_format);
2889 gtk_table_attach (GTK_TABLE (table1), label_format, 0, 3, 0, 1,
2890 (GtkAttachOptions) (GTK_FILL),
2891 (GtkAttachOptions) (0), 0, 0);
2893 gtk_misc_set_alignment (GTK_MISC (label_format), 0, 0.5f);
2895 raw_rb = gtk_radio_button_new_with_label (NULL, ".raw");
2896 gtk_widget_show (raw_rb);
2897 gtk_table_attach (GTK_TABLE (table1), raw_rb, 1, 2, 0, 1,
2898 (GtkAttachOptions) (GTK_FILL),
2899 (GtkAttachOptions) (0), 0, 0);
2902 au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
2903 gtk_widget_show (au_rb);
2904 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2905 (GtkAttachOptions) (GTK_FILL),
2906 (GtkAttachOptions) (0), 0, 0);
2909 /* we support .au - ulaw*/
2910 wav_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".wav");
2911 gtk_widget_show (wav_rb);
2912 gtk_table_attach (GTK_TABLE (table1), wav_rb, 1, 2, 0, 1,
2913 (GtkAttachOptions) (GTK_FILL),
2914 (GtkAttachOptions) (0), 0, 0);
2916 sw_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), "8 kHz, 16 bit ");
2917 gtk_widget_show (sw_rb);
2918 gtk_table_attach (GTK_TABLE (table1), sw_rb, 2, 3, 0, 1,
2919 (GtkAttachOptions) (GTK_FILL),
2920 (GtkAttachOptions) (0), 0, 0);
2921 au_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb), ".au");
2922 gtk_widget_show (au_rb);
2923 gtk_table_attach (GTK_TABLE (table1), au_rb, 3, 4, 0, 1,
2924 (GtkAttachOptions) (GTK_FILL),
2925 (GtkAttachOptions) (0), 0, 0);
2928 channels_label = gtk_label_new ("Channels: ");
2929 gtk_widget_show (channels_label);
2930 gtk_table_attach (GTK_TABLE (table1), channels_label, 0, 1, 1, 2,
2931 (GtkAttachOptions) (GTK_FILL),
2932 (GtkAttachOptions) (0), 0, 0);
2933 gtk_misc_set_alignment (GTK_MISC (channels_label), 0, 0.5f);
2935 forward_rb = gtk_radio_button_new_with_label (NULL, "forward ");
2936 gtk_widget_show (forward_rb);
2937 gtk_table_attach (GTK_TABLE (table1), forward_rb, 1, 2, 1, 2,
2938 (GtkAttachOptions) (GTK_FILL),
2939 (GtkAttachOptions) (0), 0, 0);
2941 reversed_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "reversed ");
2942 gtk_widget_show (reversed_rb);
2943 gtk_table_attach (GTK_TABLE (table1), reversed_rb, 2, 3, 1, 2,
2944 (GtkAttachOptions) (GTK_FILL),
2945 (GtkAttachOptions) (0), 0, 0);
2947 both_rb = gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb), "both");
2948 gtk_widget_show (both_rb);
2949 gtk_table_attach (GTK_TABLE (table1), both_rb, 3, 4, 1, 2,
2950 (GtkAttachOptions) (GTK_FILL),
2951 (GtkAttachOptions) (0), 0, 0);
2954 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb), TRUE);
2957 /* if one direction is nok we don't allow saving
2958 XXX this is not ok since the user can click the refresh button and cause changes
2959 but we can not update this window. So we move all the decision on the time the ok
2962 if (user_data->forward.saved == FALSE) {
2963 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb), TRUE);
2964 gtk_widget_set_sensitive(forward_rb, FALSE);
2965 gtk_widget_set_sensitive(both_rb, FALSE);
2967 else if (user_data->reversed.saved == FALSE) {
2968 gtk_widget_set_sensitive(reversed_rb, FALSE);
2969 gtk_widget_set_sensitive(both_rb, FALSE);
2973 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
2974 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "au_rb", au_rb);
2975 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
2976 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "raw_rb", raw_rb);
2977 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "forward_rb", forward_rb);
2978 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "reversed_rb", reversed_rb);
2979 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "both_rb", both_rb);
2980 g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "user_data", user_data);
2982 g_signal_connect(user_data->dlg.save_voice_as_w, "delete_event",
2983 G_CALLBACK(window_delete_event_cb), NULL);
2984 g_signal_connect(user_data->dlg.save_voice_as_w, "destroy",
2985 G_CALLBACK(save_voice_as_destroy_cb), user_data);
2987 gtk_widget_show(user_data->dlg.save_voice_as_w);
2988 window_present(user_data->dlg.save_voice_as_w);
2990 /* "Run" the GtkFileChooserDialog. */
2991 /* Upon exit: If "Accept" run the OK callback. */
2992 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2993 /* Destroy the window. */
2994 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2995 /* return with a TRUE status so that the dialog window will be destroyed. */
2996 /* Trying to re-run the dialog after popping up an alert box will not work */
2997 /* since the user will not be able to dismiss the alert box. */
2998 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2999 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
3001 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
3002 /* GtkFileChooserDialog. */
3003 while (gtk_dialog_run(GTK_DIALOG(user_data->dlg.save_voice_as_w)) == GTK_RESPONSE_ACCEPT) {
3004 if (save_voice_as_ok_cb(NULL, user_data->dlg.save_voice_as_w)) {
3005 break; /* we're done */
3008 window_destroy(user_data->dlg.save_voice_as_w);
3013 /****************************************************************************/
3014 /* when we are finished with redisection, we add the label for the statistic */
3015 static void draw_stat(user_data_t *user_data)
3017 gchar label_max[300];
3018 guint32 f_expected = (user_data->forward.statinfo.stop_seq_nr + user_data->forward.statinfo.cycles*65536)
3019 - user_data->forward.statinfo.start_seq_nr + 1;
3020 guint32 r_expected = (user_data->reversed.statinfo.stop_seq_nr + user_data->reversed.statinfo.cycles*65536)
3021 - user_data->reversed.statinfo.start_seq_nr + 1;
3022 guint32 f_total_nr = user_data->forward.statinfo.total_nr;
3023 guint32 r_total_nr = user_data->reversed.statinfo.total_nr;
3024 gint32 f_lost = f_expected - f_total_nr;
3025 gint32 r_lost = r_expected - r_total_nr;
3026 double f_sumt = user_data->forward.statinfo.sumt;
3027 double f_sumTS = user_data->forward.statinfo.sumTS;
3028 double f_sumt2 = user_data->forward.statinfo.sumt2;
3029 double f_sumtTS = user_data->forward.statinfo.sumtTS;
3031 double r_sumt = user_data->reversed.statinfo.sumt;
3032 double r_sumTS = user_data->reversed.statinfo.sumTS;
3033 double r_sumt2 = user_data->reversed.statinfo.sumt2;
3034 double r_sumtTS = user_data->reversed.statinfo.sumtTS;
3035 double f_perc, r_perc;
3036 double f_clock_drift = 1.0;
3037 double r_clock_drift = 1.0;
3038 double f_duration = user_data->forward.statinfo.time - user_data->forward.statinfo.start_time;
3039 double r_duration = user_data->reversed.statinfo.time - user_data->reversed.statinfo.start_time;
3040 guint32 f_clock_rate = user_data->forward.statinfo.clock_rate;
3041 guint32 r_clock_rate = user_data->reversed.statinfo.clock_rate;
3043 if (f_clock_rate == 0){
3047 if (r_clock_rate == 0){
3052 f_perc = (double)(f_lost*100)/(double)f_expected;
3057 r_perc = (double)(r_lost*100)/(double)r_expected;
3062 if ((f_total_nr >0)&&(f_sumt2 > 0)){
3063 f_clock_drift = (f_total_nr * f_sumtTS - f_sumt * f_sumTS) / (f_total_nr * f_sumt2 - f_sumt * f_sumt);
3065 if ((r_total_nr >0)&&(r_sumt2 > 0)){
3066 r_clock_drift = (r_total_nr * r_sumtTS - r_sumt * r_sumTS) / (r_total_nr * r_sumt2 - r_sumt * r_sumt);
3068 g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
3069 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
3070 "Max skew = %.2f ms.\n"
3071 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
3072 " Sequence errors = %u \n"
3073 "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
3074 user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr,
3075 user_data->forward.statinfo.max_jitter,user_data->forward.statinfo.mean_jitter,
3076 user_data->forward.statinfo.max_skew,
3077 f_expected, f_expected, f_lost, f_perc,
3078 user_data->forward.statinfo.sequence,
3079 f_duration/1000,f_duration*(f_clock_drift-1.0),f_clock_drift*f_clock_rate,100.0*(f_clock_drift-1.0));
3081 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_fwd), label_max);
3082 gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_fwd),TRUE);
3084 g_snprintf(label_max, sizeof(label_max), "Max delta = %.2f ms at packet no. %u \n"
3085 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
3086 "Max skew = %.2f ms.\n"
3087 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
3088 " Sequence errors = %u \n"
3089 "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
3090 user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr,
3091 user_data->reversed.statinfo.max_jitter,user_data->reversed.statinfo.mean_jitter,
3092 user_data->reversed.statinfo.max_skew,
3093 r_expected, r_expected, r_lost, r_perc,
3094 user_data->reversed.statinfo.sequence,
3095 r_duration/1000,r_duration*(r_clock_drift-1.0),r_clock_drift*r_clock_rate,100.0*(r_clock_drift-1.0));
3097 gtk_label_set_text(GTK_LABEL(user_data->dlg.label_stats_rev), label_max);
3098 gtk_label_set_selectable (GTK_LABEL(user_data->dlg.label_stats_rev),TRUE);
3105 /****************************************************************************/
3106 /* append a line to list */
3107 static void add_to_list(GtkWidget *list, user_data_t * user_data, guint32 number, guint16 seq_num, guint32 timestamp,
3108 double delta, double jitter,double skew, double bandwidth, gchar *status, gboolean marker,
3109 gchar *timeStr, guint32 pkt_len, gchar *color_str, guint32 flags)
3111 GtkListStore *list_store;
3113 if (strcmp(status, OK_TEXT) != 0) {
3114 user_data->dlg.number_of_nok++;
3117 list_store = GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list))); /* Get store */
3119 /* Creates a new row at position. iter will be changed to point to this new row.
3120 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
3121 * The row will be filled with the values given to this function.
3123 * should generally be preferred when inserting rows in a sorted list store.
3125 gtk_list_store_insert_with_values( list_store , &user_data->dlg.iter, G_MAXINT,
3126 PACKET_COLUMN, number,
3127 SEQUENCE_COLUMN, seq_num,
3128 TIMESTAMP_COLUMN, timestamp,
3129 DELTA_COLUMN, delta,
3130 JITTER_COLUMN, jitter,
3132 IPBW_COLUMN, bandwidth,
3133 MARKER_COLUMN, marker,
3134 STATUS_COLUMN, (char *)status,
3135 DATE_COLUMN, (char *)timeStr,
3136 LENGTH_COLUMN, pkt_len,
3137 FOREGROUND_COLOR_COL, NULL,
3138 BACKGROUND_COLOR_COL, (char *)color_str,
3141 if(flags & STAT_FLAG_FIRST){
3142 /* Set first row as active */
3143 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list)), &user_data->dlg.iter);
3147 /****************************************************************************
3148 * Functions needed to present values from the list
3152 /* Present boolean value */
3154 rtp_boolean_data_func (GtkTreeViewColumn *column _U_,
3155 GtkCellRenderer *renderer,
3156 GtkTreeModel *model,
3162 /* the col to get data from is in userdata */
3163 gint bool_col = GPOINTER_TO_INT(user_data);
3165 gtk_tree_model_get(model, iter, bool_col, &bool_val, -1);
3169 g_strlcpy(buf, bool_val ? "SET" : "", sizeof(buf));
3172 g_assert_not_reached();
3175 g_object_set(renderer, "text", buf, NULL);
3180 GtkWidget* create_list(user_data_t* user_data)
3183 GtkListStore *list_store;
3185 GtkTreeViewColumn *column;
3186 GtkCellRenderer *renderer;
3187 GtkTreeSortable *sortable;
3188 GtkTreeView *list_view;
3189 GtkTreeSelection *selection;
3191 /* Create the store */
3192 list_store = gtk_list_store_new(N_COLUMN, /* Total number of columns XXX */
3193 G_TYPE_UINT, /* Packet */
3194 G_TYPE_UINT, /* Sequence */
3195 G_TYPE_UINT, /* Time stamp */
3196 G_TYPE_FLOAT, /* Delta(ms) */
3197 G_TYPE_FLOAT, /* Filtered Jitter(ms) */
3198 G_TYPE_FLOAT, /* Skew(ms) */
3199 G_TYPE_FLOAT, /* IP BW(kbps) */
3200 G_TYPE_BOOLEAN, /* Marker */
3201 G_TYPE_STRING, /* Status */
3202 G_TYPE_STRING, /* Date */
3203 G_TYPE_UINT, /* Length */
3204 G_TYPE_STRING, /* Foreground color */
3205 G_TYPE_STRING); /* Background color */
3208 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store));
3210 list_view = GTK_TREE_VIEW(list);
3211 sortable = GTK_TREE_SORTABLE(list_store);
3213 /* Speed up the list display */
3214 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
3216 /* Setup the sortable columns */
3217 gtk_tree_sortable_set_sort_column_id(sortable, PACKET_COLUMN, GTK_SORT_ASCENDING);
3218 gtk_tree_view_set_headers_clickable(list_view, FALSE);
3220 /* The view now holds a reference. We can get rid of our own reference */
3221 g_object_unref (G_OBJECT (list_store));
3224 * Create the first column packet, associating the "text" attribute of the
3225 * cell_renderer to the first column of the model
3227 renderer = gtk_cell_renderer_text_new ();
3228 column = gtk_tree_view_column_new_with_attributes ("Packet", renderer,
3229 "text", PACKET_COLUMN,
3230 "foreground", FOREGROUND_COLOR_COL,
3231 "background", BACKGROUND_COLOR_COL,
3233 gtk_tree_view_column_set_sort_column_id(column, PACKET_COLUMN);
3234 gtk_tree_view_column_set_resizable(column, TRUE);
3235 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3236 gtk_tree_view_column_set_min_width(column, 55);
3238 /* Add the column to the view. */
3239 gtk_tree_view_append_column (list_view, column);
3242 renderer = gtk_cell_renderer_text_new ();
3243 column = gtk_tree_view_column_new_with_attributes ("Sequence", renderer,
3244 "text", SEQUENCE_COLUMN,
3245 "foreground", FOREGROUND_COLOR_COL,
3246 "background", BACKGROUND_COLOR_COL,
3248 gtk_tree_view_column_set_sort_column_id(column, SEQUENCE_COLUMN);
3249 gtk_tree_view_column_set_resizable(column, TRUE);
3250 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3251 gtk_tree_view_column_set_min_width(column, 75);
3252 gtk_tree_view_append_column (list_view, column);
3255 Currently not visible
3257 renderer = gtk_cell_renderer_text_new ();
3258 column = gtk_tree_view_column_new_with_attributes ("Time stamp", renderer,
3259 "text", TIMESTAMP_COLUMN,
3260 "foreground", FOREGROUND_COLOR_COL,
3261 "background", BACKGROUND_COLOR_COL,
3263 gtk_tree_view_column_set_sort_column_id(column, TIMESTAMP_COLUMN);
3264 gtk_tree_view_column_set_resizable(column, TRUE);
3265 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3266 gtk_tree_view_column_set_min_width(column, 75);
3267 gtk_tree_view_append_column (list_view, column);
3270 renderer = gtk_cell_renderer_text_new ();
3271 column = gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer,
3272 "text", DELTA_COLUMN,
3273 "foreground", FOREGROUND_COLOR_COL,
3274 "background", BACKGROUND_COLOR_COL,
3277 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3278 GINT_TO_POINTER(DELTA_COLUMN), NULL);
3280 gtk_tree_view_column_set_sort_column_id(column, DELTA_COLUMN);
3281 gtk_tree_view_column_set_resizable(column, TRUE);
3282 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3283 gtk_tree_view_column_set_min_width(column, 75);
3284 gtk_tree_view_append_column (list_view, column);
3287 renderer = gtk_cell_renderer_text_new ();
3288 column = gtk_tree_view_column_new_with_attributes ("Filtered Jitter(ms)", renderer,
3289 "text", JITTER_COLUMN,
3290 "foreground", FOREGROUND_COLOR_COL,
3291 "background", BACKGROUND_COLOR_COL,
3294 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3295 GINT_TO_POINTER(JITTER_COLUMN), NULL);
3297 gtk_tree_view_column_set_sort_column_id(column, JITTER_COLUMN);
3298 gtk_tree_view_column_set_resizable(column, TRUE);
3299 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3300 gtk_tree_view_column_set_min_width(column, 110);
3301 gtk_tree_view_append_column (list_view, column);
3304 renderer = gtk_cell_renderer_text_new ();
3305 column = gtk_tree_view_column_new_with_attributes ("Skew(ms)", renderer,
3306 "text", SKEW_COLUMN,
3307 "foreground", FOREGROUND_COLOR_COL,
3308 "background", BACKGROUND_COLOR_COL,
3311 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3312 GINT_TO_POINTER(SKEW_COLUMN), NULL);
3314 gtk_tree_view_column_set_sort_column_id(column, SKEW_COLUMN);
3315 gtk_tree_view_column_set_resizable(column, TRUE);
3316 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3317 gtk_tree_view_column_set_min_width(column, 110);
3318 gtk_tree_view_append_column (list_view, column);
3321 renderer = gtk_cell_renderer_text_new ();
3322 column = gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer,
3323 "text", IPBW_COLUMN,
3324 "foreground", FOREGROUND_COLOR_COL,
3325 "background", BACKGROUND_COLOR_COL,
3328 gtk_tree_view_column_set_cell_data_func(column, renderer, float_data_func,
3329 GINT_TO_POINTER(IPBW_COLUMN), NULL);
3331 gtk_tree_view_column_set_sort_column_id(column, IPBW_COLUMN);
3332 gtk_tree_view_column_set_resizable(column, TRUE);
3333 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3334 gtk_tree_view_column_set_min_width(column, 80);
3335 gtk_tree_view_append_column (list_view, column);
3338 renderer = gtk_cell_renderer_text_new ();
3339 column = gtk_tree_view_column_new_with_attributes ("Marker", renderer,
3340 "text", MARKER_COLUMN,
3341 "foreground", FOREGROUND_COLOR_COL,
3342 "background", BACKGROUND_COLOR_COL,
3345 gtk_tree_view_column_set_cell_data_func(column, renderer, rtp_boolean_data_func,
3346 GINT_TO_POINTER(MARKER_COLUMN), NULL);
3348 gtk_tree_view_column_set_sort_column_id(column, MARKER_COLUMN);
3349 gtk_tree_view_column_set_resizable(column, TRUE);
3350 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3351 gtk_tree_view_column_set_min_width(column, 60);
3352 gtk_tree_view_append_column (list_view, column);
3355 renderer = gtk_cell_renderer_text_new ();
3356 column = gtk_tree_view_column_new_with_attributes ( "Status", renderer,
3357 "text", STATUS_COLUMN,
3358 "foreground", FOREGROUND_COLOR_COL,
3359 "background", BACKGROUND_COLOR_COL,
3361 gtk_tree_view_column_set_sort_column_id(column, STATUS_COLUMN);
3362 gtk_tree_view_column_set_resizable(column, TRUE);
3363 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
3364 gtk_tree_view_column_set_min_width(column, 100);
3365 gtk_tree_view_append_column (list_view, column);
3367 /* Now enable the sorting of each column */
3368 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view), TRUE);
3369 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view), TRUE);
3371 /* Setup the selection handler */
3372 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
3373 gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
3375 g_signal_connect (G_OBJECT (selection), "changed", /* select_row */
3376 G_CALLBACK (on_list_select_row),
3381 /****************************************************************************/
3382 /* Create the dialog box with all widgets */
3383 static void create_rtp_dialog(user_data_t* user_data)
3385 GtkWidget *window = NULL;
3386 GtkWidget *list_fwd;
3387 GtkWidget *list_rev;
3388 GtkWidget *label_stats_fwd;
3389 GtkWidget *label_stats_rev;
3390 GtkWidget *notebook;
3392 GtkWidget *main_vb, *page, *page_r;
3394 GtkWidget *scrolled_window, *scrolled_window_r/*, *frame, *text, *label4, *page_help*/;
3395 GtkWidget *box4, *voice_bt, *refresh_bt, *goto_bt, *close_bt, *csv_bt, *next_bt;
3396 #ifdef HAVE_LIBPORTAUDIO
3397 GtkWidget *player_bt = NULL;
3398 #endif /* HAVE_LIBPORTAUDIO */
3399 GtkWidget *graph_bt;
3400 gchar label_forward[150];
3401 gchar label_forward_tree[150];
3402 gchar label_reverse[150];
3404 gchar str_ip_src[16];
3405 gchar str_ip_dst[16];
3407 window = dlg_window_new("Wireshark: RTP Stream Analysis"); /* transient_for top_level */
3408 gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
3410 /* Container for each row of widgets */
3411 main_vb = gtk_vbox_new(FALSE, 2);
3412 gtk_container_set_border_width(GTK_CONTAINER(main_vb), 2);
3413 gtk_container_add(GTK_CONTAINER(window), main_vb);
3414 gtk_widget_show(main_vb);
3417 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_fwd)), sizeof(str_ip_src));
3418 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_fwd)), sizeof(str_ip_dst));
3420 g_snprintf(label_forward, sizeof(label_forward),
3421 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3422 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3424 g_snprintf(label_forward_tree, sizeof(label_forward_tree),
3425 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3426 str_ip_src, user_data->port_src_fwd, str_ip_dst, user_data->port_dst_fwd, user_data->ssrc_fwd);
3429 g_strlcpy(str_ip_src, get_addr_name(&(user_data->ip_src_rev)), sizeof(str_ip_src));
3430 g_strlcpy(str_ip_dst, get_addr_name(&(user_data->ip_dst_rev)), sizeof(str_ip_dst));
3432 g_snprintf(label_reverse, sizeof(label_reverse),
3433 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3434 str_ip_src, user_data->port_src_rev, str_ip_dst, user_data->port_dst_rev, user_data->ssrc_rev);
3436 /* Start a notebook for flipping between sets of changes */
3437 notebook = gtk_notebook_new();
3438 gtk_container_add(GTK_CONTAINER(main_vb), notebook);
3439 g_object_set_data(G_OBJECT(window), "notebook", notebook);
3441 user_data->dlg.notebook_signal_id =
3442 g_signal_connect(notebook, "switch_page", G_CALLBACK(on_notebook_switch_page), user_data);
3444 /* page for forward connection */
3445 page = gtk_vbox_new(FALSE, 8);
3446 gtk_container_set_border_width(GTK_CONTAINER(page), 8);
3448 /* direction label */
3449 label = gtk_label_new(label_forward);
3450 gtk_box_pack_start(GTK_BOX(page), label, FALSE, FALSE, 0);
3452 /* place for some statistics */
3453 label_stats_fwd = gtk_label_new("\n");
3454 gtk_box_pack_end(GTK_BOX(page), label_stats_fwd, FALSE, FALSE, 0);
3456 /* scrolled window */
3457 scrolled_window = scrolled_window_new(NULL, NULL);
3460 list_fwd = create_list(user_data);
3461 gtk_widget_show(list_fwd);
3462 gtk_container_add(GTK_CONTAINER(scrolled_window), list_fwd);
3463 gtk_box_pack_start(GTK_BOX(page), scrolled_window, TRUE, TRUE, 0);
3464 gtk_widget_show(scrolled_window);
3467 label = gtk_label_new(" Forward Direction ");
3468 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page, label);
3470 /* same page for reversed connection */
3471 page_r = gtk_vbox_new(FALSE, 8);
3472 gtk_container_set_border_width(GTK_CONTAINER(page_r), 8);
3473 label = gtk_label_new(label_reverse);
3474 gtk_box_pack_start(GTK_BOX(page_r), label, FALSE, FALSE, 0);
3475 label_stats_rev = gtk_label_new("\n");
3476 gtk_box_pack_end(GTK_BOX(page_r), label_stats_rev, FALSE, FALSE, 0);
3478 scrolled_window_r = scrolled_window_new(NULL, NULL);
3480 list_rev = create_list(user_data);
3481 gtk_widget_show(list_rev);
3482 gtk_container_add(GTK_CONTAINER(scrolled_window_r), list_rev);
3483 gtk_box_pack_start(GTK_BOX(page_r), scrolled_window_r, TRUE, TRUE, 0);
3484 gtk_widget_show(scrolled_window_r);
3486 label = gtk_label_new(" Reversed Direction ");
3487 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_r, label);
3489 /* page for help&about or future */
3491 page_help = gtk_hbox_new(FALSE, 5);
3492 label = gtk_label_new(" Future ");
3493 gtk_notebook_append_page(GTK_NOTEBOOK(notebook), page_help, label);
3494 frame = gtk_frame_new("");
3495 text = gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution,...");
3496 gtk_label_set_justify(GTK_LABEL(text), GTK_JUSTIFY_LEFT);
3497 gtk_container_add(GTK_CONTAINER(frame), text);
3498 gtk_container_set_border_width(GTK_CONTAINER(frame), 20);
3499 gtk_box_pack_start(GTK_BOX(page_help), frame, TRUE, TRUE, 0);
3502 /* show all notebooks */
3503 gtk_widget_show_all(notebook);
3506 box4 = gtk_hbutton_box_new();
3507 gtk_box_pack_start(GTK_BOX(main_vb), box4, FALSE, FALSE, 0);
3508 gtk_container_set_border_width(GTK_CONTAINER(box4), 10);
3509 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4), GTK_BUTTONBOX_EDGE);
3510 gtk_box_set_spacing(GTK_BOX (box4), 0);
3511 gtk_widget_show(box4);
3513 voice_bt = gtk_button_new_with_label("Save payload...");
3514 gtk_container_add(GTK_CONTAINER(box4), voice_bt);
3515 gtk_widget_show(voice_bt);
3516 g_signal_connect(voice_bt, "clicked", G_CALLBACK(on_save_bt_clicked), user_data);
3518 csv_bt = gtk_button_new_with_label("Save as CSV...");
3519 gtk_container_add(GTK_CONTAINER(box4), csv_bt);
3520 gtk_widget_show(csv_bt);
3521 g_signal_connect(csv_bt, "clicked", G_CALLBACK(save_csv_as_cb), user_data);
3523 refresh_bt = gtk_button_new_from_stock(GTK_STOCK_REFRESH);
3524 gtk_container_add(GTK_CONTAINER(box4), refresh_bt);
3525 gtk_widget_show(refresh_bt);
3526 g_signal_connect(refresh_bt, "clicked", G_CALLBACK(on_refresh_bt_clicked), user_data);
3528 goto_bt = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
3529 gtk_container_add(GTK_CONTAINER(box4), goto_bt);
3530 gtk_widget_show(goto_bt);
3531 g_signal_connect(goto_bt, "clicked", G_CALLBACK(on_goto_bt_clicked_lst), user_data);
3533 graph_bt = gtk_button_new_with_label("Graph");
3534 gtk_container_add(GTK_CONTAINER(box4), graph_bt);
3535 gtk_widget_show(graph_bt);
3536 g_signal_connect(graph_bt, "clicked", G_CALLBACK(on_graph_bt_clicked), user_data);
3538 #ifdef HAVE_LIBPORTAUDIO
3539 player_bt = gtk_button_new_from_stock(WIRESHARK_STOCK_AUDIO_PLAYER);
3540 gtk_container_add(GTK_CONTAINER(box4), player_bt);
3541 gtk_widget_show(player_bt);
3542 g_signal_connect(player_bt, "clicked", G_CALLBACK(on_player_bt_clicked), NULL);
3543 /*gtk_widget_set_tooltip_text (player_bt, "Launch the RTP player to listen the audio stream");*/
3544 #endif /* HAVE_LIBPORTAUDIO */
3546 next_bt = gtk_button_new_with_label("Next non-Ok");
3547 gtk_container_add(GTK_CONTAINER(box4), next_bt);
3548 gtk_widget_show(next_bt);
3549 g_signal_connect(next_bt, "clicked", G_CALLBACK(on_next_bt_clicked_list), user_data);
3551 close_bt = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
3552 gtk_container_add(GTK_CONTAINER(box4), close_bt);
3553 #if GTK_CHECK_VERSION(2,18,0)
3554 gtk_widget_set_can_default(close_bt, TRUE);
3556 GTK_WIDGET_SET_FLAGS(close_bt, GTK_CAN_DEFAULT);
3558 gtk_widget_show(close_bt);
3559 window_set_cancel_button(window, close_bt, window_cancel_button_cb);
3561 g_signal_connect(window, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
3562 g_signal_connect(window, "destroy", G_CALLBACK(on_destroy), user_data);
3564 gtk_widget_show(window);
3565 window_present(window);
3568 /* some widget references need to be saved for outside use */
3569 user_data->dlg.window = window;
3570 user_data->dlg.list_fwd = list_fwd;
3571 user_data->dlg.list_rev = list_rev;
3572 user_data->dlg.label_stats_fwd = label_stats_fwd;
3573 user_data->dlg.label_stats_rev = label_stats_rev;
3574 user_data->dlg.notebook = notebook;
3575 user_data->dlg.selected_list = list_fwd;
3576 user_data->dlg.number_of_nok = 0;
3579 * select the initial row
3581 gtk_widget_grab_focus(list_fwd);
3586 /****************************************************************************/
3587 static gboolean process_node(proto_node *ptree_node, header_field_info *hfinformation,
3588 const gchar* proto_field, guint32* p_result)
3591 proto_node *proto_sibling_node;
3592 header_field_info *hfssrc;
3595 finfo = PNODE_FINFO(ptree_node);
3597 g_assert(finfo && "Caller passed top of the protocol tree. Expected child node");
3599 if (hfinformation==(finfo->hfinfo)) {
3600 hfssrc = proto_registrar_get_byname(proto_field);
3603 for(ptree_node=ptree_node->first_child; ptree_node!=NULL;
3604 ptree_node=ptree_node->next) {
3605 finfo=PNODE_FINFO(ptree_node);
3606 if (hfssrc==finfo->hfinfo) {
3607 if (hfinformation->type==FT_IPv4) {
3608 ipv4 = fvalue_get(&finfo->value);
3609 *p_result = ipv4_get_net_order_addr(ipv4);
3612 *p_result = fvalue_get_uinteger(&finfo->value);
3621 proto_sibling_node = ptree_node->next;
3623 if (proto_sibling_node) {
3624 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3630 /****************************************************************************/
3631 static gboolean get_int_value_from_proto_tree(proto_tree *protocol_tree,
3632 const gchar* proto_name,
3633 const gchar* proto_field,
3636 proto_node *ptree_node;
3637 header_field_info *hfinformation;
3639 hfinformation = proto_registrar_get_byname(proto_name);
3640 if (hfinformation == NULL)
3643 ptree_node = ((proto_node *)protocol_tree)->first_child;
3647 return process_node(ptree_node, hfinformation, proto_field, p_result);
3651 /****************************************************************************/
3653 address *ip_src_fwd,
3654 guint16 port_src_fwd,
3655 address *ip_dst_fwd,
3656 guint16 port_dst_fwd,
3658 address *ip_src_rev,
3659 guint16 port_src_rev,
3660 address *ip_dst_rev,
3661 guint16 port_dst_rev,
3665 user_data_t *user_data;
3668 static GdkColor col[MAX_GRAPHS] = {
3669 {0, 0x0000, 0x0000, 0x0000}, /* Black */
3670 {0, 0xffff, 0x0000, 0x0000}, /* Red */
3671 {0, 0x0000, 0xffff, 0x0000}, /* Green */
3672 {0, 0xdddd, 0xcccc, 0x6666}, /* Light amber yellow */
3673 {0, 0x6666, 0xcccc, 0xdddd}, /* Light bluish cyan */
3674 {0, 0x0000, 0x0000, 0xffff} /* Blue */
3676 #if GTK_CHECK_VERSION(3,0,0)
3677 static GdkRGBA rgba_col[MAX_GRAPHS] = {
3678 {0.0, 0.0, 0.0, 1.0}, /* Black */
3679 {1.0, 0.0, 0.1, 1.0}, /* Red */
3680 {0.0, 1.0, 0.0, 1.0}, /* Green */
3681 {0.867, 0.800, 0.400, 1.0}, /* Light amber yellow */
3682 {0.400, 0.800, 0.867, 1.0}, /* Light bluish cyan */
3683 {0.0, 0.0, 1.0, 1.0}, /* Blue */
3690 user_data = g_malloc(sizeof(user_data_t));
3692 COPY_ADDRESS(&(user_data->ip_src_fwd), ip_src_fwd);
3693 user_data->port_src_fwd = port_src_fwd;
3694 COPY_ADDRESS(&(user_data->ip_dst_fwd), ip_dst_fwd);
3695 user_data->port_dst_fwd = port_dst_fwd;
3696 user_data->ssrc_fwd = ssrc_fwd;
3697 COPY_ADDRESS(&(user_data->ip_src_rev), ip_src_rev);
3698 user_data->port_src_rev = port_src_rev;
3699 COPY_ADDRESS(&(user_data->ip_dst_rev), ip_dst_rev);
3700 user_data->port_dst_rev = port_dst_rev;
3701 user_data->ssrc_rev = ssrc_rev;
3704 /* file names for storing sound data */
3705 /*XXX: check for errors*/
3706 fd = create_tempfile(&tempname, "wireshark_rtp_f");
3707 user_data->f_tempname = g_strdup(tempname);
3709 fd = create_tempfile(&tempname, "wireshark_rtp_r");
3710 user_data->r_tempname = g_strdup(tempname);
3712 user_data->forward.saveinfo.fp = NULL;
3713 user_data->reversed.saveinfo.fp = NULL;
3714 user_data->dlg.save_voice_as_w = NULL;
3715 user_data->dlg.save_csv_as_w = NULL;
3716 user_data->dlg.dialog_graph.window = NULL;
3718 /* init dialog_graph */
3719 user_data->dlg.dialog_graph.needs_redraw=TRUE;
3720 user_data->dlg.dialog_graph.interval_index=DEFAULT_TICK_INTERVAL_VALUES_INDEX;
3721 user_data->dlg.dialog_graph.interval=tick_interval_values[DEFAULT_TICK_INTERVAL_VALUES_INDEX];
3722 user_data->dlg.dialog_graph.draw_area=NULL;
3723 #if GTK_CHECK_VERSION(2,22,0)
3724 user_data->dlg.dialog_graph.surface=NULL;
3726 user_data->dlg.dialog_graph.pixmap=NULL;
3728 user_data->dlg.dialog_graph.scrollbar=NULL;
3729 user_data->dlg.dialog_graph.scrollbar_adjustment=NULL;
3730 user_data->dlg.dialog_graph.surface_width=500;
3731 user_data->dlg.dialog_graph.surface_height=200;
3732 user_data->dlg.dialog_graph.pixels_per_tick_index=DEFAULT_PIXELS_PER_TICK_INDEX;
3733 user_data->dlg.dialog_graph.pixels_per_tick=pixels_per_tick[DEFAULT_PIXELS_PER_TICK_INDEX];
3734 user_data->dlg.dialog_graph.max_y_units_index=AUTO_MAX_YSCALE_INDEX;
3735 user_data->dlg.dialog_graph.max_y_units=AUTO_MAX_YSCALE;
3736 user_data->dlg.dialog_graph.last_interval=0xffffffff;
3737 user_data->dlg.dialog_graph.max_interval=0;
3738 user_data->dlg.dialog_graph.num_items=0;
3739 user_data->dlg.dialog_graph.start_time = -1;
3741 for(i=0;i<MAX_GRAPHS;i++){
3742 user_data->dlg.dialog_graph.graph[i].color.pixel=0;
3743 user_data->dlg.dialog_graph.graph[i].color.red=col[i].red;
3744 user_data->dlg.dialog_graph.graph[i].color.green=col[i].green;
3745 user_data->dlg.dialog_graph.graph[i].color.blue=col[i].blue;
3746 #if GTK_CHECK_VERSION(3,0,0)
3747 user_data->dlg.dialog_graph.graph[i].rgba_color.red=rgba_col[i].red;
3748 user_data->dlg.dialog_graph.graph[i].rgba_color.green=rgba_col[i].green;
3749 user_data->dlg.dialog_graph.graph[i].rgba_color.blue=rgba_col[i].blue;
3750 user_data->dlg.dialog_graph.graph[i].rgba_color.alpha=rgba_col[i].alpha;
3752 user_data->dlg.dialog_graph.graph[i].display=TRUE;
3753 user_data->dlg.dialog_graph.graph[i].display_button=NULL;
3754 user_data->dlg.dialog_graph.graph[i].ud=user_data;
3757 /* create the dialog box */
3758 create_rtp_dialog(user_data);
3760 /* proceed as if the Refresh button would have been pressed */
3761 on_refresh_bt_clicked(NULL, user_data);
3764 /****************************************************************************/
3765 /* entry point from main menu */
3766 #ifdef MAIN_MENU_USE_UIMANAGER
3767 void rtp_analysis_cb(GtkAction *action _U_, gpointer user_data _U_)
3769 static void rtp_analysis_cb(GtkWidget *w _U_, gpointer data _U_)
3773 guint16 port_src_fwd;
3775 guint16 port_dst_fwd;
3776 guint32 ssrc_fwd = 0;
3778 guint16 port_src_rev;
3780 guint16 port_dst_rev;
3781 guint32 ssrc_rev = 0;
3782 unsigned int version_fwd;
3784 gchar filter_text[256];
3788 gboolean frame_matched;
3790 GList *strinfo_list;
3791 GList *filtered_list = NULL;
3792 rtp_stream_info_t *strinfo;
3795 /* Try to compile the filter. */
3796 g_strlcpy(filter_text,"rtp && rtp.version && rtp.ssrc && (ip || ipv6)",sizeof(filter_text));
3797 if (!dfilter_compile(filter_text, &sfcode)) {
3798 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", dfilter_error_msg);
3801 /* we load the current file into cf variable */
3803 fdata = cf->current_frame;
3805 /* we are on the selected frame now */
3807 return; /* if we exit here it's an error */
3809 /* dissect the current frame */
3810 if (!cf_read_frame(cf, fdata))
3811 return; /* error reading the frame */
3812 epan_dissect_init(&edt, TRUE, FALSE);
3813 epan_dissect_prime_dfilter(&edt, sfcode);
3814 epan_dissect_run(&edt, &cf->pseudo_header, cf->pd, fdata, NULL);
3816 /* if it is not an rtp frame, show the rtpstream dialog */
3817 frame_matched = dfilter_apply_edt(sfcode, &edt);
3818 if (frame_matched != TRUE) {
3819 epan_dissect_cleanup(&edt);
3820 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3821 "You didn't choose a RTP packet!");
3825 /* ok, it is a RTP frame, so let's get the ip and port values */
3826 COPY_ADDRESS(&(ip_src_fwd), &(edt.pi.src))
3827 COPY_ADDRESS(&(ip_dst_fwd), &(edt.pi.dst))
3828 port_src_fwd = edt.pi.srcport;
3829 port_dst_fwd = edt.pi.destport;
3831 /* assume the inverse ip/port combination for the reverse direction */
3832 COPY_ADDRESS(&(ip_src_rev), &(edt.pi.dst))
3833 COPY_ADDRESS(&(ip_dst_rev), &(edt.pi.src))
3834 port_src_rev = edt.pi.destport;
3835 port_dst_rev = edt.pi.srcport;
3837 /* check if it is RTP Version 2 */
3838 if (!get_int_value_from_proto_tree(edt.tree, "rtp", "rtp.version", &version_fwd) || version_fwd != 2) {
3839 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3840 "RTP Version != 2 isn't supported!");
3844 /* now we need the SSRC value of the current frame */
3845 if (!get_int_value_from_proto_tree(edt.tree, "rtp", "rtp.ssrc", &ssrc_fwd)) {
3846 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3847 "SSRC value couldn't be found!");
3851 /* Scan for rtpstream */
3853 /* search for reversed direction in the global rtp streams list */
3855 strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
3856 while (strinfo_list)
3858 strinfo = (rtp_stream_info_t*)(strinfo_list->data);
3859 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_fwd))
3860 && strinfo->src_port==port_src_fwd
3861 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_fwd))
3862 && strinfo->dest_port==port_dst_fwd)
3864 filtered_list = g_list_prepend(filtered_list, strinfo);
3867 if (ADDRESSES_EQUAL(&(strinfo->src_addr),&(ip_src_rev))
3868 && strinfo->src_port==port_src_rev
3869 && ADDRESSES_EQUAL(&(strinfo->dest_addr),&(ip_dst_rev))
3870 && strinfo->dest_port==port_dst_rev)
3873 filtered_list = g_list_append(filtered_list, strinfo);
3875 ssrc_rev = strinfo->ssrc;
3878 strinfo_list = g_list_next(strinfo_list);
3881 /* if more than one reverse streams found, we let the user choose the right one */
3883 rtpstream_dlg_show(filtered_list);
3902 /****************************************************************************/
3904 rtp_analysis_init(const char *dummy _U_,void* userdata _U_)
3906 rtp_analysis_cb(NULL, NULL);
3909 /****************************************************************************/
3911 register_tap_listener_rtp_analysis(void)
3913 register_stat_cmd_arg("rtp", rtp_analysis_init, NULL);
3915 #ifdef MAIN_MENU_USE_UIMANAGER
3917 register_stat_menu_item("_RTP/Stream Analysis...", REGISTER_STAT_GROUP_TELEPHONY,
3918 rtp_analysis_cb, NULL, NULL, NULL);