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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
54 #include <wsutil/file_util.h>
55 #include <wsutil/tempfile.h>
56 #include <wsutil/g711.h>
57 #include <wsutil/pint.h>
59 #include <epan/epan_dissect.h>
60 #include <epan/filesystem.h>
62 #include <epan/dissectors/packet-rtp.h>
63 #include <epan/rtp_pt.h>
64 #include <epan/addr_resolv.h>
65 #include <epan/stat_cmd_args.h>
66 #include <epan/strutil.h>
68 #include "../stat_menu.h"
71 #include "ui/alert_box.h"
72 #include "ui/last_open_dir.h"
73 #include "ui/progress_dlg.h"
74 #include "ui/simple_dialog.h"
75 #include "ui/utf8_entities.h"
78 #include "ui/gtk/gtkglobals.h"
79 #include "ui/gtk/dlg_utils.h"
80 #include "ui/gtk/file_dlg.h"
81 #include "ui/gtk/gui_utils.h"
82 #include "ui/gtk/gui_stat_menu.h"
83 #include "ui/gtk/pixmap_save.h"
84 #include "ui/gtk/main.h"
85 #include "ui/rtp_analysis.h"
86 #include "ui/rtp_stream.h"
87 #include "ui/gtk/rtp_stream_dlg.h"
88 #include "ui/gtk/stock_icons.h"
90 #ifdef HAVE_LIBPORTAUDIO
91 #include "ui/gtk/graph_analysis.h"
92 #include "ui/gtk/voip_calls.h"
93 #include "ui/gtk/rtp_player.h"
94 #endif /* HAVE_LIBPORTAUDIO */
96 #include "ui/gtk/old-gtk-compat.h"
98 #include "frame_tvbuff.h"
113 FOREGROUND_COLOR_COL
,
114 BACKGROUND_COLOR_COL
,
115 N_COLUMN
/* The number of columns */
117 /****************************************************************************/
120 #define NUM_GRAPH_ITEMS 100000
121 #define MAX_YSCALE 16
122 #define DEFAULT_YSCALE_INDEX 0 /* AUTO_MAX_YSCALE */
123 #define AUTO_MAX_YSCALE 0
125 #define GRAPH_FWD_JITTER 0
126 #define GRAPH_FWD_DIFF 1
127 #define GRAPH_FWD_DELTA 2
128 #define GRAPH_REV_JITTER 3
129 #define GRAPH_REV_DIFF 4
130 #define GRAPH_REV_DELTA 5
132 static guint32 yscale_max
[MAX_YSCALE
] = {
133 AUTO_MAX_YSCALE
, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000,
134 1000000, 2000000, 5000000, 10000000, 20000000, 50000000
137 #define MAX_PIXELS_PER_TICK 4
138 #define DEFAULT_PIXELS_PER_TICK_INDEX 2
139 static guint32 pixels_per_tick
[MAX_PIXELS_PER_TICK
] = {1, 2, 5, 10};
141 static const char *graph_descr
[MAX_GRAPHS
] = {
142 "Fwd Jitter", "Fwd Difference", "Fwd Delta", "Rvr Jitter", "Rvr Difference", "Rvr Delta"
146 #define MAX_TICK_VALUES 5
147 #define DEFAULT_TICK_VALUE_INDEX 1
148 static guint tick_interval_values
[MAX_TICK_VALUES
] = { 1, 10, 100, 1000, 10000 };
150 typedef struct _dialog_graph_graph_item_t
{
153 } dialog_graph_graph_item_t
;
155 typedef struct _dialog_graph_graph_t
{
156 struct _user_data_t
*ud
;
157 dialog_graph_graph_item_t items
[NUM_GRAPH_ITEMS
];
160 GtkWidget
*display_button
;
165 } dialog_graph_graph_t
;
168 typedef struct _dialog_graph_t
{
169 gboolean needs_redraw
;
170 gint32 interval
; /* measurement interval in ms */
171 guint32 last_interval
;
172 guint32 max_interval
; /* XXX max_interval and num_items are redundant */
176 struct _dialog_graph_graph_t graph
[MAX_GRAPHS
];
178 GtkWidget
*draw_area
;
179 #if GTK_CHECK_VERSION(2,22,0)
180 cairo_surface_t
*surface
;
184 GtkAdjustment
*scrollbar_adjustment
;
185 GtkWidget
*scrollbar
;
192 typedef struct _dialog_data_t
{
197 GtkWidget
*label_stats_fwd
;
198 GtkWidget
*label_stats_rev
;
199 GtkWidget
*selected_list
;
201 GtkTreeSelection
*selected_list_sel
;
202 gint selected_list_row
;
204 GtkWidget
*save_voice_as_w
;
205 GtkWidget
*save_csv_as_w
;
206 gulong notebook_signal_id
;
207 dialog_graph_t dialog_graph
;
210 #define OK_TEXT "[ Ok ]"
212 /* type of error when saving voice in a file didn't succeed */
215 TAP_RTP_WRONG_LENGTH
,
216 TAP_RTP_PADDING_ERROR
,
218 TAP_RTP_FILE_OPEN_ERROR
,
219 TAP_RTP_FILE_WRITE_ERROR
,
223 typedef struct _tap_rtp_save_info_t
{
226 error_type_t error_type
;
228 } tap_rtp_save_info_t
;
231 /* structure that holds the information about the forward and reversed direction */
232 struct _info_direction
{
233 tap_rtp_stat_t statinfo
;
234 tap_rtp_save_info_t saveinfo
;
237 #define SILENCE_PCMU (guint8)0xFF
238 #define SILENCE_PCMA (guint8)0x55
240 /* structure that holds general information about the connection
241 * and structures for both directions */
242 typedef struct _user_data_t
{
243 /* tap associated data*/
245 guint32 port_src_fwd
;
247 guint32 port_dst_fwd
;
250 guint32 port_src_rev
;
252 guint32 port_dst_rev
;
255 struct _info_direction forward
;
256 struct _info_direction reversed
;
261 /* dialog associated data */
268 static const gchar
*titles
[11] = {
282 #define SAVE_FORWARD_DIRECTION_MASK 0x01
283 #define SAVE_REVERSE_DIRECTION_MASK 0x02
284 #define SAVE_BOTH_DIRECTION_MASK (SAVE_FORWARD_DIRECTION_MASK|SAVE_REVERSE_DIRECTION_MASK)
286 #define SAVE_NONE_FORMAT 0
287 #define SAVE_WAV_FORMAT 1
288 #define SAVE_AU_FORMAT 2
289 #define SAVE_SW_FORMAT 3
290 #define SAVE_RAW_FORMAT 4
293 static void on_refresh_bt_clicked(GtkWidget
*bt _U_
, user_data_t
*user_data
);
295 /****************************************************************************/
297 enable_graph(dialog_graph_graph_t
*dgg
)
304 static void dialog_graph_reset(user_data_t
* user_data
);
308 /****************************************************************************/
311 /****************************************************************************/
312 /* when there is a [re]reading of packet's */
314 rtp_reset(void *user_data_arg
)
316 user_data_t
*user_data
= (user_data_t
*)user_data_arg
;
318 user_data
->forward
.statinfo
.first_packet
= TRUE
;
319 user_data
->reversed
.statinfo
.first_packet
= TRUE
;
320 user_data
->forward
.statinfo
.max_delta
= 0;
321 user_data
->reversed
.statinfo
.max_delta
= 0;
322 user_data
->forward
.statinfo
.max_jitter
= 0;
323 user_data
->reversed
.statinfo
.max_jitter
= 0;
324 user_data
->forward
.statinfo
.max_skew
= 0;
325 user_data
->reversed
.statinfo
.max_skew
= 0;
326 user_data
->forward
.statinfo
.mean_jitter
= 0;
327 user_data
->reversed
.statinfo
.mean_jitter
= 0;
328 user_data
->forward
.statinfo
.delta
= 0;
329 user_data
->reversed
.statinfo
.delta
= 0;
330 user_data
->forward
.statinfo
.diff
= 0;
331 user_data
->reversed
.statinfo
.diff
= 0;
332 user_data
->forward
.statinfo
.jitter
= 0;
333 user_data
->reversed
.statinfo
.jitter
= 0;
334 user_data
->forward
.statinfo
.skew
= 0;
335 user_data
->reversed
.statinfo
.skew
= 0;
336 user_data
->forward
.statinfo
.sumt
= 0;
337 user_data
->reversed
.statinfo
.sumt
= 0;
338 user_data
->forward
.statinfo
.sumTS
= 0;
339 user_data
->reversed
.statinfo
.sumTS
= 0;
340 user_data
->forward
.statinfo
.sumt2
= 0;
341 user_data
->reversed
.statinfo
.sumt2
= 0;
342 user_data
->forward
.statinfo
.sumtTS
= 0;
343 user_data
->reversed
.statinfo
.sumtTS
= 0;
344 user_data
->forward
.statinfo
.bandwidth
= 0;
345 user_data
->reversed
.statinfo
.bandwidth
= 0;
346 user_data
->forward
.statinfo
.total_bytes
= 0;
347 user_data
->reversed
.statinfo
.total_bytes
= 0;
348 user_data
->forward
.statinfo
.bw_start_index
= 0;
349 user_data
->reversed
.statinfo
.bw_start_index
= 0;
350 user_data
->forward
.statinfo
.bw_index
= 0;
351 user_data
->reversed
.statinfo
.bw_index
= 0;
352 user_data
->forward
.statinfo
.timestamp
= 0;
353 user_data
->reversed
.statinfo
.timestamp
= 0;
354 user_data
->forward
.statinfo
.max_nr
= 0;
355 user_data
->reversed
.statinfo
.max_nr
= 0;
356 user_data
->forward
.statinfo
.total_nr
= 0;
357 user_data
->reversed
.statinfo
.total_nr
= 0;
358 user_data
->forward
.statinfo
.sequence
= 0;
359 user_data
->reversed
.statinfo
.sequence
= 0;
360 user_data
->forward
.statinfo
.start_seq_nr
= 0;
361 user_data
->reversed
.statinfo
.start_seq_nr
= 1; /* 1 is ok (for statistics in reversed direction) */
362 user_data
->forward
.statinfo
.stop_seq_nr
= 0;
363 user_data
->reversed
.statinfo
.stop_seq_nr
= 0;
364 user_data
->forward
.statinfo
.cycles
= 0;
365 user_data
->reversed
.statinfo
.cycles
= 0;
366 user_data
->forward
.statinfo
.under
= FALSE
;
367 user_data
->reversed
.statinfo
.under
= FALSE
;
368 user_data
->forward
.statinfo
.start_time
= 0;
369 user_data
->reversed
.statinfo
.start_time
= 0;
370 user_data
->forward
.statinfo
.time
= 0;
371 user_data
->reversed
.statinfo
.time
= 0;
372 user_data
->forward
.statinfo
.reg_pt
= PT_UNDEFINED
;
373 user_data
->reversed
.statinfo
.reg_pt
= PT_UNDEFINED
;
375 user_data
->forward
.saveinfo
.count
= 0;
376 user_data
->reversed
.saveinfo
.count
= 0;
377 user_data
->forward
.saveinfo
.saved
= FALSE
;
378 user_data
->reversed
.saveinfo
.saved
= FALSE
;
380 /* clear the dialog box lists */
381 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data
->dlg
.list_fwd
))));
382 gtk_list_store_clear(GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW(user_data
->dlg
.list_rev
))));
384 /* reset graph info */
385 dialog_graph_reset(user_data
);
387 #ifdef HAVE_LIBPORTAUDIO
388 /* reset the RTP player */
391 /* XXX check for error at fclose? */
392 if (user_data
->forward
.saveinfo
.fp
!= NULL
)
393 fclose(user_data
->forward
.saveinfo
.fp
);
394 if (user_data
->reversed
.saveinfo
.fp
!= NULL
)
395 fclose(user_data
->reversed
.saveinfo
.fp
);
396 user_data
->forward
.saveinfo
.fp
= ws_fopen(user_data
->f_tempname
, "wb");
397 if (user_data
->forward
.saveinfo
.fp
== NULL
)
398 user_data
->forward
.saveinfo
.error_type
= TAP_RTP_FILE_OPEN_ERROR
;
399 user_data
->reversed
.saveinfo
.fp
= ws_fopen(user_data
->r_tempname
, "wb");
400 if (user_data
->reversed
.saveinfo
.fp
== NULL
)
401 user_data
->reversed
.saveinfo
.error_type
= TAP_RTP_FILE_OPEN_ERROR
;
405 /****************************************************************************/
407 rtp_packet_add_graph(dialog_graph_graph_t
*dgg
, tap_rtp_stat_t
*statinfo
, packet_info
*pinfo
, guint32 value
)
409 dialog_graph_graph_item_t
*it
;
414 * We sometimes get called when dgg is disabled.
415 * This is a bug since the tap listener should be removed first
421 dgg
->ud
->dlg
.dialog_graph
.needs_redraw
= TRUE
;
424 * Find which interval this is supposed to go in and store the
425 * interval index as idx
427 if (dgg
->ud
->dlg
.dialog_graph
.start_time
== -1) { /* it is the first */
428 dgg
->ud
->dlg
.dialog_graph
.start_time
= statinfo
->start_time
;
430 rtp_time
= nstime_to_msec(&pinfo
->rel_ts
) - dgg
->ud
->dlg
.dialog_graph
.start_time
;
434 idx
= (guint32
)(rtp_time
)/dgg
->ud
->dlg
.dialog_graph
.interval
;
436 /* some sanity checks */
437 if (idx
>= NUM_GRAPH_ITEMS
) {
441 /* update num_items */
442 if (idx
> dgg
->ud
->dlg
.dialog_graph
.num_items
) {
443 dgg
->ud
->dlg
.dialog_graph
.num_items
= idx
;
444 dgg
->ud
->dlg
.dialog_graph
.max_interval
= idx
*dgg
->ud
->dlg
.dialog_graph
.interval
;
448 * Find the appropriate dialog_graph_graph_item_t structure
450 it
= &dgg
->items
[idx
];
453 * Use the max value to highlight RTP problems
455 if (value
> it
->value
) {
458 it
->flags
= it
->flags
| statinfo
->flags
;
463 /****************************************************************************/
464 /* here we can redraw the output */
467 rtp_draw(void *prs _U_
)
472 /* forward declarations */
473 static void add_to_list(GtkWidget
*list
, user_data_t
* user_data
, guint32 number
, guint16 seq_num
, guint32 timestamp
,
474 double delta
, double jitter
, double skew
, double bandwidth
, gchar
*status
, gboolean marker
,
475 gchar
*timeStr
, guint32 pkt_len
, gchar
*color_str
, guint32 flags
);
477 static int rtp_packet_add_info(GtkWidget
*list
, user_data_t
* user_data
,
478 tap_rtp_stat_t
*statinfo
, packet_info
*pinfo
,
479 const struct _rtp_info
*rtpinfo
);
481 static int rtp_packet_save_payload(tap_rtp_save_info_t
*saveinfo
,
482 tap_rtp_stat_t
*statinfo
,
484 const struct _rtp_info
*rtpinfo
);
487 /****************************************************************************/
488 /* whenever a RTP packet is seen by the tap listener */
490 rtp_packet(void *user_data_arg
, packet_info
*pinfo
, epan_dissect_t
*edt _U_
, const void *rtpinfo_arg
)
492 user_data_t
*user_data
= (user_data_t
*)user_data_arg
;
493 const struct _rtp_info
*rtpinfo
= (struct _rtp_info
*)rtpinfo_arg
;
494 gboolean rtp_selected
= FALSE
;
496 /* we ignore packets that are not displayed */
497 if (pinfo
->fd
->flags
.passed_dfilter
== 0)
499 /* also ignore RTP Version != 2 */
500 else if (rtpinfo
->info_version
!= 2)
502 /* is it the forward direction? */
503 else if (user_data
->ssrc_fwd
== rtpinfo
->info_sync_src
504 && (CMP_ADDRESS(&(user_data
->src_fwd
), &(pinfo
->src
)) == 0)
505 && (user_data
->port_src_fwd
== pinfo
->srcport
)
506 && (CMP_ADDRESS(&(user_data
->dst_fwd
), &(pinfo
->dst
)) == 0)
507 && (user_data
->port_dst_fwd
== pinfo
->destport
)) {
508 rtp_packet_analyse(&(user_data
->forward
.statinfo
), pinfo
, rtpinfo
);
509 rtp_packet_add_graph(&(user_data
->dlg
.dialog_graph
.graph
[GRAPH_FWD_JITTER
]),
510 &(user_data
->forward
.statinfo
), pinfo
,
511 (guint32
)(user_data
->forward
.statinfo
.jitter
*1000));
512 rtp_packet_add_graph(&(user_data
->dlg
.dialog_graph
.graph
[GRAPH_FWD_DIFF
]),
513 &(user_data
->forward
.statinfo
), pinfo
,
514 (guint32
)(user_data
->forward
.statinfo
.diff
*1000));
515 rtp_packet_add_graph(&(user_data
->dlg
.dialog_graph
.graph
[GRAPH_FWD_DELTA
]),
516 &(user_data
->forward
.statinfo
), pinfo
,
517 (guint32
)(user_data
->forward
.statinfo
.delta
*1000));
518 rtp_packet_add_info(user_data
->dlg
.list_fwd
, user_data
,
519 &(user_data
->forward
.statinfo
), pinfo
, rtpinfo
);
520 rtp_packet_save_payload(&(user_data
->forward
.saveinfo
),
521 &(user_data
->forward
.statinfo
), pinfo
, rtpinfo
);
524 /* is it the reversed direction? */
525 else if (user_data
->ssrc_rev
== rtpinfo
->info_sync_src
526 && (CMP_ADDRESS(&(user_data
->src_rev
), &(pinfo
->src
)) == 0)
527 && (user_data
->port_src_rev
== pinfo
->srcport
)
528 && (CMP_ADDRESS(&(user_data
->dst_rev
), &(pinfo
->dst
)) == 0)
529 && (user_data
->port_dst_rev
== pinfo
->destport
)) {
530 rtp_packet_analyse(&(user_data
->reversed
.statinfo
), pinfo
, rtpinfo
);
531 rtp_packet_add_graph(&(user_data
->dlg
.dialog_graph
.graph
[GRAPH_REV_JITTER
]),
532 &(user_data
->reversed
.statinfo
), pinfo
,
533 (guint32
)(user_data
->reversed
.statinfo
.jitter
*1000));
534 rtp_packet_add_graph(&(user_data
->dlg
.dialog_graph
.graph
[GRAPH_REV_DIFF
]),
535 &(user_data
->reversed
.statinfo
), pinfo
,
536 (guint32
)(user_data
->reversed
.statinfo
.diff
*1000));
537 rtp_packet_add_graph(&(user_data
->dlg
.dialog_graph
.graph
[GRAPH_REV_DELTA
]),
538 &(user_data
->reversed
.statinfo
), pinfo
,
539 (guint32
)(user_data
->reversed
.statinfo
.delta
*1000));
540 rtp_packet_add_info(user_data
->dlg
.list_rev
, user_data
,
541 &(user_data
->reversed
.statinfo
), pinfo
, rtpinfo
);
542 rtp_packet_save_payload(&(user_data
->reversed
.saveinfo
),
543 &(user_data
->reversed
.statinfo
), pinfo
, rtpinfo
);
546 /* add this RTP for future listening using the RTP Player*/
548 #ifdef HAVE_LIBPORTAUDIO
549 add_rtp_packet(rtpinfo
, pinfo
);
557 Replaced by using the strings instead.
558 static const GdkColor COLOR_DEFAULT = {0, 0xffff, 0xffff, 0xffff};
559 static const GdkColor COLOR_ERROR = {0, 0xffff, 0xbfff, 0xbfff};
560 static const GdkColor COLOR_WARNING = {0, 0xffff, 0xdfff, 0xbfff};
561 static const GdkColor COLOR_CN = {0, 0xbfff, 0xbfff, 0xffff};
562 GdkColor yellow = {0, 0xffff, 0xffff, 0x0000};
563 COLOR_T_EVENT g_snprintf(color_str, sizeof(color_str), "#ef8c bfff ffff");
564 static const GdkColor COLOR_FOREGROUND = {0, 0x0000, 0x0000, 0x0000};
566 /****************************************************************************/
567 /* adds statistics information from the packet to the list */
569 rtp_packet_add_info(GtkWidget
*list
, user_data_t
* user_data
,
570 tap_rtp_stat_t
*statinfo
, packet_info
*pinfo
,
571 const struct _rtp_info
*rtpinfo
)
580 then
= pinfo
->fd
->abs_ts
.secs
;
581 msecs
= (guint16
)(pinfo
->fd
->abs_ts
.nsecs
/1000000);
582 tm_tmp
= localtime(&then
);
583 g_snprintf(timeStr
, sizeof(timeStr
), "%02d/%02d/%04d %02d:%02d:%02d.%03d",
586 tm_tmp
->tm_year
+ 1900,
592 /* Default to using black on white text if nothing below overrides it */
593 g_snprintf(color_str
, sizeof(color_str
), "#ffffffffffff");
595 if (statinfo
->pt
== PT_CN
) {
596 g_snprintf(status
, sizeof(status
), "Comfort noise (PT=13, RFC 3389)");
597 /* color = COLOR_CN; */
598 g_snprintf(color_str
, sizeof(color_str
), "#bfffbfffffff");
600 else if (statinfo
->pt
== PT_CN_OLD
) {
601 g_snprintf(status
, sizeof(status
), "Comfort noise (PT=19, reserved)");
602 /* color = COLOR_CN; */
603 g_snprintf(color_str
, sizeof(color_str
), "#bfffbfffffff");
605 else if (statinfo
->flags
& STAT_FLAG_WRONG_SEQ
) {
606 g_snprintf(status
, sizeof(status
), "Wrong sequence nr.");
607 /* color = COLOR_ERROR; */
608 g_snprintf(color_str
, sizeof(color_str
), "#ffffbfffbfff");
610 else if (statinfo
->flags
& STAT_FLAG_DUP_PKT
) {
611 g_snprintf(status
, sizeof(status
), "Suspected duplicate(MAC address) only delta time calculated");
612 /* color = Yellow; */
613 g_snprintf(color_str
, sizeof(color_str
), "#ffffffff0000");
615 else if (statinfo
->flags
& STAT_FLAG_REG_PT_CHANGE
) {
616 if (statinfo
->flags
& STAT_FLAG_PT_T_EVENT
) {
617 g_snprintf(status
, sizeof(status
), "Payload changed to PT=%u telephone/event", statinfo
->pt
);
619 g_snprintf(status
, sizeof(status
), "Payload changed to PT=%u", statinfo
->pt
);
621 /* color = COLOR_WARNING; */
622 g_snprintf(color_str
, sizeof(color_str
), "#ffffdfffbfff");
624 else if (statinfo
->flags
& STAT_FLAG_WRONG_TIMESTAMP
) {
625 g_snprintf(status
, sizeof(status
), "Incorrect timestamp");
626 /* color = COLOR_WARNING; */
627 g_snprintf(color_str
, sizeof(color_str
), "#ffffdfffbfff");
629 else if ((statinfo
->flags
& STAT_FLAG_PT_CHANGE
)
630 && !(statinfo
->flags
& STAT_FLAG_FIRST
)
631 && !(statinfo
->flags
& STAT_FLAG_PT_CN
)
632 && (statinfo
->flags
& STAT_FLAG_FOLLOW_PT_CN
)
633 && !(statinfo
->flags
& STAT_FLAG_MARKER
)) {
634 g_snprintf(status
, sizeof(status
), "Marker missing?");
635 /* color = COLOR_WARNING; */
636 g_snprintf(color_str
, sizeof(color_str
), "#ffffdfffbfff");
637 }else if (statinfo
->flags
& STAT_FLAG_PT_T_EVENT
) {
638 g_snprintf(status
, sizeof(status
), "PT=%u telephone/event", statinfo
->pt
);
640 /* color = COLOR_T_EVENT; */
641 g_snprintf(color_str
, sizeof(color_str
), "#ef8cbfffffff");
643 if (statinfo
->flags
& STAT_FLAG_MARKER
) {
644 /* color = COLOR_WARNING; */
645 g_snprintf(color_str
, sizeof(color_str
), "#ffffdfffbfff");
647 g_snprintf(status
, sizeof(status
), OK_TEXT
);
649 /* is this the first packet we got in this direction? */
650 if (statinfo
->flags
& STAT_FLAG_FIRST
) {
651 add_to_list(list
, user_data
,
652 pinfo
->fd
->num
, rtpinfo
->info_seq_num
,
659 rtpinfo
->info_marker_set
,
660 timeStr
, pinfo
->fd
->pkt_len
,
665 add_to_list(list
, user_data
,
666 pinfo
->fd
->num
, rtpinfo
->info_seq_num
,
673 rtpinfo
->info_marker_set
,
674 timeStr
, pinfo
->fd
->pkt_len
,
681 #define MAX_SILENCE_TICKS 1000000
682 /****************************************************************************/
684 rtp_packet_save_payload(tap_rtp_save_info_t
*saveinfo
,
685 tap_rtp_stat_t
*statinfo
,
687 const struct _rtp_info
*rtpinfo
)
694 /* is this the first packet we got in this direction? */
695 if (statinfo
->flags
& STAT_FLAG_FIRST
) {
696 if (saveinfo
->fp
== NULL
) {
697 saveinfo
->saved
= FALSE
;
698 saveinfo
->error_type
= TAP_RTP_FILE_OPEN_ERROR
;
701 saveinfo
->saved
= TRUE
;
704 /* save the voice information */
705 /* if there was already an error, we quit */
706 if (saveinfo
->saved
== FALSE
)
709 /* if the captured length and packet length aren't equal, we quit
710 * if also the RTP dissector thinks there is some information missing */
711 if ((pinfo
->fd
->pkt_len
!= pinfo
->fd
->cap_len
)
712 && (!rtpinfo
->info_all_data_present
)) {
713 saveinfo
->saved
= FALSE
;
714 saveinfo
->error_type
= TAP_RTP_WRONG_LENGTH
;
718 /* if padding bit is set, but the padding count is bigger
719 * then the whole RTP data - error with padding count */
720 if ( (rtpinfo
->info_padding_set
!= FALSE
)
721 && (rtpinfo
->info_padding_count
> rtpinfo
->info_payload_len
) ) {
722 saveinfo
->saved
= FALSE
;
723 saveinfo
->error_type
= TAP_RTP_PADDING_ERROR
;
727 /* do we need to insert some silence? */
728 if ((rtpinfo
->info_marker_set
)
729 && ! (statinfo
->flags
& STAT_FLAG_FIRST
)
730 && ! (statinfo
->flags
& STAT_FLAG_WRONG_TIMESTAMP
)
731 && (statinfo
->delta_timestamp
> (rtpinfo
->info_payload_len
- rtpinfo
->info_padding_count
)) ) {
732 /* the amount of silence should be the difference between
733 * the last timestamp and the current one minus x
734 * x should equal the amount of information in the last frame
735 * XXX not done yet */
737 (i
< (statinfo
->delta_timestamp
- rtpinfo
->info_payload_len
- rtpinfo
->info_padding_count
))
738 && (i
< MAX_SILENCE_TICKS
);
740 switch (statinfo
->reg_pt
) {
751 nchars
= fwrite(&tmp
, 1, 1, saveinfo
->fp
);
753 /* Write error or short write */
754 saveinfo
->saved
= FALSE
;
755 saveinfo
->error_type
= TAP_RTP_FILE_WRITE_ERROR
;
760 fflush(saveinfo
->fp
);
764 if ((rtpinfo
->info_payload_type
== PT_CN
)
765 || (rtpinfo
->info_payload_type
== PT_CN_OLD
)) {
767 /* all other payloads */
769 if (!rtpinfo
->info_all_data_present
) {
770 /* Not all the data was captured. */
771 saveinfo
->saved
= FALSE
;
772 saveinfo
->error_type
= TAP_RTP_SHORT_FRAME
;
776 /* we put the pointer at the beginning of the RTP
777 * payload, that is, at the beginning of the RTP data
778 * plus the offset of the payload from the beginning
780 data
= rtpinfo
->info_data
+ rtpinfo
->info_payload_offset
;
781 nchars
= fwrite(data
, sizeof(unsigned char),
782 (rtpinfo
->info_payload_len
- rtpinfo
->info_padding_count
),
784 if (nchars
!= (rtpinfo
->info_payload_len
- rtpinfo
->info_padding_count
)) {
785 /* Write error or short write */
786 saveinfo
->saved
= FALSE
;
787 saveinfo
->error_type
= TAP_RTP_FILE_WRITE_ERROR
;
790 saveinfo
->count
+= (rtpinfo
->info_payload_len
- rtpinfo
->info_padding_count
);
792 fflush(saveinfo
->fp
);
793 saveinfo
->saved
= TRUE
;
801 /****************************************************************************/
804 /****************************************************************************/
806 /****************************************************************************/
807 /* close the dialog window and remove the tap listener */
809 on_destroy(GtkWidget
*win _U_
, user_data_t
*user_data
)
811 /* remove tap listener */
812 remove_tap_listener(user_data
);
814 /* close and remove temporary files */
815 if (user_data
->forward
.saveinfo
.fp
!= NULL
)
816 fclose(user_data
->forward
.saveinfo
.fp
);
817 if (user_data
->reversed
.saveinfo
.fp
!= NULL
)
818 fclose(user_data
->reversed
.saveinfo
.fp
);
819 /*XXX: test for error **/
820 ws_remove(user_data
->f_tempname
);
821 ws_remove(user_data
->r_tempname
);
823 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
824 /* destroy save_voice_as window if open */
825 if (user_data
->dlg
.save_voice_as_w
!= NULL
)
826 window_destroy(user_data
->dlg
.save_voice_as_w
);
828 /* destroy graph window if open */
829 if (user_data
->dlg
.dialog_graph
.window
!= NULL
)
830 window_destroy(user_data
->dlg
.dialog_graph
.window
);
832 /* disable the "switch_page" signal in the dlg, otherwise will be called when the windows
833 * is destroyed and cause an exception using GTK1*/
834 g_signal_handler_disconnect(user_data
->dlg
.notebook
, user_data
->dlg
.notebook_signal_id
);
836 g_free(user_data
->f_tempname
);
837 g_free(user_data
->r_tempname
);
842 /****************************************************************************/
844 on_notebook_switch_page(GtkNotebook
*notebook _U_
,
847 user_data_t
*user_data
)
849 user_data
->dlg
.selected_list
=
850 (page_num
== 0) ? user_data
->dlg
.list_fwd
: user_data
->dlg
.list_rev
;
852 user_data
->dlg
.selected_list_row
= 0;
855 /****************************************************************************/
858 on_list_select_row(GtkTreeSelection
*selection
,
859 user_data_t
*user_data
/*gpointer data */)
861 user_data
->dlg
.selected_list_sel
= selection
;
865 /****************************************************************************/
867 dialog_graph_set_title(user_data_t
* user_data
)
871 if (!user_data
->dlg
.dialog_graph
.window
) {
875 title
= g_strdup_printf("RTP Graph Analysis Forward: %s:%u to %s:%u Reverse: %s:%u to %s:%u",
876 get_addr_name(&(user_data
->src_fwd
)),
877 user_data
->port_src_fwd
,
878 get_addr_name(&(user_data
->dst_fwd
)),
879 user_data
->port_dst_fwd
,
880 get_addr_name(&(user_data
->src_rev
)),
881 user_data
->port_src_rev
,
882 get_addr_name(&(user_data
->dst_rev
)),
883 user_data
->port_dst_rev
);
885 gtk_window_set_title(GTK_WINDOW(user_data
->dlg
.dialog_graph
.window
), title
);
891 /****************************************************************************/
893 dialog_graph_reset(user_data_t
* user_data
)
897 user_data
->dlg
.dialog_graph
.needs_redraw
= TRUE
;
898 for (i
= 0; i
< MAX_GRAPHS
; i
++) {
899 for (j
= 0; j
< NUM_GRAPH_ITEMS
; j
++) {
900 dialog_graph_graph_item_t
*dggi
;
901 dggi
=&user_data
->dlg
.dialog_graph
.graph
[i
].items
[j
];
906 user_data
->dlg
.dialog_graph
.last_interval
= 0xffffffff;
907 user_data
->dlg
.dialog_graph
.max_interval
= 0;
908 user_data
->dlg
.dialog_graph
.num_items
= 0;
910 /* create the color titles near the filter buttons */
911 for (i
= 0; i
< MAX_GRAPHS
; i
++) {
913 if (i
< (MAX_GRAPHS
/2)) {
914 g_snprintf(user_data
->dlg
.dialog_graph
.graph
[i
].title
,
915 sizeof(user_data
->dlg
.dialog_graph
.graph
[0].title
),
916 "%s: %s:%u to %s:%u (SSRC=0x%X)",
918 get_addr_name(&(user_data
->src_fwd
)),
919 user_data
->port_src_fwd
,
920 get_addr_name(&(user_data
->dst_fwd
)),
921 user_data
->port_dst_fwd
,
922 user_data
->ssrc_fwd
);
925 g_snprintf(user_data
->dlg
.dialog_graph
.graph
[i
].title
,
926 sizeof(user_data
->dlg
.dialog_graph
.graph
[0].title
),
927 "%s: %s:%u to %s:%u (SSRC=0x%X)",
929 get_addr_name(&(user_data
->src_rev
)),
930 user_data
->port_src_rev
,
931 get_addr_name(&(user_data
->dst_rev
)),
932 user_data
->port_dst_rev
,
933 user_data
->ssrc_rev
);
937 dialog_graph_set_title(user_data
);
940 /****************************************************************************/
942 get_it_value(dialog_graph_graph_t
*dgg
, int idx
)
944 dialog_graph_graph_item_t
*it
;
946 it
= &dgg
->items
[idx
];
951 /****************************************************************************/
953 print_time_scale_string(char *buf
, int buf_len
, guint32 t
)
956 g_snprintf(buf
, buf_len
, "%ds", t
/1000000);
957 } else if (t
>= 1000000) {
958 g_snprintf(buf
, buf_len
, "%d.%03ds", t
/1000000, (t
%1000000)/1000);
959 } else if (t
>= 10000) {
960 g_snprintf(buf
, buf_len
, "%dms", t
/1000);
961 } else if (t
>= 1000) {
962 g_snprintf(buf
, buf_len
, "%d.%03dms", t
/1000, t
%1000);
964 g_snprintf(buf
, buf_len
, "%dus", t
);
968 /****************************************************************************/
970 dialog_graph_draw(user_data_t
* user_data
)
973 guint32 last_interval
, first_interval
, interval_delta
, delta_multiplier
;
974 gint32 current_interval
;
975 guint32 left_x_border
;
976 guint32 right_x_border
;
977 guint32 top_y_border
;
978 guint32 bottom_y_border
;
980 int label_width
, label_height
;
981 int label_width_mid
, label_height_mid
;
982 guint32 draw_width
, draw_height
;
983 char label_string
[15];
984 GtkAllocation widget_alloc
;
988 guint32 num_time_intervals
;
989 guint32 max_value
; /* max value of seen data */
990 guint32 max_y
; /* max value of the Y scale */
992 if (!user_data
->dlg
.dialog_graph
.needs_redraw
) {
995 user_data
->dlg
.dialog_graph
.needs_redraw
= FALSE
;
998 * Find the length of the intervals we have data for
999 * so we know how large arrays we need to malloc()
1001 num_time_intervals
= user_data
->dlg
.dialog_graph
.num_items
;
1002 /* if there isnt anything to do, just return */
1003 if (num_time_intervals
== 0) {
1006 num_time_intervals
+=1;
1007 /* XXX move this check to _packet() */
1008 if (num_time_intervals
>NUM_GRAPH_ITEMS
) {
1009 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
, "RTP Graph error. There are too many entries, bailing out");
1014 * find the max value so we can autoscale the y axis
1017 for (i
= 0; i
< MAX_GRAPHS
; i
++) {
1020 if (!user_data
->dlg
.dialog_graph
.graph
[i
].display
) {
1023 for (idx
= 0; (guint32
)(idx
) < num_time_intervals
; idx
++) {
1026 val
= get_it_value(&user_data
->dlg
.dialog_graph
.graph
[i
], idx
);
1028 /* keep track of the max value we have encountered */
1029 if (val
>max_value
) {
1036 * Clear out old plot
1038 #if GTK_CHECK_VERSION(2,22,0)
1039 cr
= cairo_create (user_data
->dlg
.dialog_graph
.surface
);
1041 cr
= gdk_cairo_create (user_data
->dlg
.dialog_graph
.pixmap
);
1043 cairo_set_source_rgb (cr
, 1, 1, 1);
1044 gtk_widget_get_allocation(user_data
->dlg
.dialog_graph
.draw_area
, &widget_alloc
);
1045 cairo_rectangle (cr
,
1049 widget_alloc
.height
);
1054 * Calculate the y scale we should use
1056 if (user_data
->dlg
.dialog_graph
.max_y_units
== AUTO_MAX_YSCALE
) {
1057 max_y
= yscale_max
[MAX_YSCALE
-1];
1058 for (i
= MAX_YSCALE
-1; i
> 0; i
--) {
1059 if (max_value
<yscale_max
[i
]) {
1060 max_y
= yscale_max
[i
];
1064 /* the user had specified an explicit y scale to use */
1065 max_y
= user_data
->dlg
.dialog_graph
.max_y_units
;
1069 * Calculate size of borders surrounding the plot
1070 * The border on the right side needs to be adjusted depending
1071 * on the width of the text labels.
1073 print_time_scale_string(label_string
, sizeof(label_string
), max_y
);
1074 layout
= gtk_widget_create_pango_layout(user_data
->dlg
.dialog_graph
.draw_area
, label_string
);
1075 pango_layout_get_pixel_size(layout
, &label_width
, &label_height
);
1076 print_time_scale_string(label_string
, sizeof(label_string
), max_y
*5/10);
1077 layout
= gtk_widget_create_pango_layout(user_data
->dlg
.dialog_graph
.draw_area
, label_string
);
1078 pango_layout_get_pixel_size(layout
, &label_width_mid
, &label_height_mid
);
1079 if (label_width_mid
> label_width
) {
1080 label_width
= label_width_mid
;
1081 label_height
= label_height_mid
;
1085 right_x_border
= label_width
+ 20;
1087 bottom_y_border
= label_height
+ 20;
1091 * Calculate the size of the drawing area for the actual plot
1093 draw_width
= user_data
->dlg
.dialog_graph
.surface_width
- right_x_border
- left_x_border
;
1094 draw_height
= user_data
->dlg
.dialog_graph
.surface_height
- top_y_border
- bottom_y_border
;
1098 * Draw the y axis and labels
1099 * (we always draw the y scale with 11 ticks along the axis)
1101 #if GTK_CHECK_VERSION(2,22,0)
1102 cr
= cairo_create (user_data
->dlg
.dialog_graph
.surface
);
1104 cr
= gdk_cairo_create (user_data
->dlg
.dialog_graph
.pixmap
);
1106 cairo_set_line_width (cr
, 1.0);
1108 user_data
->dlg
.dialog_graph
.surface_width
- right_x_border
+ 1.5,
1109 top_y_border
+ 0.5);
1111 user_data
->dlg
.dialog_graph
.surface_width
- right_x_border
+ 1.5,
1112 user_data
->dlg
.dialog_graph
.surface_height
- bottom_y_border
+ 0.5);
1116 for (i
= 0; i
<= 10; i
++) {
1121 /* first, middle and last tick are slightly longer */
1125 #if GTK_CHECK_VERSION(2,22,0)
1126 cr
= cairo_create (user_data
->dlg
.dialog_graph
.surface
);
1128 cr
= gdk_cairo_create (user_data
->dlg
.dialog_graph
.pixmap
);
1130 cairo_set_line_width (cr
, 1.0);
1132 user_data
->dlg
.dialog_graph
.surface_width
- right_x_border
+ 1.5,
1133 user_data
->dlg
.dialog_graph
.surface_height
- bottom_y_border
- draw_height
*i
/10 + 0.5);
1136 user_data
->dlg
.dialog_graph
.surface_width
- right_x_border
+ 1.5 + xwidth
,
1137 user_data
->dlg
.dialog_graph
.surface_height
- bottom_y_border
- draw_height
*i
/10 + 0.5);
1140 /* draw the labels */
1142 print_time_scale_string(label_string
, sizeof(label_string
), (max_y
*i
/10));
1143 pango_layout_set_text(layout
, label_string
, -1);
1144 pango_layout_get_pixel_size(layout
, &lwidth
, NULL
);
1145 #if GTK_CHECK_VERSION(2,22,0)
1146 cr
= cairo_create (user_data
->dlg
.dialog_graph
.surface
);
1148 cr
= gdk_cairo_create (user_data
->dlg
.dialog_graph
.pixmap
);
1151 user_data
->dlg
.dialog_graph
.surface_width
- right_x_border
+ 15 + label_width
- lwidth
,
1152 user_data
->dlg
.dialog_graph
.surface_height
- bottom_y_border
- draw_height
*i
/10 - label_height
/2);
1153 pango_cairo_show_layout (cr
, layout
);
1158 print_time_scale_string(label_string
, sizeof(label_string
), (max_y
*i
/10));
1159 pango_layout_set_text(layout
, label_string
, -1);
1160 pango_layout_get_pixel_size(layout
, &lwidth
, NULL
);
1161 #if GTK_CHECK_VERSION(2,22,0)
1162 cr
= cairo_create (user_data
->dlg
.dialog_graph
.surface
);
1164 cr
= gdk_cairo_create (user_data
->dlg
.dialog_graph
.pixmap
);
1167 user_data
->dlg
.dialog_graph
.surface_width
- right_x_border
+ 15 + label_width
- lwidth
,
1168 user_data
->dlg
.dialog_graph
.surface_height
- bottom_y_border
- draw_height
*i
/10 - label_height
/2);
1169 pango_cairo_show_layout (cr
, layout
);
1174 print_time_scale_string(label_string
, sizeof(label_string
), (max_y
*i
/10));
1175 pango_layout_set_text(layout
, label_string
, -1);
1176 pango_layout_get_pixel_size(layout
, &lwidth
, NULL
);
1177 #if GTK_CHECK_VERSION(2,22,0)
1178 cr
= cairo_create (user_data
->dlg
.dialog_graph
.surface
);
1180 cr
= gdk_cairo_create (user_data
->dlg
.dialog_graph
.pixmap
);
1183 user_data
->dlg
.dialog_graph
.surface_width
- right_x_border
+ 15 + label_width
- lwidth
,
1184 user_data
->dlg
.dialog_graph
.surface_height
- bottom_y_border
- draw_height
*i
/10 - label_height
/2);
1185 pango_cairo_show_layout (cr
, layout
);
1194 * if we have not specified the last_interval via the gui,
1195 * then just pick the current end of the capture so that is scrolls
1196 * nicely when doing live captures
1198 if (user_data
->dlg
.dialog_graph
.last_interval
== 0xffffffff) {
1199 last_interval
= user_data
->dlg
.dialog_graph
.max_interval
;
1201 last_interval
= user_data
->dlg
.dialog_graph
.last_interval
;
1208 /* plot the x-scale */
1209 #if GTK_CHECK_VERSION(2,22,0)
1210 cr
= cairo_create (user_data
->dlg
.dialog_graph
.surface
);
1212 cr
= gdk_cairo_create (user_data
->dlg
.dialog_graph
.pixmap
);
1214 cairo_set_line_width (cr
, 1.0);
1216 left_x_border
+ 0.5,
1217 user_data
->dlg
.dialog_graph
.surface_height
-bottom_y_border
+ 1.5);
1219 user_data
->dlg
.dialog_graph
.surface_width
- right_x_border
+ 1.5,
1220 user_data
->dlg
.dialog_graph
.surface_height
- bottom_y_border
+ 1.5);
1224 if ((last_interval
/user_data
->dlg
.dialog_graph
.interval
) > draw_width
/user_data
->dlg
.dialog_graph
.pixels_per_tick
+1) {
1225 first_interval
= (last_interval
/user_data
->dlg
.dialog_graph
.interval
)
1226 - draw_width
/user_data
->dlg
.dialog_graph
.pixels_per_tick
+ 1;
1227 first_interval
*= user_data
->dlg
.dialog_graph
.interval
;
1233 delta_multiplier
= 5;
1234 while (interval_delta
<((last_interval
-first_interval
)/10)) {
1235 interval_delta
*=delta_multiplier
;
1236 if (delta_multiplier
== 5) {
1237 delta_multiplier
= 2;
1239 delta_multiplier
= 5;
1243 for (current_interval
= last_interval
;
1244 current_interval
> (gint32
)first_interval
;
1245 current_interval
= current_interval
-user_data
->dlg
.dialog_graph
.interval
) {
1248 /* if pixels_per_tick is <5, only draw every 10 ticks */
1249 if ((user_data
->dlg
.dialog_graph
.pixels_per_tick
<10)
1250 && (current_interval
%(10*user_data
->dlg
.dialog_graph
.interval
))) {
1254 if (current_interval
%interval_delta
) {
1260 x
= draw_width
+left_x_border
1261 - (((last_interval
-current_interval
)/user_data
->dlg
.dialog_graph
.interval
)
1262 * user_data
->dlg
.dialog_graph
.pixels_per_tick
);
1263 #if GTK_CHECK_VERSION(2,22,0)
1264 cr
= cairo_create (user_data
->dlg
.dialog_graph
.surface
);
1266 cr
= gdk_cairo_create (user_data
->dlg
.dialog_graph
.pixmap
);
1268 cairo_set_line_width (cr
, 1.0);
1270 x
- 1 - user_data
->dlg
.dialog_graph
.pixels_per_tick
/2 + 0.5,
1271 user_data
->dlg
.dialog_graph
.surface_height
-bottom_y_border
+ 1.5);
1272 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);
1277 if (user_data
->dlg
.dialog_graph
.interval
>= 1000) {
1278 g_snprintf(label_string
, sizeof(label_string
),
1279 "%ds", current_interval
/1000);
1280 } else if (user_data
->dlg
.dialog_graph
.interval
>= 100) {
1281 g_snprintf(label_string
, sizeof(label_string
),
1282 "%d.%1ds", current_interval
/1000, (current_interval
/100)%10);
1283 } else if (user_data
->dlg
.dialog_graph
.interval
>= 10) {
1284 g_snprintf(label_string
, sizeof(label_string
),
1285 "%d.%2ds", current_interval
/1000, (current_interval
/10)%100);
1287 g_snprintf(label_string
, sizeof(label_string
),
1288 "%d.%3ds", current_interval
/1000, current_interval
%1000);
1290 pango_layout_set_text(layout
, label_string
, -1);
1291 pango_layout_get_pixel_size(layout
, &lwidth
, NULL
);
1292 #if GTK_CHECK_VERSION(2,22,0)
1293 cr
= cairo_create (user_data
->dlg
.dialog_graph
.surface
);
1295 cr
= gdk_cairo_create (user_data
->dlg
.dialog_graph
.pixmap
);
1298 x
- 1 - user_data
->dlg
.dialog_graph
.pixels_per_tick
/2 - lwidth
/2,
1299 user_data
->dlg
.dialog_graph
.surface_height
-bottom_y_border
+ 20);
1300 pango_cairo_show_layout (cr
, layout
);
1313 * Draw "x" for Sequence Errors and "m" for Marks
1315 /* Draw the labels Fwd and Rev */
1316 g_strlcpy(label_string
, UTF8_LEFTWARDS_ARROW
"Fwd", sizeof(label_string
));
1317 pango_layout_set_text(layout
, label_string
, -1);
1318 pango_layout_get_pixel_size(layout
, &lwidth
, NULL
);
1319 #if GTK_CHECK_VERSION(2,22,0)
1320 cr
= cairo_create (user_data
->dlg
.dialog_graph
.surface
);
1322 cr
= gdk_cairo_create (user_data
->dlg
.dialog_graph
.pixmap
);
1325 user_data
->dlg
.dialog_graph
.surface_width
- right_x_border
+ 33 - lwidth
,
1326 user_data
->dlg
.dialog_graph
.surface_height
- bottom_y_border
+ 3);
1327 pango_cairo_show_layout (cr
, layout
);
1331 g_strlcpy(label_string
, UTF8_LEFTWARDS_ARROW
"Rev", sizeof(label_string
));
1332 pango_layout_set_text(layout
, label_string
, -1);
1333 pango_layout_get_pixel_size(layout
, &lwidth
, NULL
);
1334 #if GTK_CHECK_VERSION(2,22,0)
1335 cr
= cairo_create (user_data
->dlg
.dialog_graph
.surface
);
1337 cr
= gdk_cairo_create (user_data
->dlg
.dialog_graph
.pixmap
);
1340 user_data
->dlg
.dialog_graph
.surface_width
- right_x_border
+ 33 - lwidth
,
1341 user_data
->dlg
.dialog_graph
.surface_height
- bottom_y_border
+ 3 + 9);
1342 pango_cairo_show_layout (cr
, layout
);
1346 /* Draw the marks */
1347 for (i
= MAX_GRAPHS
-1; i
>= 0; i
--) {
1349 guint32 x_pos
/*, prev_x_pos*/;
1351 /* XXX for fwd or rev, the flag info for jitter and diff is the same, and here I loop twice */
1352 if (!user_data
->dlg
.dialog_graph
.graph
[i
].display
) {
1355 /* initialize prev x/y to the low left corner of the graph */
1356 /*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;*/
1358 for (interval
= first_interval
+user_data
->dlg
.dialog_graph
.interval
;
1359 interval
<= last_interval
;
1360 interval
+= user_data
->dlg
.dialog_graph
.interval
) {
1361 x_pos
= draw_width
- 1
1362 - (user_data
->dlg
.dialog_graph
.pixels_per_tick
1363 * ((last_interval
-interval
)/user_data
->dlg
.dialog_graph
.interval
+ 1))
1366 if (user_data
->dlg
.dialog_graph
.graph
[i
].items
[interval
/user_data
->dlg
.dialog_graph
.interval
].flags
&
1367 (STAT_FLAG_WRONG_SEQ
|STAT_FLAG_MARKER
|STAT_FLAG_DUP_PKT
)) {
1368 if (user_data
->dlg
.dialog_graph
.graph
[i
].items
[interval
/user_data
->dlg
.dialog_graph
.interval
].flags
&
1369 (STAT_FLAG_WRONG_SEQ
|STAT_FLAG_DUP_PKT
)) {
1370 g_strlcpy(label_string
, "x", sizeof(label_string
));
1372 g_strlcpy(label_string
, "m", sizeof(label_string
));
1375 pango_layout_set_text(layout
, label_string
, -1);
1376 pango_layout_get_pixel_size(layout
, &lwidth
, NULL
);
1377 #if GTK_CHECK_VERSION(2,22,0)
1378 cr
= cairo_create (user_data
->dlg
.dialog_graph
.surface
);
1380 cr
= gdk_cairo_create (user_data
->dlg
.dialog_graph
.pixmap
);
1383 x_pos
- 1 - lwidth
/2,
1384 user_data
->dlg
.dialog_graph
.surface_height
- bottom_y_border
+ 3 + 7*(i
/2));
1385 pango_cairo_show_layout (cr
, layout
);
1391 /*prev_x_pos = x_pos;*/
1395 g_object_unref(G_OBJECT(layout
));
1398 * Loop over all graphs and draw them
1400 for (i
= MAX_GRAPHS
-1; i
>= 0; i
--) {
1402 guint32 x_pos
, y_pos
, /*prev_x_pos,*/ prev_y_pos
;
1403 if (! user_data
->dlg
.dialog_graph
.graph
[i
].display
) {
1406 /* initialize prev x/y to the low left corner of the graph */
1407 /*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;*/
1408 prev_y_pos
= draw_height
-1+top_y_border
;
1410 for (interval
= first_interval
+user_data
->dlg
.dialog_graph
.interval
;
1411 interval
<= last_interval
;
1412 interval
+=user_data
->dlg
.dialog_graph
.interval
) {
1414 x_pos
= draw_width
- 1
1415 - (user_data
->dlg
.dialog_graph
.pixels_per_tick
1416 * ((last_interval
-interval
)/user_data
->dlg
.dialog_graph
.interval
+1))
1418 val
= get_it_value(&user_data
->dlg
.dialog_graph
.graph
[i
],
1419 interval
/user_data
->dlg
.dialog_graph
.interval
);
1423 y_pos
= draw_height
- 1 - (val
*draw_height
)/max_y
+ top_y_border
;
1426 /* dont need to draw anything if the segment
1427 * is entirely above the top of the graph
1429 if ( (prev_y_pos
== 0) && (y_pos
== 0) ) {
1431 /*prev_x_pos = x_pos;*/
1436 #if GTK_CHECK_VERSION(2,22,0)
1437 cr
= cairo_create (user_data
->dlg
.dialog_graph
.surface
);
1439 cr
= gdk_cairo_create (user_data
->dlg
.dialog_graph
.pixmap
);
1441 gdk_cairo_set_source_rgba (cr
, &user_data
->dlg
.dialog_graph
.graph
[i
].rgba_color
);
1442 cairo_set_line_width (cr
, 1.0);
1443 cairo_move_to(cr
, x_pos
+ 0.5, draw_height
- 1 + top_y_border
+ 0.5);
1444 cairo_line_to(cr
, x_pos
+ 0.5, y_pos
+ 0.5);
1450 /*prev_x_pos = x_pos;*/
1454 cr
= gdk_cairo_create (gtk_widget_get_window(user_data
->dlg
.dialog_graph
.draw_area
));
1456 #if GTK_CHECK_VERSION(2,22,0)
1457 cairo_set_source_surface (cr
, user_data
->dlg
.dialog_graph
.surface
, 0, 0);
1459 gdk_cairo_set_source_pixmap (cr
, user_data
->dlg
.dialog_graph
.pixmap
, 0, 0);
1461 cairo_rectangle (cr
, 0, 0,
1462 user_data
->dlg
.dialog_graph
.surface_width
,
1463 user_data
->dlg
.dialog_graph
.surface_height
);
1468 /* update the scrollbar */
1469 gtk_adjustment_set_upper(user_data
->dlg
.dialog_graph
.scrollbar_adjustment
,
1470 (gfloat
) user_data
->dlg
.dialog_graph
.max_interval
);
1471 gtk_adjustment_set_step_increment(user_data
->dlg
.dialog_graph
.scrollbar_adjustment
,
1472 (gfloat
) ((last_interval
- first_interval
)/10));
1473 gtk_adjustment_set_page_increment(user_data
->dlg
.dialog_graph
.scrollbar_adjustment
,
1474 (gfloat
) (last_interval
- first_interval
));
1475 if (((last_interval
-first_interval
)*100) < user_data
->dlg
.dialog_graph
.max_interval
) {
1476 gtk_adjustment_set_page_size(user_data
->dlg
.dialog_graph
.scrollbar_adjustment
,
1477 (gfloat
) (user_data
->dlg
.dialog_graph
.max_interval
/100));
1479 gtk_adjustment_set_page_size(user_data
->dlg
.dialog_graph
.scrollbar_adjustment
,
1480 (gfloat
) (last_interval
- first_interval
));
1482 gtk_adjustment_set_value(user_data
->dlg
.dialog_graph
.scrollbar_adjustment
,
1484 - gtk_adjustment_get_page_size(user_data
->dlg
.dialog_graph
.scrollbar_adjustment
));
1485 gtk_adjustment_changed(user_data
->dlg
.dialog_graph
.scrollbar_adjustment
);
1486 gtk_adjustment_value_changed(user_data
->dlg
.dialog_graph
.scrollbar_adjustment
);
1490 /****************************************************************************/
1492 dialog_graph_redraw(user_data_t
* user_data
)
1494 user_data
->dlg
.dialog_graph
.needs_redraw
= TRUE
;
1495 dialog_graph_draw(user_data
);
1498 /****************************************************************************/
1500 quit(GtkWidget
*widget _U_
, user_data_t
*user_data
)
1502 GtkWidget
*bt_save
= (GtkWidget
*)g_object_get_data(G_OBJECT(user_data
->dlg
.dialog_graph
.window
), "bt_save");
1503 surface_info_t
*surface_info
= (surface_info_t
*)g_object_get_data(G_OBJECT(bt_save
), "surface-info");
1505 g_free(surface_info
);
1506 user_data
->dlg
.dialog_graph
.window
= NULL
;
1509 /****************************************************************************/
1510 #if GTK_CHECK_VERSION(3,0,0)
1512 draw_area_draw(GtkWidget
*widget
, cairo_t
*cr
, gpointer data
)
1514 user_data_t
*user_data
= (user_data_t
*)data
;
1515 GtkAllocation allocation
;
1517 gtk_widget_get_allocation (widget
, &allocation
);
1519 cairo_set_source_surface (cr
, user_data
->dlg
.dialog_graph
.surface
, 0, 0);
1520 cairo_rectangle (cr
, 0, 0, allocation
.width
, allocation
.height
);
1527 expose_event(GtkWidget
*widget
, GdkEventExpose
*event
, gpointer data
)
1529 user_data_t
*user_data
= (user_data_t
*)data
;
1530 cairo_t
*cr
= gdk_cairo_create (gtk_widget_get_window(widget
));
1533 #if GTK_CHECK_VERSION(2,22,0)
1534 cairo_set_source_surface (cr
, user_data
->dlg
.dialog_graph
.surface
, 0, 0);
1536 gdk_cairo_set_source_pixmap (cr
, user_data
->dlg
.dialog_graph
.pixmap
, 0, 0);
1538 cairo_rectangle (cr
, event
->area
.x
, event
->area
.y
, event
->area
.width
, event
->area
.height
);
1547 /****************************************************************************/
1549 configure_event(GtkWidget
*widget
, GdkEventConfigure
*event _U_
, gpointer data
)
1551 user_data_t
*user_data
= (user_data_t
*)data
;
1553 GtkAllocation widget_alloc
;
1555 #if GTK_CHECK_VERSION(2,22,0)
1556 surface_info_t
*surface_info
= g_new(surface_info_t
, 1);
1563 #if GTK_CHECK_VERSION(2,22,0)
1564 if (user_data
->dlg
.dialog_graph
.surface
) {
1565 g_object_unref(user_data
->dlg
.dialog_graph
.surface
);
1566 user_data
->dlg
.dialog_graph
.surface
= NULL
;
1568 gtk_widget_get_allocation(widget
, &widget_alloc
);
1569 user_data
->dlg
.dialog_graph
.surface
= gdk_window_create_similar_surface (gtk_widget_get_window(widget
),
1570 CAIRO_CONTENT_COLOR
,
1572 widget_alloc
.height
);
1574 if (user_data
->dlg
.dialog_graph
.pixmap
) {
1575 g_object_unref(user_data
->dlg
.dialog_graph
.pixmap
);
1576 user_data
->dlg
.dialog_graph
.pixmap
= NULL
;
1579 gtk_widget_get_allocation(widget
, &widget_alloc
);
1580 user_data
->dlg
.dialog_graph
.pixmap
= gdk_pixmap_new(gtk_widget_get_window(widget
),
1582 widget_alloc
.height
,
1585 user_data
->dlg
.dialog_graph
.surface_width
= widget_alloc
.width
;
1586 user_data
->dlg
.dialog_graph
.surface_height
= widget_alloc
.height
;
1588 bt_save
= (GtkWidget
*)g_object_get_data(G_OBJECT(user_data
->dlg
.dialog_graph
.window
), "bt_save");
1589 #if GTK_CHECK_VERSION(2,22,0)
1590 surface_info
->surface
= user_data
->dlg
.dialog_graph
.surface
;
1591 surface_info
->width
= widget_alloc
.width
;
1592 surface_info
->height
= widget_alloc
.height
;
1593 g_object_set_data(G_OBJECT(bt_save
), "surface-info", surface_info
);
1594 gtk_widget_set_sensitive(bt_save
, TRUE
);
1596 cr
= cairo_create (user_data
->dlg
.dialog_graph
.surface
);
1598 g_object_set_data(G_OBJECT(bt_save
), "pixmap", user_data
->dlg
.dialog_graph
.pixmap
);
1599 gtk_widget_set_sensitive(bt_save
, TRUE
);
1601 cr
= gdk_cairo_create (user_data
->dlg
.dialog_graph
.pixmap
);
1603 cairo_rectangle (cr
, 0, 0, widget_alloc
.width
, widget_alloc
.height
);
1604 cairo_set_source_rgb (cr
, 1, 1, 1);
1608 dialog_graph_redraw(user_data
);
1612 /****************************************************************************/
1614 scrollbar_changed(GtkWidget
*widget _U_
, gpointer data
)
1616 user_data_t
*user_data
= (user_data_t
*)data
;
1619 mi
= (guint32
) (gtk_adjustment_get_value(user_data
->dlg
.dialog_graph
.scrollbar_adjustment
)
1620 + gtk_adjustment_get_page_size(user_data
->dlg
.dialog_graph
.scrollbar_adjustment
));
1621 if (user_data
->dlg
.dialog_graph
.last_interval
== mi
) {
1624 if ( (user_data
->dlg
.dialog_graph
.last_interval
== 0xffffffff)
1625 && (mi
== user_data
->dlg
.dialog_graph
.max_interval
) ) {
1629 user_data
->dlg
.dialog_graph
.last_interval
=
1630 (mi
/ user_data
->dlg
.dialog_graph
.interval
) * user_data
->dlg
.dialog_graph
.interval
;
1632 dialog_graph_redraw(user_data
);
1636 /****************************************************************************/
1638 create_draw_area(user_data_t
* user_data
, GtkWidget
*box
)
1640 user_data
->dlg
.dialog_graph
.draw_area
= gtk_drawing_area_new();
1641 g_signal_connect(user_data
->dlg
.dialog_graph
.draw_area
, "destroy", G_CALLBACK(quit
), user_data
);
1643 gtk_widget_set_size_request(user_data
->dlg
.dialog_graph
.draw_area
,
1644 user_data
->dlg
.dialog_graph
.surface_width
,
1645 user_data
->dlg
.dialog_graph
.surface_height
);
1647 /* signals needed to handle backing pixmap */
1648 #if GTK_CHECK_VERSION(3,0,0)
1649 g_signal_connect(user_data
->dlg
.dialog_graph
.draw_area
, "draw", G_CALLBACK(draw_area_draw
), user_data
);
1651 g_signal_connect(user_data
->dlg
.dialog_graph
.draw_area
, "expose_event", G_CALLBACK(expose_event
), user_data
);
1653 g_signal_connect(user_data
->dlg
.dialog_graph
.draw_area
, "configure_event", G_CALLBACK(configure_event
), user_data
);
1655 gtk_widget_show(user_data
->dlg
.dialog_graph
.draw_area
);
1656 gtk_box_pack_start(GTK_BOX(box
), user_data
->dlg
.dialog_graph
.draw_area
, TRUE
, TRUE
, 0);
1658 /* create the associated scrollbar */
1659 user_data
->dlg
.dialog_graph
.scrollbar_adjustment
= (GtkAdjustment
*)gtk_adjustment_new(0, 0, 0, 0, 0, 0);
1660 user_data
->dlg
.dialog_graph
.scrollbar
= gtk_scrollbar_new(GTK_ORIENTATION_HORIZONTAL
,
1661 user_data
->dlg
.dialog_graph
.scrollbar_adjustment
);
1662 gtk_widget_show(user_data
->dlg
.dialog_graph
.scrollbar
);
1663 gtk_box_pack_start(GTK_BOX(box
), user_data
->dlg
.dialog_graph
.scrollbar
, FALSE
, FALSE
, 0);
1664 g_signal_connect(user_data
->dlg
.dialog_graph
.scrollbar_adjustment
,
1666 G_CALLBACK(scrollbar_changed
),
1670 /****************************************************************************/
1672 disable_graph(dialog_graph_graph_t
*dgg
)
1675 dgg
->display
= FALSE
;
1676 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg
->display_button
),
1681 /****************************************************************************/
1683 filter_callback(GtkWidget
*widget _U_
, dialog_graph_graph_t
*dgg
)
1685 /* this graph is not active, just update display and redraw */
1686 if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(dgg
->display_button
))) {
1688 dialog_graph_redraw(dgg
->ud
);
1693 cf_retap_packets(&cfile
);
1694 dialog_graph_redraw(dgg
->ud
);
1699 /****************************************************************************/
1701 create_filter_box(dialog_graph_graph_t
*dgg
, GtkWidget
*vbox
, int num
)
1707 hbox
= ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3, FALSE
);
1708 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
1709 gtk_widget_show(hbox
);
1711 g_snprintf(str
, sizeof(str
), "Graph %d", num
);
1712 dgg
->display_button
= gtk_toggle_button_new_with_label(str
);
1713 gtk_box_pack_start(GTK_BOX(hbox
), dgg
->display_button
, FALSE
, FALSE
, 0);
1714 gtk_widget_show(dgg
->display_button
);
1715 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg
->display_button
), dgg
->display
);
1716 g_signal_connect(dgg
->display_button
, "toggled", G_CALLBACK(filter_callback
), dgg
);
1718 label
= gtk_label_new(dgg
->title
);
1719 gtk_widget_show(label
);
1720 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
1722 #if GTK_CHECK_VERSION(3,0,0)
1723 gtk_widget_override_color(label
, GTK_STATE_FLAG_NORMAL
, &dgg
->rgba_color
);
1724 gtk_widget_override_color(label
, GTK_STATE_FLAG_ACTIVE
, &dgg
->rgba_color
);
1725 gtk_widget_override_color(label
, GTK_STATE_FLAG_PRELIGHT
, &dgg
->rgba_color
);
1726 gtk_widget_override_color(label
, GTK_STATE_FLAG_SELECTED
, &dgg
->rgba_color
);
1727 gtk_widget_override_color(label
, GTK_STATE_FLAG_INSENSITIVE
, &dgg
->rgba_color
);
1729 gtk_widget_modify_fg(label
, GTK_STATE_NORMAL
, &dgg
->color
);
1730 gtk_widget_modify_fg(label
, GTK_STATE_ACTIVE
, &dgg
->color
);
1731 gtk_widget_modify_fg(label
, GTK_STATE_PRELIGHT
, &dgg
->color
);
1732 gtk_widget_modify_fg(label
, GTK_STATE_SELECTED
, &dgg
->color
);
1733 gtk_widget_modify_fg(label
, GTK_STATE_INSENSITIVE
, &dgg
->color
);
1738 /****************************************************************************/
1740 create_filter_area(user_data_t
* user_data
, GtkWidget
*box
)
1747 frame
= gtk_frame_new("Graphs");
1748 gtk_box_pack_start(GTK_BOX(box
), frame
, TRUE
, TRUE
, 0);
1749 gtk_widget_show(frame
);
1751 vbox
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 1, FALSE
);
1752 gtk_container_add(GTK_CONTAINER(frame
), vbox
);
1753 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 3);
1754 gtk_widget_show(vbox
);
1756 for (i
= 0; i
< MAX_GRAPHS
; i
++) {
1757 create_filter_box(&user_data
->dlg
.dialog_graph
.graph
[i
], vbox
, i
+1);
1760 label
= gtk_label_new("Label: x = Wrong Seq. number m = Mark set");
1761 gtk_widget_show(label
);
1762 gtk_box_pack_start(GTK_BOX(vbox
), label
, FALSE
, FALSE
, 0);
1767 /****************************************************************************/
1769 yscale_select(GtkWidget
*item
, gpointer key
)
1772 user_data_t
*user_data
;
1774 user_data
= (user_data_t
*)key
;
1775 i
= gtk_combo_box_get_active (GTK_COMBO_BOX(item
));
1777 user_data
->dlg
.dialog_graph
.max_y_units
= yscale_max
[i
];
1778 dialog_graph_redraw(user_data
);
1781 /****************************************************************************/
1783 pixels_per_tick_select(GtkWidget
*item
, gpointer key
)
1786 user_data_t
*user_data
;
1788 user_data
= (user_data_t
*)key
;
1789 i
= gtk_combo_box_get_active (GTK_COMBO_BOX(item
));
1791 user_data
->dlg
.dialog_graph
.pixels_per_tick
= pixels_per_tick
[i
];
1792 dialog_graph_redraw(user_data
);
1795 /****************************************************************************/
1797 tick_interval_select(GtkWidget
*item
, gpointer key
)
1800 user_data_t
*user_data
;
1802 user_data
= (user_data_t
*)key
;
1803 i
= gtk_combo_box_get_active (GTK_COMBO_BOX(item
));
1805 user_data
->dlg
.dialog_graph
.interval
= tick_interval_values
[i
];
1806 cf_retap_packets(&cfile
);
1807 dialog_graph_redraw(user_data
);
1810 /****************************************************************************/
1812 create_yscale_max_menu_items(user_data_t
* user_data
)
1815 GtkWidget
*combo_box
;
1818 combo_box
= gtk_combo_box_text_new();
1820 for (i
= 0; i
< MAX_YSCALE
; i
++) {
1821 if (yscale_max
[i
] == AUTO_MAX_YSCALE
) {
1822 g_strlcpy(str
, "Auto", sizeof(str
));
1823 } else if (yscale_max
[i
] < 1000000) {
1824 g_snprintf(str
, sizeof(str
), "%u ms", yscale_max
[i
]/1000);
1826 g_snprintf(str
, sizeof(str
), "%u s", yscale_max
[i
]/1000000);
1828 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box
), str
);
1830 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box
), DEFAULT_YSCALE_INDEX
);
1831 g_signal_connect(combo_box
, "changed", G_CALLBACK(yscale_select
), (gpointer
)user_data
);
1836 /****************************************************************************/
1838 create_pixels_per_tick_menu_items(user_data_t
*user_data
)
1841 GtkWidget
*combo_box
;
1844 combo_box
= gtk_combo_box_text_new();
1846 for (i
= 0; i
< MAX_PIXELS_PER_TICK
; i
++) {
1847 g_snprintf(str
, sizeof(str
), "%u", pixels_per_tick
[i
]);
1848 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box
), str
);
1850 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box
), DEFAULT_PIXELS_PER_TICK_INDEX
);
1852 g_signal_connect(combo_box
, "changed", G_CALLBACK(pixels_per_tick_select
), (gpointer
)user_data
);
1857 /****************************************************************************/
1859 create_tick_interval_menu_items(user_data_t
*user_data
)
1861 GtkWidget
*combo_box
;
1865 combo_box
= gtk_combo_box_text_new();
1867 for (i
= 0; i
< MAX_TICK_VALUES
; i
++) {
1868 if (tick_interval_values
[i
] >= 1000) {
1869 g_snprintf(str
, sizeof(str
), "%u sec", tick_interval_values
[i
]/1000);
1870 } else if (tick_interval_values
[i
] >= 100) {
1871 g_snprintf(str
, sizeof(str
), "0.%1u sec", (tick_interval_values
[i
]/100)%10);
1872 } else if (tick_interval_values
[i
] >= 10) {
1873 g_snprintf(str
, sizeof(str
), "0.%02u sec", (tick_interval_values
[i
]/10)%10);
1875 g_snprintf(str
, sizeof(str
), "0.%03u sec", (tick_interval_values
[i
])%10);
1877 gtk_combo_box_text_append_text (GTK_COMBO_BOX_TEXT (combo_box
), str
);
1879 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box
), DEFAULT_TICK_VALUE_INDEX
);
1880 g_signal_connect(combo_box
, "changed", G_CALLBACK(tick_interval_select
), (gpointer
)user_data
);
1885 /****************************************************************************/
1887 create_ctrl_menu(user_data_t
* user_data
, GtkWidget
*vbox
, const char *name
, GtkWidget
*(*func
)(user_data_t
* user_data
))
1891 GtkWidget
*combo_box
;
1893 hbox
= ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 0, FALSE
);
1894 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
1895 gtk_widget_show(hbox
);
1897 label
= gtk_label_new(name
);
1898 gtk_widget_show(label
);
1899 gtk_box_pack_start(GTK_BOX(hbox
), label
, FALSE
, FALSE
, 0);
1901 combo_box
= (*func
)(user_data
);
1902 gtk_box_pack_end(GTK_BOX(hbox
), combo_box
, FALSE
, FALSE
, 0);
1903 gtk_widget_show(combo_box
);
1906 /****************************************************************************/
1908 create_ctrl_area(user_data_t
* user_data
, GtkWidget
*box
)
1910 GtkWidget
*frame_vbox
;
1914 frame_vbox
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0, FALSE
);
1915 gtk_box_pack_start(GTK_BOX(box
), frame_vbox
, TRUE
, TRUE
, 0);
1916 gtk_widget_show(frame_vbox
);
1918 frame
= gtk_frame_new("X Axis");
1919 gtk_container_add(GTK_CONTAINER(frame_vbox
), frame
);
1920 gtk_widget_show(frame
);
1922 vbox
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0, FALSE
);
1923 gtk_container_add(GTK_CONTAINER(frame
), vbox
);
1924 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 3);
1925 gtk_widget_show(vbox
);
1927 create_ctrl_menu(user_data
, vbox
, "Tick interval:", create_tick_interval_menu_items
);
1928 create_ctrl_menu(user_data
, vbox
, "Pixels per tick:", create_pixels_per_tick_menu_items
);
1930 frame
= gtk_frame_new("Y Axis");
1931 gtk_container_add(GTK_CONTAINER(frame_vbox
), frame
);
1932 gtk_widget_show(frame
);
1934 vbox
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0, FALSE
);
1935 gtk_container_add(GTK_CONTAINER(frame
), vbox
);
1936 gtk_container_set_border_width(GTK_CONTAINER(vbox
), 3);
1937 gtk_widget_show(vbox
);
1939 create_ctrl_menu(user_data
, vbox
, "Scale:", create_yscale_max_menu_items
);
1944 /****************************************************************************/
1946 dialog_graph_init_window(user_data_t
* user_data
)
1950 GtkWidget
*bt_close
;
1953 /* create the main window */
1954 user_data
->dlg
.dialog_graph
.window
= dlg_window_new("I/O Graphs"); /* transient_for top_level */
1956 vbox
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0, FALSE
);
1957 gtk_container_add(GTK_CONTAINER(user_data
->dlg
.dialog_graph
.window
), vbox
);
1958 gtk_widget_show(vbox
);
1960 create_draw_area(user_data
, vbox
);
1962 hbox
= ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 3, FALSE
);
1963 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 5);
1964 gtk_container_set_border_width(GTK_CONTAINER(hbox
), 3);
1965 gtk_widget_show(hbox
);
1967 create_filter_area(user_data
, hbox
);
1968 create_ctrl_area(user_data
, hbox
);
1970 dialog_graph_set_title(user_data
);
1972 hbox
= dlg_button_row_new(GTK_STOCK_CLOSE
, GTK_STOCK_SAVE
, NULL
);
1973 gtk_box_pack_start(GTK_BOX(vbox
), hbox
, FALSE
, FALSE
, 0);
1974 gtk_widget_show(hbox
);
1976 bt_close
= (GtkWidget
*)g_object_get_data(G_OBJECT(hbox
), GTK_STOCK_CLOSE
);
1977 window_set_cancel_button(user_data
->dlg
.dialog_graph
.window
, bt_close
, window_cancel_button_cb
);
1979 bt_save
= (GtkWidget
*)g_object_get_data(G_OBJECT(hbox
), GTK_STOCK_SAVE
);
1980 gtk_widget_set_sensitive(bt_save
, FALSE
);
1981 gtk_widget_set_tooltip_text(bt_save
, "Save the displayed graph to a file");
1982 g_signal_connect(bt_save
, "clicked", G_CALLBACK(pixmap_save_cb
), NULL
);
1983 g_object_set_data(G_OBJECT(user_data
->dlg
.dialog_graph
.window
), "bt_save", bt_save
);
1985 g_signal_connect(user_data
->dlg
.dialog_graph
.window
, "delete_event", G_CALLBACK(window_delete_event_cb
), NULL
);
1987 gtk_widget_show(user_data
->dlg
.dialog_graph
.window
);
1988 window_present(user_data
->dlg
.dialog_graph
.window
);
1993 /****************************************************************************/
1995 on_graph_bt_clicked(GtkWidget
*bt _U_
, user_data_t
*user_data
)
1997 if (user_data
->dlg
.dialog_graph
.window
!= NULL
) {
1998 /* There's already a graph window; reactivate it. */
1999 reactivate_window(user_data
->dlg
.dialog_graph
.window
);
2003 dialog_graph_init_window(user_data
);
2007 /****************************************************************************/
2010 on_goto_bt_clicked_lst(GtkWidget
*bt _U_
, user_data_t
*user_data _U_
)
2013 GtkTreeModel
*model
;
2014 GtkTreeSelection
*selection
;
2017 selection
= user_data
->dlg
.selected_list_sel
;
2019 if (selection
== NULL
)
2022 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
)) {
2023 gtk_tree_model_get (model
, &iter
, PACKET_COLUMN
, &fnumber
, -1);
2024 cf_goto_frame(&cfile
, fnumber
);
2029 static void draw_stat(user_data_t
*user_data
);
2031 /****************************************************************************/
2032 /* re-dissects all packets */
2034 on_refresh_bt_clicked(GtkWidget
*bt _U_
, user_data_t
*user_data
)
2036 GString
*error_string
;
2038 /* remove tap listener */
2039 remove_tap_listener(user_data
);
2041 /* register tap listener */
2042 error_string
= register_tap_listener("rtp", user_data
, NULL
, 0,
2043 rtp_reset
, rtp_packet
, rtp_draw
);
2044 if (error_string
!= NULL
) {
2045 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
, "%s", error_string
->str
);
2046 g_string_free(error_string
, TRUE
);
2050 /* retap all packets */
2051 cf_retap_packets(&cfile
);
2053 /* draw statistics info */
2054 draw_stat(user_data
);
2058 #ifdef HAVE_LIBPORTAUDIO
2059 /****************************************************************************/
2061 on_player_bt_clicked(GtkButton
*button _U_
, gpointer user_data _U_
)
2063 /*rtp_player_init(voip_calls_get_info());*/
2064 rtp_player_init(NULL
);
2066 #endif /* HAVE_LIBPORTAUDIO */
2069 on_next_bt_clicked_list(GtkWidget
*bt _U_
, user_data_t
*user_data _U_
)
2072 GtkTreeModel
*model
;
2074 GtkTreeSelection
*selection
;
2077 selection
= user_data
->dlg
.selected_list_sel
;
2079 if (selection
== NULL
)
2083 if (gtk_tree_selection_get_selected (selection
, &model
, &iter
)) {
2084 while (gtk_tree_model_iter_next (model
, &iter
)) {
2085 gtk_tree_model_get (model
, &iter
, STATUS_COLUMN
, &text
, -1);
2086 if (strcmp(text
, OK_TEXT
) != 0) {
2087 gtk_tree_selection_select_iter (selection
, &iter
);
2088 path
= gtk_tree_model_get_path(model
, &iter
);
2089 gtk_tree_view_scroll_to_cell(GTK_TREE_VIEW( user_data
->dlg
.selected_list
),
2092 gtk_tree_path_free(path
);
2099 if (user_data
->dlg
.number_of_nok
>1) {
2100 /* Get the first iter and select it before starting over */
2101 gtk_tree_model_get_iter_first(model
, &iter
);
2102 gtk_tree_selection_select_iter (selection
, &iter
);
2109 /****************************************************************************/
2110 /* when we want to save the information */
2112 save_csv_as_ok_cb(GtkWidget
*w _U_
, gpointer fc
/*user_data_t *user_data*/)
2115 GtkWidget
*rev
, *forw
, *both
;
2116 user_data_t
*user_data
;
2118 GtkListStore
*store
;
2120 GtkTreeModel
*model
;
2121 gboolean more_items
= TRUE
;
2123 /* To Hold data from the list row */
2124 guint32 packet
; /* Packet */
2125 guint16 sequence
; /* Sequence */
2126 guint32 timestamp
; /* timestamp */
2127 gfloat delta
; /* Delta(ms) */
2128 gfloat jitter
; /* Jitter(ms) */
2129 gfloat skew
; /* Skew(ms) */
2130 gfloat ipbw
; /* IP BW(kbps) */
2131 gboolean marker
; /* Marker */
2132 char *status_str
; /* Status */
2133 char *date_str
; /* Date */
2134 guint length
; /* Length */
2139 g_dest
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc
));
2141 /* Perhaps the user specified a directory instead of a file.
2142 * Check whether they did.
2144 if (test_for_directory(g_dest
) == EISDIR
) {
2145 /* It's a directory - set the file selection box to display it. */
2146 set_last_open_dir(g_dest
);
2148 file_selection_set_current_folder((GtkWidget
*)fc
, get_last_open_dir());
2149 gtk_file_chooser_set_current_name((GtkFileChooser
*)fc
, "");
2150 return FALSE
; /* run the dialog again */
2153 rev
= (GtkWidget
*)g_object_get_data(G_OBJECT(fc
), "reversed_rb");
2154 forw
= (GtkWidget
*)g_object_get_data(G_OBJECT(fc
), "forward_rb");
2155 both
= (GtkWidget
*)g_object_get_data(G_OBJECT(fc
), "both_rb");
2156 user_data
= (user_data_t
*)g_object_get_data(G_OBJECT(fc
), "user_data");
2158 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(forw
))
2159 || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both
))) {
2160 fp
= ws_fopen(g_dest
, "w");
2162 open_failure_alert_box(g_dest
, errno
, TRUE
);
2164 return TRUE
; /* we're done */
2167 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both
))) {
2168 fprintf(fp
, "Forward\n");
2170 write_failure_alert_box(g_dest
, errno
);
2173 return TRUE
; /* we're done */
2177 for (j
= 0; j
< NUM_COLS
; j
++) {
2179 fprintf(fp
, "\"%s\"", titles
[j
]);
2181 fprintf(fp
, ",\"%s\"", titles
[j
]);
2186 write_failure_alert_box(g_dest
, errno
);
2191 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(user_data
->dlg
.list_fwd
));
2192 store
= GTK_LIST_STORE(model
);
2193 if ( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store
), &iter
) ) {
2195 while (more_items
) {
2196 gtk_tree_model_get(GTK_TREE_MODEL(store
), &iter
,
2197 PACKET_COLUMN
, &packet
,
2198 SEQUENCE_COLUMN
, &sequence
,
2199 TIMESTAMP_COLUMN
, ×tamp
,
2200 DELTA_COLUMN
, &delta
,
2201 JITTER_COLUMN
, &jitter
,
2204 MARKER_COLUMN
, &marker
,
2205 STATUS_COLUMN
, &status_str
,
2206 DATE_COLUMN
, &date_str
,
2207 LENGTH_COLUMN
, &length
,
2209 fprintf(fp
, "\"%u\"", packet
);
2210 fprintf(fp
, ",\"%u\"", sequence
);
2211 fprintf(fp
, ",\"%u\"", timestamp
);
2212 fprintf(fp
, ",\"%.2f\"", delta
);
2213 fprintf(fp
, ",\"%.2f\"", jitter
);
2214 fprintf(fp
, ",\"%.2f\"", skew
);
2215 fprintf(fp
, ",\"%.2f\"", ipbw
);
2216 fprintf(fp
, ",\"%s\"", marker
? "SET" : "");
2217 fprintf(fp
, ",\"%s\"", status_str
);
2218 fprintf(fp
, ",\"%s\"", date_str
);
2219 fprintf(fp
, ",\"%u\"", length
);
2224 write_failure_alert_box(g_dest
, errno
);
2227 return TRUE
; /* we're done */
2230 more_items
= gtk_tree_model_iter_next (model
, &iter
);
2234 if (fclose(fp
) == EOF
) {
2235 write_failure_alert_box(g_dest
, errno
);
2237 return TRUE
; /* we're done */
2241 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(rev
))
2242 || gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both
))) {
2244 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both
))) {
2245 fp
= ws_fopen(g_dest
, "a");
2247 open_failure_alert_box(g_dest
, errno
, TRUE
);
2249 return TRUE
; /* we're done */
2251 fprintf(fp
, "\nReverse\n");
2253 write_failure_alert_box(g_dest
, errno
);
2256 return TRUE
; /* we're done */
2259 fp
= ws_fopen(g_dest
, "w");
2261 open_failure_alert_box(g_dest
, errno
, TRUE
);
2263 return TRUE
; /* we're done */
2266 for (j
= 0; j
< NUM_COLS
; j
++) {
2268 fprintf(fp
, "\"%s\"", titles
[j
]);
2270 fprintf(fp
, ",\"%s\"", titles
[j
]);
2275 write_failure_alert_box(g_dest
, errno
);
2278 return TRUE
; /* we're done */
2280 model
= gtk_tree_view_get_model(GTK_TREE_VIEW(user_data
->dlg
.list_rev
));
2281 store
= GTK_LIST_STORE(model
);
2282 if ( gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store
), &iter
) ) {
2286 while (more_items
) {
2287 gtk_tree_model_get(GTK_TREE_MODEL(store
), &iter
,
2288 PACKET_COLUMN
, &packet
,
2289 SEQUENCE_COLUMN
, &sequence
,
2290 TIMESTAMP_COLUMN
, ×tamp
,
2291 DELTA_COLUMN
, &delta
,
2292 JITTER_COLUMN
, &jitter
,
2295 MARKER_COLUMN
, &marker
,
2296 STATUS_COLUMN
, &status_str
,
2297 DATE_COLUMN
, &date_str
,
2298 LENGTH_COLUMN
, &length
,
2300 fprintf(fp
, "\"%u\"", packet
);
2301 fprintf(fp
, ",\"%u\"", sequence
);
2302 fprintf(fp
, ",\"%u\"", timestamp
);
2303 fprintf(fp
, ",\"%.2f\"", delta
);
2304 fprintf(fp
, ",\"%.2f\"", jitter
);
2305 fprintf(fp
, ",\"%.2f\"", skew
);
2306 fprintf(fp
, ",\"%.2f\"", ipbw
);
2307 fprintf(fp
, ",\"%s\"", marker
? "SET" : "");
2308 fprintf(fp
, ",\"%s\"", status_str
);
2309 fprintf(fp
, ",\"%s\"", date_str
);
2310 fprintf(fp
, ",\"%u\"", length
);
2315 write_failure_alert_box(g_dest
, errno
);
2318 return TRUE
; /* we're done */
2321 more_items
= gtk_tree_model_iter_next (model
, &iter
);
2324 if (fclose(fp
) == EOF
) {
2325 write_failure_alert_box(g_dest
, errno
);
2327 return TRUE
; /* we're done */
2332 return TRUE
; /* we're done */
2336 save_csv_as_destroy_cb(GtkWidget
*win _U_
, user_data_t
*user_data
)
2338 user_data
->dlg
.save_csv_as_w
= NULL
;
2341 /* when the user wants to save the csv information in a file */
2343 save_csv_as_cb(GtkWidget
*bt _U_
, user_data_t
*user_data
)
2347 GtkWidget
*label_format
;
2348 GtkWidget
*channels_label
;
2349 GtkWidget
*forward_rb
;
2350 GtkWidget
*reversed_rb
;
2353 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2354 if (user_data
->dlg
.save_csv_as_w
!= NULL
) {
2355 /* There's already a Save CSV info dialog box; reactivate it. */
2356 reactivate_window(user_data
->dlg
.save_csv_as_w
);
2360 user_data
->dlg
.save_csv_as_w
= gtk_file_chooser_dialog_new("Wireshark: Save Data As CSV",
2361 GTK_WINDOW(user_data
->dlg
.notebook
),
2362 GTK_FILE_CHOOSER_ACTION_SAVE
,
2363 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
,
2364 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
2366 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data
->dlg
.save_csv_as_w
), TRUE
);
2368 /* Container for each row of widgets */
2369 vertb
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0, FALSE
);
2370 gtk_container_set_border_width(GTK_CONTAINER(vertb
), 5);
2371 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data
->dlg
.save_csv_as_w
), vertb
);
2372 gtk_widget_show (vertb
);
2374 grid1
= ws_gtk_grid_new ();
2375 gtk_widget_show (grid1
);
2376 gtk_box_pack_start (GTK_BOX (vertb
), grid1
, FALSE
, FALSE
, 0);
2377 gtk_container_set_border_width (GTK_CONTAINER (grid1
), 10);
2378 ws_gtk_grid_set_row_spacing (GTK_GRID (grid1
), 20);
2380 label_format
= gtk_label_new ("Format: Comma Separated Values");
2381 gtk_widget_show (label_format
);
2382 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), label_format
, 0, 0, 3, 1,
2383 (GtkAttachOptions
) (GTK_FILL
),
2384 (GtkAttachOptions
) (0), 0, 0);
2386 gtk_misc_set_alignment (GTK_MISC (label_format
), 0, 0.5f
);
2389 channels_label
= gtk_label_new ("Channels: ");
2390 gtk_widget_show (channels_label
);
2391 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), channels_label
, 0, 1, 1, 1,
2392 (GtkAttachOptions
) (GTK_FILL
),
2393 (GtkAttachOptions
) (0), 0, 0);
2394 gtk_misc_set_alignment (GTK_MISC (channels_label
), 0, 0.5f
);
2396 forward_rb
= gtk_radio_button_new_with_label (NULL
, "forward ");
2397 gtk_widget_show (forward_rb
);
2398 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), forward_rb
, 1, 1, 1, 1,
2399 (GtkAttachOptions
) (GTK_FILL
),
2400 (GtkAttachOptions
) (0), 0, 0);
2402 reversed_rb
= gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb
), "reversed ");
2403 gtk_widget_show (reversed_rb
);
2404 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), reversed_rb
, 2, 1, 1, 1,
2405 (GtkAttachOptions
) (GTK_FILL
),
2406 (GtkAttachOptions
) (0), 0, 0);
2408 both_rb
= gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb
), "both");
2409 gtk_widget_show (both_rb
);
2410 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), both_rb
, 3, 1, 1, 1,
2411 (GtkAttachOptions
) (GTK_FILL
),
2412 (GtkAttachOptions
) (0), 0, 0);
2414 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(both_rb
), TRUE
);
2416 g_object_set_data(G_OBJECT(user_data
->dlg
.save_csv_as_w
), "forward_rb", forward_rb
);
2417 g_object_set_data(G_OBJECT(user_data
->dlg
.save_csv_as_w
), "reversed_rb", reversed_rb
);
2418 g_object_set_data(G_OBJECT(user_data
->dlg
.save_csv_as_w
), "both_rb", both_rb
);
2419 g_object_set_data(G_OBJECT(user_data
->dlg
.save_csv_as_w
), "user_data", user_data
);
2421 g_signal_connect(user_data
->dlg
.save_csv_as_w
, "delete_event",
2422 G_CALLBACK(window_delete_event_cb
), NULL
);
2423 g_signal_connect(user_data
->dlg
.save_csv_as_w
, "destroy",
2424 G_CALLBACK(save_csv_as_destroy_cb
), user_data
);
2426 gtk_widget_show(user_data
->dlg
.save_csv_as_w
);
2427 window_present(user_data
->dlg
.save_csv_as_w
);
2429 /* "Run" the GtkFileChooserDialog. */
2430 /* Upon exit: If "Accept" run the OK callback. */
2431 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
2432 /* Destroy the window. */
2433 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
2434 /* return with a TRUE status so that the dialog window will be destroyed. */
2435 /* Trying to re-run the dialog after popping up an alert box will not work */
2436 /* since the user will not be able to dismiss the alert box. */
2437 /* The (somewhat unfriendly) effect: the user must re-invoke the */
2438 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
2440 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
2441 /* GtkFileChooserDialog. */
2442 while (gtk_dialog_run(GTK_DIALOG(user_data
->dlg
.save_csv_as_w
)) == GTK_RESPONSE_ACCEPT
) {
2443 if (save_csv_as_ok_cb(NULL
, user_data
->dlg
.save_csv_as_w
)) {
2444 break; /* we're done */
2447 window_destroy(user_data
->dlg
.save_csv_as_w
);
2451 /****************************************************************************/
2453 save_voice_as_destroy_cb(GtkWidget
*win _U_
, user_data_t
*user_data
)
2455 /* Note that we no longer have a Save voice info dialog box. */
2456 user_data
->dlg
.save_voice_as_w
= NULL
;
2459 /****************************************************************************/
2460 /* here we save it into a file that user specified */
2461 /* XXX what about endians here? could go something wrong? */
2464 copy_file(gchar
*dest
, gint channels
, gint format
, user_data_t
*user_data
)
2466 FILE *to_stream
, *forw_stream
, *rev_stream
;
2467 size_t fwritten
, rwritten
;
2468 int f_rawvalue
, r_rawvalue
, rawvalue
;
2471 guint32 f_write_silence
= 0;
2472 guint32 r_write_silence
= 0;
2474 guint32 progbar_count
, progbar_quantum
;
2475 guint32 progbar_nextstep
= 0;
2477 gboolean stop_flag
= FALSE
;
2481 forw_stream
= ws_fopen(user_data
->f_tempname
, "rb");
2482 if (forw_stream
== NULL
)
2484 rev_stream
= ws_fopen(user_data
->r_tempname
, "rb");
2485 if (rev_stream
== NULL
) {
2486 fclose(forw_stream
);
2490 /* open file for saving */
2491 to_stream
= ws_fopen(dest
, "wb");
2492 if (to_stream
== NULL
) {
2493 fclose(forw_stream
);
2498 progbar
= create_progress_dlg(top_level
, "Saving voice in a file", dest
, TRUE
, &stop_flag
);
2500 if (format
== SAVE_AU_FORMAT
) /* au format */
2502 /* First we write the .au header. XXX Hope this is endian independent */
2503 /* the magic word 0x2e736e64 == .snd */
2504 phtonl(pd
, 0x2e736e64);
2505 nchars
= fwrite(pd
, 1, 4, to_stream
);
2508 /* header offset == 24 bytes */
2510 nchars
= fwrite(pd
, 1, 4, to_stream
);
2513 /* total length; it is permitted to set this to 0xffffffff */
2515 nchars
= fwrite(pd
, 1, 4, to_stream
);
2518 /* encoding format == 16-bit linear PCM */
2520 nchars
= fwrite(pd
, 1, 4, to_stream
);
2523 /* sample rate == 8000 Hz */
2525 nchars
= fwrite(pd
, 1, 4, to_stream
);
2530 nchars
= fwrite(pd
, 1, 4, to_stream
);
2536 /* only forward direction */
2537 case SAVE_FORWARD_DIRECTION_MASK
: {
2538 progbar_count
= user_data
->forward
.saveinfo
.count
;
2539 progbar_quantum
= user_data
->forward
.saveinfo
.count
/100;
2540 while ((f_rawvalue
= getc(forw_stream
)) != EOF
) {
2543 if ((count
> progbar_nextstep
) && (count
<= progbar_count
)) {
2544 update_progress_dlg(progbar
,
2545 (gfloat
) count
/progbar_count
, "Saving");
2546 progbar_nextstep
= progbar_nextstep
+ progbar_quantum
;
2550 if (user_data
->forward
.statinfo
.pt
== PT_PCMU
) {
2551 sample
= ulaw2linear((unsigned char)f_rawvalue
);
2554 else if (user_data
->forward
.statinfo
.pt
== PT_PCMA
) {
2555 sample
= alaw2linear((unsigned char)f_rawvalue
);
2562 fwritten
= fwrite(pd
, 1, 2, to_stream
);
2569 /* only reversed direction */
2570 case SAVE_REVERSE_DIRECTION_MASK
: {
2571 progbar_count
= user_data
->reversed
.saveinfo
.count
;
2572 progbar_quantum
= user_data
->reversed
.saveinfo
.count
/100;
2573 while ((r_rawvalue
= getc(rev_stream
)) != EOF
) {
2576 if ((count
> progbar_nextstep
) && (count
<= progbar_count
)) {
2577 update_progress_dlg(progbar
,
2578 (gfloat
) count
/progbar_count
, "Saving");
2579 progbar_nextstep
= progbar_nextstep
+ progbar_quantum
;
2583 if (user_data
->reversed
.statinfo
.pt
== PT_PCMU
) {
2584 sample
= ulaw2linear((unsigned char)r_rawvalue
);
2587 else if (user_data
->reversed
.statinfo
.pt
== PT_PCMA
) {
2588 sample
= alaw2linear((unsigned char)r_rawvalue
);
2595 rwritten
= fwrite(pd
, 1, 2, to_stream
);
2602 /* both directions */
2603 case SAVE_BOTH_DIRECTION_MASK
: {
2604 (user_data
->forward
.saveinfo
.count
> user_data
->reversed
.saveinfo
.count
) ?
2605 (progbar_count
= user_data
->forward
.saveinfo
.count
) :
2606 (progbar_count
= user_data
->reversed
.saveinfo
.count
);
2607 progbar_quantum
= progbar_count
/100;
2608 /* since conversation in one way can start later than in the other one,
2609 * we have to write some silence information for one channel */
2610 if (user_data
->forward
.statinfo
.start_time
> user_data
->reversed
.statinfo
.start_time
) {
2611 f_write_silence
= (guint32
)
2612 ((user_data
->forward
.statinfo
.start_time
2613 - user_data
->reversed
.statinfo
.start_time
)
2616 else if (user_data
->forward
.statinfo
.start_time
< user_data
->reversed
.statinfo
.start_time
) {
2617 r_write_silence
= (guint32
)
2618 ((user_data
->reversed
.statinfo
.start_time
2619 - user_data
->forward
.statinfo
.start_time
)
2625 if ((count
> progbar_nextstep
) && (count
<= progbar_count
)) {
2626 update_progress_dlg(progbar
,
2627 (gfloat
) count
/progbar_count
, "Saving");
2628 progbar_nextstep
= progbar_nextstep
+ progbar_quantum
;
2631 if (f_write_silence
> 0) {
2632 r_rawvalue
= getc(rev_stream
);
2633 switch (user_data
->forward
.statinfo
.reg_pt
) {
2635 f_rawvalue
= SILENCE_PCMU
;
2638 f_rawvalue
= SILENCE_PCMA
;
2646 else if (r_write_silence
> 0) {
2647 f_rawvalue
= getc(forw_stream
);
2648 switch (user_data
->reversed
.statinfo
.reg_pt
) {
2650 r_rawvalue
= SILENCE_PCMU
;
2653 r_rawvalue
= SILENCE_PCMA
;
2662 f_rawvalue
= getc(forw_stream
);
2663 r_rawvalue
= getc(rev_stream
);
2665 if ((r_rawvalue
== EOF
) && (f_rawvalue
== EOF
))
2667 if ((user_data
->forward
.statinfo
.pt
== PT_PCMU
)
2668 && (user_data
->reversed
.statinfo
.pt
== PT_PCMU
)) {
2669 sample
= (ulaw2linear((unsigned char)r_rawvalue
)
2670 + ulaw2linear((unsigned char)f_rawvalue
)) / 2;
2673 else if ((user_data
->forward
.statinfo
.pt
== PT_PCMA
)
2674 && (user_data
->reversed
.statinfo
.pt
== PT_PCMA
)) {
2675 sample
= (alaw2linear((unsigned char)r_rawvalue
)
2676 + alaw2linear((unsigned char)f_rawvalue
)) / 2;
2685 rwritten
= fwrite(pd
, 1, 2, to_stream
);
2693 else if (format
== SAVE_RAW_FORMAT
) /* raw format */
2697 /* only forward direction */
2698 case SAVE_FORWARD_DIRECTION_MASK
: {
2699 progbar_count
= user_data
->forward
.saveinfo
.count
;
2700 progbar_quantum
= user_data
->forward
.saveinfo
.count
/100;
2701 stream
= forw_stream
;
2704 /* only reversed direction */
2705 case SAVE_REVERSE_DIRECTION_MASK
: {
2706 progbar_count
= user_data
->reversed
.saveinfo
.count
;
2707 progbar_quantum
= user_data
->reversed
.saveinfo
.count
/100;
2708 stream
= rev_stream
;
2718 /* XXX how do you just copy the file? */
2719 while ((rawvalue
= getc(stream
)) != EOF
) {
2722 if ((count
> progbar_nextstep
) && (count
<= progbar_count
)) {
2723 update_progress_dlg(progbar
,
2724 (gfloat
) count
/progbar_count
, "Saving");
2725 progbar_nextstep
= progbar_nextstep
+ progbar_quantum
;
2729 if (putc(rawvalue
, to_stream
) == EOF
) {
2743 destroy_progress_dlg(progbar
);
2744 fclose(forw_stream
);
2751 /****************************************************************************/
2752 /* the user wants to save in a file */
2753 /* XXX support for different formats is currently commented out */
2755 save_voice_as_ok_cb(GtkWidget
*w _U_
, gpointer fc
)
2758 /*GtkWidget *wav, *sw;*/
2759 GtkWidget
*au
, *raw
;
2760 GtkWidget
*rev
, *forw
, *both
;
2761 user_data_t
*user_data
;
2762 gint channels
, format
;
2764 g_dest
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fc
));
2766 /* Perhaps the user specified a directory instead of a file.
2767 * Check whether they did.
2769 if (test_for_directory(g_dest
) == EISDIR
) {
2770 /* It's a directory - set the file selection box to display it. */
2771 set_last_open_dir(g_dest
);
2773 file_selection_set_current_folder((GtkWidget
*)fc
, get_last_open_dir());
2774 gtk_file_chooser_set_current_name((GtkFileChooser
*)fc
, "");
2775 return FALSE
; /* run the dialog again */
2779 wav
= (GtkWidget
*)g_object_get_data(G_OBJECT(fc
), "wav_rb");
2780 sw
= (GtkWidget
*)g_object_get_data(G_OBJECT(fc
), "sw_rb");
2782 au
= (GtkWidget
*)g_object_get_data(G_OBJECT(fc
), "au_rb");
2783 raw
= (GtkWidget
*)g_object_get_data(G_OBJECT(fc
), "raw_rb");
2784 rev
= (GtkWidget
*)g_object_get_data(G_OBJECT(fc
), "reversed_rb");
2785 forw
= (GtkWidget
*)g_object_get_data(G_OBJECT(fc
), "forward_rb");
2786 both
= (GtkWidget
*)g_object_get_data(G_OBJECT(fc
), "both_rb");
2787 user_data
= (user_data_t
*)g_object_get_data(G_OBJECT(fc
), "user_data");
2789 /* XXX user clicks the ok button, but we know we can't save the voice info because f.e.
2790 * we don't support that codec. So we pop up a warning. Maybe it would be better to
2791 * disable the ok button or disable the buttons for direction if only one is not ok. The
2792 * problem is if we open the save voice dialog and then click the refresh button and maybe
2793 * the state changes, so we can't save anymore. In this case we should be able to update
2794 * the buttons. For now it is easier if we put the warning when the ok button is pressed.
2797 /* we can not save in both directions */
2798 if ((user_data
->forward
.saveinfo
.saved
== FALSE
)
2799 && (user_data
->reversed
.saveinfo
.saved
== FALSE
)
2800 && (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both
)))) {
2801 /* there are many combinations here, we just exit when first matches */
2802 if ((user_data
->forward
.saveinfo
.error_type
== TAP_RTP_WRONG_CODEC
)
2803 || (user_data
->reversed
.saveinfo
.error_type
== TAP_RTP_WRONG_CODEC
))
2804 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2805 "Can't save in a file: Unsupported codec.");
2806 else if ((user_data
->forward
.saveinfo
.error_type
== TAP_RTP_WRONG_LENGTH
)
2807 || (user_data
->reversed
.saveinfo
.error_type
== TAP_RTP_WRONG_LENGTH
))
2808 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2809 "Can't save in a file: Wrong length of captured packets.");
2810 else if ((user_data
->forward
.saveinfo
.error_type
== TAP_RTP_PADDING_ERROR
)
2811 || (user_data
->reversed
.saveinfo
.error_type
== TAP_RTP_PADDING_ERROR
))
2812 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2813 "Can't save in a file: RTP data with padding.");
2814 else if ((user_data
->forward
.saveinfo
.error_type
== TAP_RTP_SHORT_FRAME
)
2815 || (user_data
->reversed
.saveinfo
.error_type
== TAP_RTP_SHORT_FRAME
))
2816 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2817 "Can't save in a file: Not all data in all packets was captured.");
2819 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2820 "Can't save in a file: File I/O problem.");
2822 return TRUE
; /* we're done */
2824 /* we can not save forward direction */
2825 else if ((user_data
->forward
.saveinfo
.saved
== FALSE
)
2826 && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (forw
)))
2827 || (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both
))))) {
2828 if (user_data
->forward
.saveinfo
.error_type
== TAP_RTP_WRONG_CODEC
)
2829 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2830 "Can't save forward direction in a file: Unsupported codec.");
2831 else if (user_data
->forward
.saveinfo
.error_type
== TAP_RTP_WRONG_LENGTH
)
2832 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2833 "Can't save forward direction in a file: Wrong length of captured packets.");
2834 else if (user_data
->forward
.saveinfo
.error_type
== TAP_RTP_PADDING_ERROR
)
2835 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2836 "Can't save forward direction in a file: RTP data with padding.");
2837 else if (user_data
->forward
.saveinfo
.error_type
== TAP_RTP_SHORT_FRAME
)
2838 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2839 "Can't save forward direction in a file: Not all data in all packets was captured.");
2841 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2842 "Can't save forward direction in a file: File I/O problem.");
2844 return TRUE
; /* we're done */
2846 /* we can not save reversed direction */
2847 else if ((user_data
->reversed
.saveinfo
.saved
== FALSE
)
2848 && ((gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev
)))
2849 || (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both
))))) {
2850 if (user_data
->reversed
.saveinfo
.error_type
== TAP_RTP_WRONG_CODEC
)
2851 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2852 "Can't save reversed direction in a file: Unsupported codec.");
2853 else if (user_data
->reversed
.saveinfo
.error_type
== TAP_RTP_WRONG_LENGTH
)
2854 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2855 "Can't save reversed direction in a file: Wrong length of captured packets.");
2856 else if (user_data
->reversed
.saveinfo
.error_type
== TAP_RTP_PADDING_ERROR
)
2857 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2858 "Can't save reversed direction in a file: RTP data with padding.");
2859 else if (user_data
->reversed
.saveinfo
.error_type
== TAP_RTP_SHORT_FRAME
)
2860 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2861 "Can't save reversed direction in a file: Not all data in all packets was captured.");
2862 else if (user_data
->reversed
.saveinfo
.error_type
== TAP_RTP_NO_DATA
)
2863 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2864 "Can't save reversed direction in a file: No RTP data.");
2866 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2867 "Can't save reversed direction in a file: File I/O problem.");
2869 return TRUE
; /* we're done */
2873 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (wav
)))
2874 format
= SAVE_WAV_FORMAT
;
2877 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (au
)))
2878 format
= SAVE_AU_FORMAT
;
2880 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (sw
)))
2881 format
= SAVE_SW_FORMAT
;
2883 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (raw
)))
2884 format
= SAVE_RAW_FORMAT
;
2886 format
= SAVE_NONE_FORMAT
;
2888 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (rev
)))
2889 channels
= SAVE_REVERSE_DIRECTION_MASK
;
2890 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (both
)))
2891 channels
= SAVE_BOTH_DIRECTION_MASK
;
2893 channels
= SAVE_FORWARD_DIRECTION_MASK
;
2895 /* direction/format validity*/
2896 if (format
== SAVE_AU_FORMAT
)
2898 /* make sure streams are alaw/ulaw */
2899 if ((channels
& SAVE_FORWARD_DIRECTION_MASK
)
2900 && (user_data
->forward
.statinfo
.pt
!= PT_PCMA
)
2901 && (user_data
->forward
.statinfo
.pt
!= PT_PCMU
)) {
2902 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2903 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2905 return TRUE
; /* we're done */
2907 if ((channels
& SAVE_REVERSE_DIRECTION_MASK
)
2908 && (user_data
->reversed
.statinfo
.pt
!= PT_PCMA
)
2909 && (user_data
->reversed
.statinfo
.pt
!= PT_PCMU
)) {
2910 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2911 "Can't save in a file: saving in au format supported only for alaw/ulaw streams");
2913 return TRUE
; /* we're done */
2915 /* make sure pt's don't differ */
2916 if ((channels
== SAVE_BOTH_DIRECTION_MASK
)
2917 && (user_data
->forward
.statinfo
.pt
!= user_data
->reversed
.statinfo
.pt
)) {
2918 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2919 "Can't save in a file: Forward and reverse direction differ in type");
2921 return TRUE
; /* we're done */
2924 else if (format
== SAVE_RAW_FORMAT
)
2926 /* can't save raw in both directions */
2927 if (channels
== SAVE_BOTH_DIRECTION_MASK
) {
2928 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2929 "Can't save in a file: Unable to save raw data in both directions");
2931 return TRUE
; /* we're done */
2936 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2937 "Can't save in a file: Invalid save format");
2939 return TRUE
; /* we're done */
2942 if (!copy_file(g_dest
, channels
, format
, user_data
)) {
2943 /* XXX - report the error type! */
2944 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
2945 "An error occurred while saving voice in a file.");
2947 return TRUE
; /* we're done */
2951 return TRUE
; /* we're done */
2954 /****************************************************************************/
2955 /* when the user wants to save the voice information in a file */
2956 /* XXX support for different formats is currently commented out */
2958 on_save_bt_clicked(GtkWidget
*bt _U_
, user_data_t
*user_data
)
2962 GtkWidget
*label_format
;
2963 GtkWidget
*channels_label
;
2964 GtkWidget
*forward_rb
;
2965 GtkWidget
*reversed_rb
;
2967 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2971 /* if we can't save in a file: wrong codec, cut packets or other errors */
2972 /* Should the error arise here or later when you click ok button ?
2973 * if we do it here, then we must disable the refresh button, so we don't do it here
2976 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
2977 if (user_data
->dlg
.save_voice_as_w
!= NULL
) {
2978 /* There's already a Save voice info dialog box; reactivate it. */
2979 reactivate_window(user_data
->dlg
.save_voice_as_w
);
2983 /* XXX - use file_selection from dlg_utils instead! */
2984 user_data
->dlg
.save_voice_as_w
= gtk_file_chooser_dialog_new("Wireshark: Save Payload As ...",
2985 GTK_WINDOW(user_data
->dlg
.notebook
),
2986 GTK_FILE_CHOOSER_ACTION_SAVE
,
2987 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
,
2988 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
2990 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(user_data
->dlg
.save_voice_as_w
), TRUE
);
2992 /* Container for each row of widgets */
2993 vertb
=ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0, FALSE
);
2994 gtk_container_set_border_width(GTK_CONTAINER(vertb
), 5);
2995 gtk_file_chooser_set_extra_widget(GTK_FILE_CHOOSER(user_data
->dlg
.save_voice_as_w
), vertb
);
2996 gtk_widget_show (vertb
);
2998 grid1
= ws_gtk_grid_new ();
2999 gtk_widget_show (grid1
);
3000 gtk_box_pack_start (GTK_BOX (vertb
), grid1
, FALSE
, FALSE
, 0);
3001 gtk_container_set_border_width (GTK_CONTAINER (grid1
), 10);
3002 ws_gtk_grid_set_row_spacing (GTK_GRID (grid1
), 20);
3005 label_format
= gtk_label_new ("Format: .au (ulaw, 8 bit, 8000 Hz, mono) ");
3006 gtk_widget_show (label_format
);
3007 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), label_format
, 0, 0, 3, 1,
3008 (GtkAttachOptions
) (GTK_FILL
),
3009 (GtkAttachOptions
) (0), 0, 0);
3012 label_format
= gtk_label_new ("Format: ");
3013 gtk_widget_show (label_format
);
3014 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), label_format
, 0, 0, 1, 1,
3015 (GtkAttachOptions
) (GTK_FILL
),
3016 (GtkAttachOptions
) (0), 0, 0);
3018 gtk_misc_set_alignment (GTK_MISC (label_format
), 0, 0.5f
);
3020 raw_rb
= gtk_radio_button_new_with_label (NULL
, ".raw");
3021 gtk_widget_show (raw_rb
);
3022 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), raw_rb
, 1, 0, 1, 1,
3023 (GtkAttachOptions
) (GTK_FILL
),
3024 (GtkAttachOptions
) (0), 0, 0);
3027 au_rb
= gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb
), ".au");
3028 gtk_widget_show (au_rb
);
3029 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), au_rb
, 3, 0, 1, 1,
3030 (GtkAttachOptions
) (GTK_FILL
),
3031 (GtkAttachOptions
) (0), 0, 0);
3034 /* we support .au - ulaw*/
3035 wav_rb
= gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb
), ".wav");
3036 gtk_widget_show (wav_rb
);
3037 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), wav_rb
, 1, 0, 1, 1,
3038 (GtkAttachOptions
) (GTK_FILL
),
3039 (GtkAttachOptions
) (0), 0, 0);
3041 sw_rb
= gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb
), "8 kHz, 16 bit ");
3042 gtk_widget_show (sw_rb
);
3043 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), sw_rb
, 2, 0, 1, 1,
3044 (GtkAttachOptions
) (GTK_FILL
),
3045 (GtkAttachOptions
) (0), 0, 0);
3046 au_rb
= gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(raw_rb
), ".au");
3047 gtk_widget_show (au_rb
);
3048 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), au_rb
, 3, 0, 1, 1,
3049 (GtkAttachOptions
) (GTK_FILL
),
3050 (GtkAttachOptions
) (0), 0, 0);
3053 channels_label
= gtk_label_new ("Channels: ");
3054 gtk_widget_show (channels_label
);
3055 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), channels_label
, 0, 1, 1, 1,
3056 (GtkAttachOptions
) (GTK_FILL
),
3057 (GtkAttachOptions
) (0), 0, 0);
3058 gtk_misc_set_alignment (GTK_MISC (channels_label
), 0, 0.5f
);
3060 forward_rb
= gtk_radio_button_new_with_label (NULL
, "forward ");
3061 gtk_widget_show (forward_rb
);
3062 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), forward_rb
, 1, 1, 1, 1,
3063 (GtkAttachOptions
) (GTK_FILL
),
3064 (GtkAttachOptions
) (0), 0, 0);
3066 reversed_rb
= gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb
), "reversed ");
3067 gtk_widget_show (reversed_rb
);
3068 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), reversed_rb
, 2, 1, 1, 1,
3069 (GtkAttachOptions
) (GTK_FILL
),
3070 (GtkAttachOptions
) (0), 0, 0);
3072 both_rb
= gtk_radio_button_new_with_label_from_widget (GTK_RADIO_BUTTON(forward_rb
), "both");
3073 gtk_widget_show (both_rb
);
3074 ws_gtk_grid_attach_extended (GTK_GRID (grid1
), both_rb
, 3, 1, 1, 1,
3075 (GtkAttachOptions
) (GTK_FILL
),
3076 (GtkAttachOptions
) (0), 0, 0);
3079 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(forward_rb
), TRUE
);
3082 /* if one direction is nok we don't allow saving
3083 XXX this is not ok since the user can click the refresh button and cause changes
3084 but we can not update this window. So we move all the decision on the time the ok
3087 if (user_data
->forward
.saved
== FALSE
) {
3088 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(reversed_rb
), TRUE
);
3089 gtk_widget_set_sensitive(forward_rb
, FALSE
);
3090 gtk_widget_set_sensitive(both_rb
, FALSE
);
3092 else if (user_data
->reversed
.saved
== FALSE
) {
3093 gtk_widget_set_sensitive(reversed_rb
, FALSE
);
3094 gtk_widget_set_sensitive(both_rb
, FALSE
);
3098 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "wav_rb", wav_rb);*/
3099 g_object_set_data(G_OBJECT(user_data
->dlg
.save_voice_as_w
), "au_rb", au_rb
);
3100 /*g_object_set_data(G_OBJECT(user_data->dlg.save_voice_as_w), "sw_rb", sw_rb);*/
3101 g_object_set_data(G_OBJECT(user_data
->dlg
.save_voice_as_w
), "raw_rb", raw_rb
);
3102 g_object_set_data(G_OBJECT(user_data
->dlg
.save_voice_as_w
), "forward_rb", forward_rb
);
3103 g_object_set_data(G_OBJECT(user_data
->dlg
.save_voice_as_w
), "reversed_rb", reversed_rb
);
3104 g_object_set_data(G_OBJECT(user_data
->dlg
.save_voice_as_w
), "both_rb", both_rb
);
3105 g_object_set_data(G_OBJECT(user_data
->dlg
.save_voice_as_w
), "user_data", user_data
);
3107 g_signal_connect(user_data
->dlg
.save_voice_as_w
, "delete_event",
3108 G_CALLBACK(window_delete_event_cb
), NULL
);
3109 g_signal_connect(user_data
->dlg
.save_voice_as_w
, "destroy",
3110 G_CALLBACK(save_voice_as_destroy_cb
), user_data
);
3112 gtk_widget_show(user_data
->dlg
.save_voice_as_w
);
3113 window_present(user_data
->dlg
.save_voice_as_w
);
3115 /* "Run" the GtkFileChooserDialog. */
3116 /* Upon exit: If "Accept" run the OK callback. */
3117 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
3118 /* Destroy the window. */
3119 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
3120 /* return with a TRUE status so that the dialog window will be destroyed. */
3121 /* Trying to re-run the dialog after popping up an alert box will not work */
3122 /* since the user will not be able to dismiss the alert box. */
3123 /* The (somewhat unfriendly) effect: the user must re-invoke the */
3124 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
3126 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
3127 /* GtkFileChooserDialog. */
3128 while (gtk_dialog_run(GTK_DIALOG(user_data
->dlg
.save_voice_as_w
)) == GTK_RESPONSE_ACCEPT
) {
3129 if (save_voice_as_ok_cb(NULL
, user_data
->dlg
.save_voice_as_w
)) {
3130 break; /* we're done */
3133 window_destroy(user_data
->dlg
.save_voice_as_w
);
3138 /****************************************************************************/
3139 /* when we are finished with redisection, we add the label for the statistic */
3141 draw_stat(user_data_t
*user_data
)
3143 gchar label_max
[300];
3144 guint32 f_expected
= (user_data
->forward
.statinfo
.stop_seq_nr
+ user_data
->forward
.statinfo
.cycles
*65536)
3145 - user_data
->forward
.statinfo
.start_seq_nr
+ 1;
3146 guint32 r_expected
= (user_data
->reversed
.statinfo
.stop_seq_nr
+ user_data
->reversed
.statinfo
.cycles
*65536)
3147 - user_data
->reversed
.statinfo
.start_seq_nr
+ 1;
3148 guint32 f_total_nr
= user_data
->forward
.statinfo
.total_nr
;
3149 guint32 r_total_nr
= user_data
->reversed
.statinfo
.total_nr
;
3150 gint32 f_lost
= f_expected
- f_total_nr
;
3151 gint32 r_lost
= r_expected
- r_total_nr
;
3152 double f_sumt
= user_data
->forward
.statinfo
.sumt
;
3153 double f_sumTS
= user_data
->forward
.statinfo
.sumTS
;
3154 double f_sumt2
= user_data
->forward
.statinfo
.sumt2
;
3155 double f_sumtTS
= user_data
->forward
.statinfo
.sumtTS
;
3157 double r_sumt
= user_data
->reversed
.statinfo
.sumt
;
3158 double r_sumTS
= user_data
->reversed
.statinfo
.sumTS
;
3159 double r_sumt2
= user_data
->reversed
.statinfo
.sumt2
;
3160 double r_sumtTS
= user_data
->reversed
.statinfo
.sumtTS
;
3161 double f_perc
, r_perc
;
3162 double f_clock_drift
= 1.0;
3163 double r_clock_drift
= 1.0;
3164 double f_duration
= user_data
->forward
.statinfo
.time
- user_data
->forward
.statinfo
.start_time
;
3165 double r_duration
= user_data
->reversed
.statinfo
.time
- user_data
->reversed
.statinfo
.start_time
;
3166 guint32 f_clock_rate
= user_data
->forward
.statinfo
.clock_rate
;
3167 guint32 r_clock_rate
= user_data
->reversed
.statinfo
.clock_rate
;
3169 if (f_clock_rate
== 0) {
3173 if (r_clock_rate
== 0) {
3178 f_perc
= (double)(f_lost
*100)/(double)f_expected
;
3183 r_perc
= (double)(r_lost
*100)/(double)r_expected
;
3188 if ((f_total_nr
>0) && (f_sumt2
> 0)) {
3189 f_clock_drift
= (f_total_nr
* f_sumtTS
- f_sumt
* f_sumTS
) / (f_total_nr
* f_sumt2
- f_sumt
* f_sumt
);
3191 if ((r_total_nr
>0) && (r_sumt2
> 0)) {
3192 r_clock_drift
= (r_total_nr
* r_sumtTS
- r_sumt
* r_sumTS
) / (r_total_nr
* r_sumt2
- r_sumt
* r_sumt
);
3194 g_snprintf(label_max
, sizeof(label_max
), "Max delta = %.2f ms at packet no. %u \n"
3195 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
3196 "Max skew = %.2f ms.\n"
3197 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
3198 " Sequence errors = %u \n"
3199 "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
3200 user_data
->forward
.statinfo
.max_delta
, user_data
->forward
.statinfo
.max_nr
,
3201 user_data
->forward
.statinfo
.max_jitter
, user_data
->forward
.statinfo
.mean_jitter
,
3202 user_data
->forward
.statinfo
.max_skew
,
3203 f_expected
, f_expected
, f_lost
, f_perc
,
3204 user_data
->forward
.statinfo
.sequence
,
3206 f_duration
* (f_clock_drift
- 1.0),
3207 f_clock_drift
* f_clock_rate
,
3208 100.0 * (f_clock_drift
- 1.0));
3210 gtk_label_set_text(GTK_LABEL(user_data
->dlg
.label_stats_fwd
), label_max
);
3211 gtk_label_set_selectable (GTK_LABEL(user_data
->dlg
.label_stats_fwd
), TRUE
);
3213 g_snprintf(label_max
, sizeof(label_max
), "Max delta = %.2f ms at packet no. %u \n"
3214 "Max jitter = %.2f ms. Mean jitter = %.2f ms.\n"
3215 "Max skew = %.2f ms.\n"
3216 "Total RTP packets = %u (expected %u) Lost RTP packets = %d (%.2f%%)"
3217 " Sequence errors = %u \n"
3218 "Duration %.2f s (%.0f ms clock drift, corresponding to %.0f Hz (%+.2f%%)",
3219 user_data
->reversed
.statinfo
.max_delta
, user_data
->reversed
.statinfo
.max_nr
,
3220 user_data
->reversed
.statinfo
.max_jitter
, user_data
->reversed
.statinfo
.mean_jitter
,
3221 user_data
->reversed
.statinfo
.max_skew
,
3222 r_expected
, r_expected
, r_lost
, r_perc
,
3223 user_data
->reversed
.statinfo
.sequence
,
3225 r_duration
* (r_clock_drift
- 1.0),
3226 r_clock_drift
* r_clock_rate
,
3227 100.0 * (r_clock_drift
- 1.0));
3229 gtk_label_set_text(GTK_LABEL(user_data
->dlg
.label_stats_rev
), label_max
);
3230 gtk_label_set_selectable (GTK_LABEL(user_data
->dlg
.label_stats_rev
), TRUE
);
3237 /****************************************************************************/
3238 /* append a line to list */
3240 add_to_list(GtkWidget
*list
, user_data_t
* user_data
, guint32 number
, guint16 seq_num
, guint32 timestamp
,
3241 double delta
, double jitter
, double skew
, double bandwidth
, gchar
*status
, gboolean marker
,
3242 gchar
*timeStr
, guint32 pkt_len
, gchar
*color_str
, guint32 flags
)
3244 GtkListStore
*list_store
;
3246 if (strcmp(status
, OK_TEXT
) != 0) {
3247 user_data
->dlg
.number_of_nok
++;
3250 list_store
= GTK_LIST_STORE(gtk_tree_view_get_model(GTK_TREE_VIEW (list
))); /* Get store */
3252 /* Creates a new row at position. iter will be changed to point to this new row.
3253 * If position is larger than the number of rows on the list, then the new row will be appended to the list.
3254 * The row will be filled with the values given to this function.
3256 * should generally be preferred when inserting rows in a sorted list store.
3258 gtk_list_store_insert_with_values( list_store
, &user_data
->dlg
.iter
, G_MAXINT
,
3259 PACKET_COLUMN
, number
,
3260 SEQUENCE_COLUMN
, seq_num
,
3261 TIMESTAMP_COLUMN
, timestamp
,
3262 DELTA_COLUMN
, delta
,
3263 JITTER_COLUMN
, jitter
,
3265 IPBW_COLUMN
, bandwidth
,
3266 MARKER_COLUMN
, marker
,
3267 STATUS_COLUMN
, (char *)status
,
3268 DATE_COLUMN
, (char *)timeStr
,
3269 LENGTH_COLUMN
, pkt_len
,
3270 FOREGROUND_COLOR_COL
, NULL
,
3271 BACKGROUND_COLOR_COL
, (char *)color_str
,
3274 if (flags
& STAT_FLAG_FIRST
) {
3275 /* Set first row as active */
3276 gtk_tree_selection_select_iter(gtk_tree_view_get_selection(GTK_TREE_VIEW(list
)), &user_data
->dlg
.iter
);
3280 /****************************************************************************
3281 * Functions needed to present values from the list
3285 /* Present boolean value */
3287 rtp_boolean_data_func (GtkTreeViewColumn
*column _U_
,
3288 GtkCellRenderer
*renderer
,
3289 GtkTreeModel
*model
,
3295 /* the col to get data from is in userdata */
3296 gint bool_col
= GPOINTER_TO_INT(user_data
);
3298 gtk_tree_model_get(model
, iter
, bool_col
, &bool_val
, -1);
3302 g_strlcpy(buf
, bool_val
? "SET" : "", sizeof(buf
));
3305 g_assert_not_reached();
3308 g_object_set(renderer
, "text", buf
, NULL
);
3313 create_list(user_data_t
* user_data
)
3316 GtkListStore
*list_store
;
3318 GtkTreeViewColumn
*column
;
3319 GtkCellRenderer
*renderer
;
3320 GtkTreeSortable
*sortable
;
3321 GtkTreeView
*list_view
;
3322 GtkTreeSelection
*selection
;
3324 /* Create the store */
3325 list_store
= gtk_list_store_new(N_COLUMN
, /* Total number of columns XXX */
3326 G_TYPE_UINT
, /* Packet */
3327 G_TYPE_UINT
, /* Sequence */
3328 G_TYPE_UINT
, /* Time stamp */
3329 G_TYPE_FLOAT
, /* Delta(ms) */
3330 G_TYPE_FLOAT
, /* Filtered Jitter(ms) */
3331 G_TYPE_FLOAT
, /* Skew(ms) */
3332 G_TYPE_FLOAT
, /* IP BW(kbps) */
3333 G_TYPE_BOOLEAN
, /* Marker */
3334 G_TYPE_STRING
, /* Status */
3335 G_TYPE_STRING
, /* Date */
3336 G_TYPE_UINT
, /* Length */
3337 G_TYPE_STRING
, /* Foreground color */
3338 G_TYPE_STRING
); /* Background color */
3341 list
= gtk_tree_view_new_with_model (GTK_TREE_MODEL (list_store
));
3343 list_view
= GTK_TREE_VIEW(list
);
3344 sortable
= GTK_TREE_SORTABLE(list_store
);
3346 /* Speed up the list display */
3347 gtk_tree_view_set_fixed_height_mode(list_view
, TRUE
);
3349 /* Setup the sortable columns */
3350 gtk_tree_sortable_set_sort_column_id(sortable
, PACKET_COLUMN
, GTK_SORT_ASCENDING
);
3351 gtk_tree_view_set_headers_clickable(list_view
, FALSE
);
3353 /* The view now holds a reference. We can get rid of our own reference */
3354 g_object_unref (G_OBJECT (list_store
));
3357 * Create the first column packet, associating the "text" attribute of the
3358 * cell_renderer to the first column of the model
3360 renderer
= gtk_cell_renderer_text_new ();
3361 column
= gtk_tree_view_column_new_with_attributes ("Packet", renderer
,
3362 "text", PACKET_COLUMN
,
3363 "foreground", FOREGROUND_COLOR_COL
,
3364 "background", BACKGROUND_COLOR_COL
,
3366 gtk_tree_view_column_set_sort_column_id(column
, PACKET_COLUMN
);
3367 gtk_tree_view_column_set_resizable(column
, TRUE
);
3368 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
3369 gtk_tree_view_column_set_min_width(column
, 55);
3371 /* Add the column to the view. */
3372 gtk_tree_view_append_column (list_view
, column
);
3375 renderer
= gtk_cell_renderer_text_new ();
3376 column
= gtk_tree_view_column_new_with_attributes ("Sequence", renderer
,
3377 "text", SEQUENCE_COLUMN
,
3378 "foreground", FOREGROUND_COLOR_COL
,
3379 "background", BACKGROUND_COLOR_COL
,
3381 gtk_tree_view_column_set_sort_column_id(column
, SEQUENCE_COLUMN
);
3382 gtk_tree_view_column_set_resizable(column
, TRUE
);
3383 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
3384 gtk_tree_view_column_set_min_width(column
, 75);
3385 gtk_tree_view_append_column (list_view
, column
);
3388 Currently
not visible
3390 renderer
= gtk_cell_renderer_text_new ();
3391 column
= gtk_tree_view_column_new_with_attributes ("Time stamp", renderer
,
3392 "text", TIMESTAMP_COLUMN
,
3393 "foreground", FOREGROUND_COLOR_COL
,
3394 "background", BACKGROUND_COLOR_COL
,
3396 gtk_tree_view_column_set_sort_column_id(column
, TIMESTAMP_COLUMN
);
3397 gtk_tree_view_column_set_resizable(column
, TRUE
);
3398 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
3399 gtk_tree_view_column_set_min_width(column
, 75);
3400 gtk_tree_view_append_column (list_view
, column
);
3403 renderer
= gtk_cell_renderer_text_new ();
3404 column
= gtk_tree_view_column_new_with_attributes ("Delta(ms)", renderer
,
3405 "text", DELTA_COLUMN
,
3406 "foreground", FOREGROUND_COLOR_COL
,
3407 "background", BACKGROUND_COLOR_COL
,
3410 gtk_tree_view_column_set_cell_data_func(column
, renderer
, float_data_func
,
3411 GINT_TO_POINTER(DELTA_COLUMN
), NULL
);
3413 gtk_tree_view_column_set_sort_column_id(column
, DELTA_COLUMN
);
3414 gtk_tree_view_column_set_resizable(column
, TRUE
);
3415 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
3416 gtk_tree_view_column_set_min_width(column
, 75);
3417 gtk_tree_view_append_column (list_view
, column
);
3420 renderer
= gtk_cell_renderer_text_new ();
3421 column
= gtk_tree_view_column_new_with_attributes ("Filtered Jitter(ms)", renderer
,
3422 "text", JITTER_COLUMN
,
3423 "foreground", FOREGROUND_COLOR_COL
,
3424 "background", BACKGROUND_COLOR_COL
,
3427 gtk_tree_view_column_set_cell_data_func(column
, renderer
, float_data_func
,
3428 GINT_TO_POINTER(JITTER_COLUMN
), NULL
);
3430 gtk_tree_view_column_set_sort_column_id(column
, JITTER_COLUMN
);
3431 gtk_tree_view_column_set_resizable(column
, TRUE
);
3432 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
3433 gtk_tree_view_column_set_min_width(column
, 110);
3434 gtk_tree_view_append_column (list_view
, column
);
3437 renderer
= gtk_cell_renderer_text_new ();
3438 column
= gtk_tree_view_column_new_with_attributes ("Skew(ms)", renderer
,
3439 "text", SKEW_COLUMN
,
3440 "foreground", FOREGROUND_COLOR_COL
,
3441 "background", BACKGROUND_COLOR_COL
,
3444 gtk_tree_view_column_set_cell_data_func(column
, renderer
, float_data_func
,
3445 GINT_TO_POINTER(SKEW_COLUMN
), NULL
);
3447 gtk_tree_view_column_set_sort_column_id(column
, SKEW_COLUMN
);
3448 gtk_tree_view_column_set_resizable(column
, TRUE
);
3449 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
3450 gtk_tree_view_column_set_min_width(column
, 110);
3451 gtk_tree_view_append_column (list_view
, column
);
3454 renderer
= gtk_cell_renderer_text_new ();
3455 column
= gtk_tree_view_column_new_with_attributes ("IP BW(kbps)", renderer
,
3456 "text", IPBW_COLUMN
,
3457 "foreground", FOREGROUND_COLOR_COL
,
3458 "background", BACKGROUND_COLOR_COL
,
3461 gtk_tree_view_column_set_cell_data_func(column
, renderer
, float_data_func
,
3462 GINT_TO_POINTER(IPBW_COLUMN
), NULL
);
3464 gtk_tree_view_column_set_sort_column_id(column
, IPBW_COLUMN
);
3465 gtk_tree_view_column_set_resizable(column
, TRUE
);
3466 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
3467 gtk_tree_view_column_set_min_width(column
, 80);
3468 gtk_tree_view_append_column (list_view
, column
);
3471 renderer
= gtk_cell_renderer_text_new ();
3472 column
= gtk_tree_view_column_new_with_attributes ("Marker", renderer
,
3473 "text", MARKER_COLUMN
,
3474 "foreground", FOREGROUND_COLOR_COL
,
3475 "background", BACKGROUND_COLOR_COL
,
3478 gtk_tree_view_column_set_cell_data_func(column
, renderer
, rtp_boolean_data_func
,
3479 GINT_TO_POINTER(MARKER_COLUMN
), NULL
);
3481 gtk_tree_view_column_set_sort_column_id(column
, MARKER_COLUMN
);
3482 gtk_tree_view_column_set_resizable(column
, TRUE
);
3483 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
3484 gtk_tree_view_column_set_min_width(column
, 60);
3485 gtk_tree_view_append_column (list_view
, column
);
3488 renderer
= gtk_cell_renderer_text_new ();
3489 column
= gtk_tree_view_column_new_with_attributes ( "Status", renderer
,
3490 "text", STATUS_COLUMN
,
3491 "foreground", FOREGROUND_COLOR_COL
,
3492 "background", BACKGROUND_COLOR_COL
,
3494 gtk_tree_view_column_set_sort_column_id(column
, STATUS_COLUMN
);
3495 gtk_tree_view_column_set_resizable(column
, TRUE
);
3496 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
3497 gtk_tree_view_column_set_min_width(column
, 100);
3498 gtk_tree_view_append_column (list_view
, column
);
3500 /* Now enable the sorting of each column */
3501 gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(list_view
), TRUE
);
3502 gtk_tree_view_set_headers_clickable(GTK_TREE_VIEW(list_view
), TRUE
);
3504 /* Setup the selection handler */
3505 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
3506 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_SINGLE
);
3508 g_signal_connect (G_OBJECT (selection
), "changed", /* select_row */
3509 G_CALLBACK (on_list_select_row
),
3514 /****************************************************************************/
3515 /* Create the dialog box with all widgets */
3517 create_rtp_dialog(user_data_t
* user_data
)
3520 GtkWidget
*list_fwd
;
3521 GtkWidget
*list_rev
;
3522 GtkWidget
*label_stats_fwd
;
3523 GtkWidget
*label_stats_rev
;
3524 GtkWidget
*notebook
;
3526 GtkWidget
*main_vb
, *page
, *page_r
;
3528 GtkWidget
*scrolled_window
, *scrolled_window_r
/*, *frame, *text, *label4, *page_help*/;
3529 GtkWidget
*box4
, *voice_bt
, *refresh_bt
, *goto_bt
, *close_bt
, *csv_bt
, *next_bt
;
3530 #ifdef HAVE_LIBPORTAUDIO
3531 GtkWidget
*player_bt
;
3532 #endif /* HAVE_LIBPORTAUDIO */
3533 GtkWidget
*graph_bt
;
3534 gchar label_forward
[150];
3535 gchar label_forward_tree
[150];
3536 gchar label_reverse
[150];
3541 window
= dlg_window_new("Wireshark: RTP Stream Analysis"); /* transient_for top_level */
3542 gtk_window_set_default_size(GTK_WINDOW(window
), 700, 400);
3544 /* Container for each row of widgets */
3545 main_vb
=ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 2, FALSE
);
3546 gtk_container_set_border_width(GTK_CONTAINER(main_vb
), 2);
3547 gtk_container_add(GTK_CONTAINER(window
), main_vb
);
3548 gtk_widget_show(main_vb
);
3551 g_strlcpy(str_src
, get_addr_name(&(user_data
->src_fwd
)), sizeof(str_src
));
3552 g_strlcpy(str_dst
, get_addr_name(&(user_data
->dst_fwd
)), sizeof(str_dst
));
3554 g_snprintf(label_forward
, sizeof(label_forward
),
3555 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X",
3556 str_src
, user_data
->port_src_fwd
, str_dst
, user_data
->port_dst_fwd
, user_data
->ssrc_fwd
);
3558 g_snprintf(label_forward_tree
, sizeof(label_forward_tree
),
3559 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X \n"
3560 "Note many things affects the accurasy of the analysis, use with caution",
3561 str_src
, user_data
->port_src_fwd
, str_dst
, user_data
->port_dst_fwd
, user_data
->ssrc_fwd
);
3564 g_strlcpy(str_src
, get_addr_name(&(user_data
->src_rev
)), sizeof(str_src
));
3565 g_strlcpy(str_dst
, get_addr_name(&(user_data
->dst_rev
)), sizeof(str_dst
));
3567 g_snprintf(label_reverse
, sizeof(label_reverse
),
3568 "Analysing stream from %s port %u to %s port %u SSRC = 0x%X \n"
3569 "Note many things affects the accurasy of the analysis, use with caution",
3570 str_src
, user_data
->port_src_rev
, str_dst
, user_data
->port_dst_rev
, user_data
->ssrc_rev
);
3572 /* Start a notebook for flipping between sets of changes */
3573 notebook
= gtk_notebook_new();
3574 gtk_box_pack_start(GTK_BOX(main_vb
), notebook
, TRUE
, TRUE
, 0);
3575 g_object_set_data(G_OBJECT(window
), "notebook", notebook
);
3577 user_data
->dlg
.notebook_signal_id
=
3578 g_signal_connect(notebook
, "switch_page", G_CALLBACK(on_notebook_switch_page
), user_data
);
3580 /* page for forward connection */
3581 page
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 8, FALSE
);
3582 gtk_container_set_border_width(GTK_CONTAINER(page
), 8);
3584 /* direction label */
3585 label
= gtk_label_new(label_forward
);
3586 gtk_box_pack_start(GTK_BOX(page
), label
, FALSE
, FALSE
, 0);
3588 /* place for some statistics */
3589 label_stats_fwd
= gtk_label_new("\n");
3590 gtk_box_pack_end(GTK_BOX(page
), label_stats_fwd
, FALSE
, FALSE
, 0);
3592 /* scrolled window */
3593 scrolled_window
= scrolled_window_new(NULL
, NULL
);
3596 list_fwd
= create_list(user_data
);
3597 gtk_widget_show(list_fwd
);
3598 gtk_container_add(GTK_CONTAINER(scrolled_window
), list_fwd
);
3599 gtk_box_pack_start(GTK_BOX(page
), scrolled_window
, TRUE
, TRUE
, 0);
3600 gtk_widget_show(scrolled_window
);
3603 label
= gtk_label_new(" Forward Direction ");
3604 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
), page
, label
);
3606 /* same page for reversed connection */
3607 page_r
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 8, FALSE
);
3608 gtk_container_set_border_width(GTK_CONTAINER(page_r
), 8);
3609 label
= gtk_label_new(label_reverse
);
3610 gtk_box_pack_start(GTK_BOX(page_r
), label
, FALSE
, FALSE
, 0);
3611 label_stats_rev
= gtk_label_new("\n");
3612 gtk_box_pack_end(GTK_BOX(page_r
), label_stats_rev
, FALSE
, FALSE
, 0);
3614 scrolled_window_r
= scrolled_window_new(NULL
, NULL
);
3616 list_rev
= create_list(user_data
);
3617 gtk_widget_show(list_rev
);
3618 gtk_container_add(GTK_CONTAINER(scrolled_window_r
), list_rev
);
3619 gtk_box_pack_start(GTK_BOX(page_r
), scrolled_window_r
, TRUE
, TRUE
, 0);
3620 gtk_widget_show(scrolled_window_r
);
3622 label
= gtk_label_new(" Reversed Direction ");
3623 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
), page_r
, label
);
3625 /* page for help&about or future */
3627 page_help
= ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL
, 5, FALSE
);
3628 label
= gtk_label_new(" Future ");
3629 gtk_notebook_append_page(GTK_NOTEBOOK(notebook
), page_help
, label
);
3630 frame
= gtk_frame_new("");
3631 text
= gtk_label_new("\n\nMaybe some more statistics: delta and jitter distribution, ...");
3632 gtk_label_set_justify(GTK_LABEL(text
), GTK_JUSTIFY_LEFT
);
3633 gtk_container_add(GTK_CONTAINER(frame
), text
);
3634 gtk_container_set_border_width(GTK_CONTAINER(frame
), 20);
3635 gtk_box_pack_start(GTK_BOX(page_help
), frame
, TRUE
, TRUE
, 0);
3638 /* show all notebooks */
3639 gtk_widget_show_all(notebook
);
3642 box4
= gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL
);
3643 gtk_box_pack_start(GTK_BOX(main_vb
), box4
, FALSE
, FALSE
, 0);
3644 gtk_container_set_border_width(GTK_CONTAINER(box4
), 10);
3645 gtk_button_box_set_layout(GTK_BUTTON_BOX (box4
), GTK_BUTTONBOX_EDGE
);
3646 gtk_box_set_spacing(GTK_BOX (box4
), 0);
3647 gtk_widget_show(box4
);
3649 voice_bt
= gtk_button_new_with_label("Save payload...");
3650 gtk_container_add(GTK_CONTAINER(box4
), voice_bt
);
3651 gtk_widget_show(voice_bt
);
3652 g_signal_connect(voice_bt
, "clicked", G_CALLBACK(on_save_bt_clicked
), user_data
);
3654 csv_bt
= gtk_button_new_with_label("Save as CSV...");
3655 gtk_container_add(GTK_CONTAINER(box4
), csv_bt
);
3656 gtk_widget_show(csv_bt
);
3657 g_signal_connect(csv_bt
, "clicked", G_CALLBACK(save_csv_as_cb
), user_data
);
3659 refresh_bt
= gtk_button_new_from_stock(GTK_STOCK_REFRESH
);
3660 gtk_container_add(GTK_CONTAINER(box4
), refresh_bt
);
3661 gtk_widget_show(refresh_bt
);
3662 g_signal_connect(refresh_bt
, "clicked", G_CALLBACK(on_refresh_bt_clicked
), user_data
);
3664 goto_bt
= gtk_button_new_from_stock(GTK_STOCK_JUMP_TO
);
3665 gtk_container_add(GTK_CONTAINER(box4
), goto_bt
);
3666 gtk_widget_show(goto_bt
);
3667 g_signal_connect(goto_bt
, "clicked", G_CALLBACK(on_goto_bt_clicked_lst
), user_data
);
3669 graph_bt
= gtk_button_new_with_label("Graph");
3670 gtk_container_add(GTK_CONTAINER(box4
), graph_bt
);
3671 gtk_widget_show(graph_bt
);
3672 g_signal_connect(graph_bt
, "clicked", G_CALLBACK(on_graph_bt_clicked
), user_data
);
3674 #ifdef HAVE_LIBPORTAUDIO
3675 player_bt
= gtk_button_new_from_stock(WIRESHARK_STOCK_AUDIO_PLAYER
);
3676 gtk_container_add(GTK_CONTAINER(box4
), player_bt
);
3677 gtk_widget_show(player_bt
);
3678 g_signal_connect(player_bt
, "clicked", G_CALLBACK(on_player_bt_clicked
), NULL
);
3679 /*gtk_widget_set_tooltip_text (player_bt, "Launch the RTP player to listen the audio stream");*/
3680 #endif /* HAVE_LIBPORTAUDIO */
3682 next_bt
= gtk_button_new_with_label("Next non-Ok");
3683 gtk_container_add(GTK_CONTAINER(box4
), next_bt
);
3684 gtk_widget_show(next_bt
);
3685 g_signal_connect(next_bt
, "clicked", G_CALLBACK(on_next_bt_clicked_list
), user_data
);
3687 close_bt
= gtk_button_new_from_stock(GTK_STOCK_CLOSE
);
3688 gtk_container_add(GTK_CONTAINER(box4
), close_bt
);
3689 gtk_widget_set_can_default(close_bt
, TRUE
);
3690 gtk_widget_show(close_bt
);
3691 window_set_cancel_button(window
, close_bt
, window_cancel_button_cb
);
3693 g_signal_connect(window
, "delete_event", G_CALLBACK(window_delete_event_cb
), NULL
);
3694 g_signal_connect(window
, "destroy", G_CALLBACK(on_destroy
), user_data
);
3696 gtk_widget_show(window
);
3697 window_present(window
);
3700 /* some widget references need to be saved for outside use */
3701 user_data
->dlg
.window
= window
;
3702 user_data
->dlg
.list_fwd
= list_fwd
;
3703 user_data
->dlg
.list_rev
= list_rev
;
3704 user_data
->dlg
.label_stats_fwd
= label_stats_fwd
;
3705 user_data
->dlg
.label_stats_rev
= label_stats_rev
;
3706 user_data
->dlg
.notebook
= notebook
;
3707 user_data
->dlg
.selected_list
= list_fwd
;
3708 user_data
->dlg
.number_of_nok
= 0;
3711 * select the initial row
3713 gtk_widget_grab_focus(list_fwd
);
3718 /****************************************************************************/
3720 process_node(proto_node
*ptree_node
, header_field_info
*hfinformation
,
3721 const gchar
* proto_field
, guint32
* p_result
)
3724 proto_node
*proto_sibling_node
;
3725 header_field_info
*hfssrc
;
3728 finfo
= PNODE_FINFO(ptree_node
);
3730 /* Caller passed top of the protocol tree. Expected child node */
3733 if (hfinformation
== (finfo
->hfinfo
)) {
3734 hfssrc
= proto_registrar_get_byname(proto_field
);
3737 for (ptree_node
= ptree_node
->first_child
;
3739 ptree_node
= ptree_node
->next
) {
3740 finfo
= PNODE_FINFO(ptree_node
);
3741 if (hfssrc
== finfo
->hfinfo
) {
3742 if (hfinformation
->type
== FT_IPv4
) {
3743 ipv4
= (ipv4_addr
*)fvalue_get(&finfo
->value
);
3744 *p_result
= ipv4_get_net_order_addr(ipv4
);
3747 *p_result
= fvalue_get_uinteger(&finfo
->value
);
3756 proto_sibling_node
= ptree_node
->next
;
3758 if (proto_sibling_node
) {
3759 return process_node(proto_sibling_node
, hfinformation
, proto_field
, p_result
);
3765 /****************************************************************************/
3767 get_int_value_from_proto_tree(proto_tree
*protocol_tree
,
3768 const gchar
*proto_name
,
3769 const gchar
*proto_field
,
3772 proto_node
*ptree_node
;
3773 header_field_info
*hfinformation
;
3775 hfinformation
= proto_registrar_get_byname(proto_name
);
3776 if (hfinformation
== NULL
)
3779 ptree_node
= ((proto_node
*)protocol_tree
)->first_child
;
3783 return process_node(ptree_node
, hfinformation
, proto_field
, p_result
);
3787 /****************************************************************************/
3789 rtp_analysis(address
*src_fwd
,
3790 guint32 port_src_fwd
,
3792 guint32 port_dst_fwd
,
3795 guint32 port_src_rev
,
3797 guint32 port_dst_rev
,
3800 user_data_t
*user_data
;
3803 static GdkColor col
[MAX_GRAPHS
] = {
3804 {0, 0x0000, 0x0000, 0x0000}, /* Black */
3805 {0, 0xffff, 0x0000, 0x0000}, /* Red */
3806 {0, 0x0000, 0xffff, 0x0000}, /* Green */
3807 {0, 0xdddd, 0xcccc, 0x6666}, /* Light amber yellow */
3808 {0, 0x6666, 0xcccc, 0xdddd}, /* Light bluish cyan */
3809 {0, 0x0000, 0x0000, 0xffff} /* Blue */
3811 static GdkRGBA rgba_col
[MAX_GRAPHS
] = {
3812 {0.0, 0.0, 0.0, 1.0}, /* Black */
3813 {1.0, 0.0, 0.1, 1.0}, /* Red */
3814 {0.0, 1.0, 0.0, 1.0}, /* Green */
3815 {0.867, 0.800, 0.400, 1.0}, /* Light amber yellow */
3816 {0.400, 0.800, 0.867, 1.0}, /* Light bluish cyan */
3817 {0.0, 0.0, 1.0, 1.0}, /* Blue */
3823 user_data
= (user_data_t
*)g_malloc(sizeof(user_data_t
));
3825 COPY_ADDRESS(&(user_data
->src_fwd
), src_fwd
);
3826 user_data
->port_src_fwd
= port_src_fwd
;
3827 COPY_ADDRESS(&(user_data
->dst_fwd
), dst_fwd
);
3828 user_data
->port_dst_fwd
= port_dst_fwd
;
3829 user_data
->ssrc_fwd
= ssrc_fwd
;
3830 COPY_ADDRESS(&(user_data
->src_rev
), src_rev
);
3831 user_data
->port_src_rev
= port_src_rev
;
3832 COPY_ADDRESS(&(user_data
->dst_rev
), dst_rev
);
3833 user_data
->port_dst_rev
= port_dst_rev
;
3834 user_data
->ssrc_rev
= ssrc_rev
;
3837 /* file names for storing sound data */
3838 fd
= create_tempfile(&tempname
, "wireshark_rtp_f");
3840 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
3841 "Can't create temporary file for RTP analysis:\n%s.",
3846 user_data
->f_tempname
= g_strdup(tempname
);
3848 fd
= create_tempfile(&tempname
, "wireshark_rtp_r");
3850 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
3851 "Can't create temporary file for RTP analysis:\n%s.",
3853 g_free(user_data
->f_tempname
);
3857 user_data
->r_tempname
= g_strdup(tempname
);
3859 user_data
->forward
.saveinfo
.fp
= NULL
;
3860 user_data
->reversed
.saveinfo
.fp
= NULL
;
3861 user_data
->dlg
.save_voice_as_w
= NULL
;
3862 user_data
->dlg
.save_csv_as_w
= NULL
;
3863 user_data
->dlg
.dialog_graph
.window
= NULL
;
3865 /* init dialog_graph */
3866 user_data
->dlg
.dialog_graph
.needs_redraw
= TRUE
;
3867 user_data
->dlg
.dialog_graph
.interval
= tick_interval_values
[DEFAULT_TICK_VALUE_INDEX
];
3868 user_data
->dlg
.dialog_graph
.draw_area
= NULL
;
3869 #if GTK_CHECK_VERSION(2,22,0)
3870 user_data
->dlg
.dialog_graph
.surface
= NULL
;
3872 user_data
->dlg
.dialog_graph
.pixmap
= NULL
;
3874 user_data
->dlg
.dialog_graph
.scrollbar
= NULL
;
3875 user_data
->dlg
.dialog_graph
.scrollbar_adjustment
= NULL
;
3876 user_data
->dlg
.dialog_graph
.surface_width
= 500;
3877 user_data
->dlg
.dialog_graph
.surface_height
= 200;
3878 user_data
->dlg
.dialog_graph
.pixels_per_tick
= pixels_per_tick
[DEFAULT_PIXELS_PER_TICK_INDEX
];
3879 user_data
->dlg
.dialog_graph
.max_y_units
= AUTO_MAX_YSCALE
;
3880 user_data
->dlg
.dialog_graph
.last_interval
= 0xffffffff;
3881 user_data
->dlg
.dialog_graph
.max_interval
= 0;
3882 user_data
->dlg
.dialog_graph
.num_items
= 0;
3883 user_data
->dlg
.dialog_graph
.start_time
= -1;
3885 for (i
= 0; i
< MAX_GRAPHS
; i
++) {
3886 user_data
->dlg
.dialog_graph
.graph
[i
].color
.pixel
= 0;
3887 user_data
->dlg
.dialog_graph
.graph
[i
].color
.red
= col
[i
].red
;
3888 user_data
->dlg
.dialog_graph
.graph
[i
].color
.green
= col
[i
].green
;
3889 user_data
->dlg
.dialog_graph
.graph
[i
].color
.blue
= col
[i
].blue
;
3890 user_data
->dlg
.dialog_graph
.graph
[i
].rgba_color
.red
= rgba_col
[i
].red
;
3891 user_data
->dlg
.dialog_graph
.graph
[i
].rgba_color
.green
= rgba_col
[i
].green
;
3892 user_data
->dlg
.dialog_graph
.graph
[i
].rgba_color
.blue
= rgba_col
[i
].blue
;
3893 user_data
->dlg
.dialog_graph
.graph
[i
].rgba_color
.alpha
= rgba_col
[i
].alpha
;
3894 user_data
->dlg
.dialog_graph
.graph
[i
].display
= TRUE
;
3895 user_data
->dlg
.dialog_graph
.graph
[i
].display_button
= NULL
;
3896 user_data
->dlg
.dialog_graph
.graph
[i
].ud
= user_data
;
3899 /* create the dialog box */
3900 create_rtp_dialog(user_data
);
3902 /* proceed as if the Refresh button would have been pressed */
3903 on_refresh_bt_clicked(NULL
, user_data
);
3906 /****************************************************************************/
3907 /* entry point from main menu */
3909 rtp_analysis_cb(GtkAction
*action _U_
, gpointer user_data _U_
)
3912 guint32 port_src_fwd
;
3914 guint32 port_dst_fwd
;
3915 guint32 ssrc_fwd
= 0;
3917 guint32 port_src_rev
;
3919 guint32 port_dst_rev
;
3920 guint32 ssrc_rev
= 0;
3921 unsigned int version_fwd
;
3923 gchar filter_text
[256];
3926 gboolean frame_matched
;
3928 GList
*strinfo_list
;
3929 GList
*filtered_list
= NULL
;
3932 rtp_stream_info_t
*strinfo
;
3934 /* Try to compile the filter. */
3935 g_strlcpy(filter_text
, "rtp && rtp.version && rtp.ssrc && (ip || ipv6)", sizeof(filter_text
));
3936 if (!dfilter_compile(filter_text
, &sfcode
)) {
3937 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
, "%s", dfilter_error_msg
);
3940 /* we load the current file into cf variable */
3942 fdata
= cf
->current_frame
;
3944 /* we are on the selected frame now */
3946 return; /* if we exit here it's an error */
3948 /* dissect the current frame */
3949 if (!cf_read_frame(cf
, fdata
))
3950 return; /* error reading the frame */
3951 epan_dissect_init(&edt
, cf
->epan
, TRUE
, FALSE
);
3952 epan_dissect_prime_dfilter(&edt
, sfcode
);
3953 epan_dissect_run(&edt
, &cf
->phdr
, frame_tvbuff_new_buffer(fdata
, &cf
->buf
), fdata
, NULL
);
3955 /* if it is not an rtp frame, show the rtpstream dialog */
3956 frame_matched
= dfilter_apply_edt(sfcode
, &edt
);
3957 if (frame_matched
!= TRUE
) {
3958 epan_dissect_cleanup(&edt
);
3959 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
3960 "Please select an RTP packet.");
3964 /* ok, it is a RTP frame, so let's get the ip and port values */
3965 COPY_ADDRESS(&(src_fwd
), &(edt
.pi
.src
));
3966 COPY_ADDRESS(&(dst_fwd
), &(edt
.pi
.dst
));
3967 port_src_fwd
= edt
.pi
.srcport
;
3968 port_dst_fwd
= edt
.pi
.destport
;
3970 /* assume the inverse ip/port combination for the reverse direction */
3971 COPY_ADDRESS(&(src_rev
), &(edt
.pi
.dst
));
3972 COPY_ADDRESS(&(dst_rev
), &(edt
.pi
.src
));
3973 port_src_rev
= edt
.pi
.destport
;
3974 port_dst_rev
= edt
.pi
.srcport
;
3976 /* check if it is RTP Version 2 */
3977 if (!get_int_value_from_proto_tree(edt
.tree
, "rtp", "rtp.version", &version_fwd
) || version_fwd
!= 2) {
3978 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
3979 "Only RTP version 2 is supported.");
3983 /* now we need the SSRC value of the current frame */
3984 if (!get_int_value_from_proto_tree(edt
.tree
, "rtp", "rtp.ssrc", &ssrc_fwd
)) {
3985 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
3986 "SSRC value not found.");
3990 /* Scan for rtpstream */
3992 /* search for reversed direction in the global rtp streams list */
3994 strinfo_list
= g_list_first(rtpstream_get_info()->strinfo_list
);
3995 while (strinfo_list
)
3997 strinfo
= (rtp_stream_info_t
*)(strinfo_list
->data
);
3998 if (ADDRESSES_EQUAL(&(strinfo
->src_addr
), &(src_fwd
))
3999 && (strinfo
->src_port
== port_src_fwd
)
4000 && (ADDRESSES_EQUAL(&(strinfo
->dest_addr
), &(dst_fwd
)))
4001 && (strinfo
->dest_port
== port_dst_fwd
))
4003 filtered_list
= g_list_prepend(filtered_list
, strinfo
);
4006 if (ADDRESSES_EQUAL(&(strinfo
->src_addr
), &(src_rev
))
4007 && (strinfo
->src_port
== port_src_rev
)
4008 && (ADDRESSES_EQUAL(&(strinfo
->dest_addr
), &(dst_rev
)))
4009 && (strinfo
->dest_port
== port_dst_rev
))
4012 filtered_list
= g_list_append(filtered_list
, strinfo
);
4014 ssrc_rev
= strinfo
->ssrc
;
4017 strinfo_list
= g_list_next(strinfo_list
);
4020 /* if more than one reverse streams found, we let the user choose the right one */
4022 rtpstream_dlg_show(filtered_list
);
4041 /****************************************************************************/
4043 rtp_analysis_init(const char *dummy _U_
, void *userdata _U_
)
4045 rtp_analysis_cb(NULL
, NULL
);
4048 /****************************************************************************/
4050 register_tap_listener_rtp_analysis(void)
4052 register_stat_cmd_arg("rtp", rtp_analysis_init
, NULL
);