add parameter dcerpc_info to PIDL_dissect_ipv?address()
[wireshark-wip.git] / ui / gtk / rtp_analysis.c
blob080f2517cd336d145adb2fcc68929696b8b22f75
1 /* rtp_analysis.c
2 * RTP analysis addition for Wireshark
4 * $Id$
6 * Copyright 2003, Alcatel Business Systems
7 * By Lars Ruoff <lars.ruoff@gmx.net>
9 * based on tap_rtp.c
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.
36 #include "config.h"
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <math.h>
41 #include <string.h>
42 #include <locale.h>
44 #ifdef HAVE_UNISTD_H
45 #include <unistd.h>
46 #endif
48 #ifdef HAVE_FCNTL_H
49 #include <fcntl.h>
50 #endif
52 #include <gtk/gtk.h>
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>
61 #include <epan/tap.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"
70 #include "ui/util.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"
100 enum
102 PACKET_COLUMN,
103 SEQUENCE_COLUMN,
104 TIMESTAMP_COLUMN,
105 DELTA_COLUMN,
106 JITTER_COLUMN,
107 SKEW_COLUMN,
108 IPBW_COLUMN,
109 MARKER_COLUMN,
110 STATUS_COLUMN,
111 DATE_COLUMN,
112 LENGTH_COLUMN,
113 FOREGROUND_COLOR_COL,
114 BACKGROUND_COLOR_COL,
115 N_COLUMN /* The number of columns */
117 /****************************************************************************/
119 #define NUM_COLS 9
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
124 #define MAX_GRAPHS 6
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"
145 /* unit is in ms */
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 {
151 guint32 value;
152 guint32 flags;
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];
158 int plot_style;
159 gboolean display;
160 GtkWidget *display_button;
161 int hf_index;
162 GdkRGBA rgba_color;
163 GdkColor color;
164 gchar title[100];
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 */
173 guint32 num_items;
174 double start_time;
176 struct _dialog_graph_graph_t graph[MAX_GRAPHS];
177 GtkWidget *window;
178 GtkWidget *draw_area;
179 #if GTK_CHECK_VERSION(2,22,0)
180 cairo_surface_t *surface;
181 #else
182 GdkPixmap *pixmap;
183 #endif
184 GtkAdjustment *scrollbar_adjustment;
185 GtkWidget *scrollbar;
186 int surface_width;
187 int surface_height;
188 int pixels_per_tick;
189 int max_y_units;
190 } dialog_graph_t;
192 typedef struct _dialog_data_t {
193 GtkWidget *window;
194 GtkWidget *list_fwd;
195 GtkTreeIter iter;
196 GtkWidget *list_rev;
197 GtkWidget *label_stats_fwd;
198 GtkWidget *label_stats_rev;
199 GtkWidget *selected_list;
200 guint number_of_nok;
201 GtkTreeSelection *selected_list_sel;
202 gint selected_list_row;
203 GtkWidget *notebook;
204 GtkWidget *save_voice_as_w;
205 GtkWidget *save_csv_as_w;
206 gulong notebook_signal_id;
207 dialog_graph_t dialog_graph;
208 } dialog_data_t;
210 #define OK_TEXT "[ Ok ]"
212 /* type of error when saving voice in a file didn't succeed */
213 typedef enum {
214 TAP_RTP_WRONG_CODEC,
215 TAP_RTP_WRONG_LENGTH,
216 TAP_RTP_PADDING_ERROR,
217 TAP_RTP_SHORT_FRAME,
218 TAP_RTP_FILE_OPEN_ERROR,
219 TAP_RTP_FILE_WRITE_ERROR,
220 TAP_RTP_NO_DATA
221 } error_type_t;
223 typedef struct _tap_rtp_save_info_t {
224 FILE *fp;
225 guint32 count;
226 error_type_t error_type;
227 gboolean saved;
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*/
244 address src_fwd;
245 guint32 port_src_fwd;
246 address dst_fwd;
247 guint32 port_dst_fwd;
248 guint32 ssrc_fwd;
249 address src_rev;
250 guint32 port_src_rev;
251 address dst_rev;
252 guint32 port_dst_rev;
253 guint32 ssrc_rev;
255 struct _info_direction forward;
256 struct _info_direction reversed;
258 char *f_tempname;
259 char *r_tempname;
261 /* dialog associated data */
262 dialog_data_t dlg;
264 } user_data_t;
267 /* Column titles. */
268 static const gchar *titles[11] = {
269 "Packet",
270 "Sequence",
271 "Time stamp",
272 "Delta (ms)",
273 "Jitter (ms)",
274 "Skew(ms)",
275 "IP BW (kbps)",
276 "Marker",
277 "Status",
278 "Date",
279 "Length"
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 /****************************************************************************/
296 static void
297 enable_graph(dialog_graph_graph_t *dgg)
300 dgg->display = TRUE;
304 static void dialog_graph_reset(user_data_t* user_data);
308 /****************************************************************************/
309 /* TAP FUNCTIONS */
311 /****************************************************************************/
312 /* when there is a [re]reading of packet's */
313 static void
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 */
389 reset_rtp_player();
390 #endif
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;
402 return;
405 /****************************************************************************/
406 static gboolean
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;
410 guint32 idx;
411 double rtp_time;
414 * We sometimes get called when dgg is disabled.
415 * This is a bug since the tap listener should be removed first
417 if (!dgg->display) {
418 return FALSE;
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;
431 if (rtp_time < 0) {
432 return FALSE;
434 idx = (guint32)(rtp_time)/dgg->ud->dlg.dialog_graph.interval;
436 /* some sanity checks */
437 if (idx >= NUM_GRAPH_ITEMS) {
438 return FALSE;
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) {
456 it->value = value;
458 it->flags = it->flags | statinfo->flags;
460 return TRUE;
463 /****************************************************************************/
464 /* here we can redraw the output */
465 /* not used yet */
466 static void
467 rtp_draw(void *prs _U_)
469 return;
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,
483 packet_info *pinfo,
484 const struct _rtp_info *rtpinfo);
487 /****************************************************************************/
488 /* whenever a RTP packet is seen by the tap listener */
489 static int
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)
498 return 0;
499 /* also ignore RTP Version != 2 */
500 else if (rtpinfo->info_version != 2)
501 return 0;
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);
522 rtp_selected = TRUE;
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);
544 rtp_selected = TRUE;
546 /* add this RTP for future listening using the RTP Player*/
547 if (rtp_selected) {
548 #ifdef HAVE_LIBPORTAUDIO
549 add_rtp_packet(rtpinfo, pinfo);
550 #endif
553 return 0;
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 */
568 static int
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)
573 guint16 msecs;
574 gchar timeStr[32];
575 struct tm *tm_tmp;
576 time_t then;
577 gchar status[80];
578 gchar color_str[14];
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",
584 tm_tmp->tm_mon + 1,
585 tm_tmp->tm_mday,
586 tm_tmp->tm_year + 1900,
587 tm_tmp->tm_hour,
588 tm_tmp->tm_min,
589 tm_tmp->tm_sec,
590 msecs);
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);
618 }else{
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);
639 /* XXX add color? */
640 /* color = COLOR_T_EVENT; */
641 g_snprintf(color_str, sizeof(color_str), "#ef8cbfffffff");
642 }else {
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,
653 statinfo->timestamp,
657 statinfo->bandwidth,
658 status,
659 rtpinfo->info_marker_set,
660 timeStr, pinfo->fd->pkt_len,
661 color_str,
662 statinfo->flags);
664 else {
665 add_to_list(list, user_data,
666 pinfo->fd->num, rtpinfo->info_seq_num,
667 statinfo->timestamp,
668 statinfo->delta,
669 statinfo->jitter,
670 statinfo->skew,
671 statinfo->bandwidth,
672 status,
673 rtpinfo->info_marker_set,
674 timeStr, pinfo->fd->pkt_len,
675 color_str,
676 statinfo->flags);
678 return 0;
681 #define MAX_SILENCE_TICKS 1000000
682 /****************************************************************************/
683 static int
684 rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo,
685 tap_rtp_stat_t *statinfo,
686 packet_info *pinfo,
687 const struct _rtp_info *rtpinfo)
689 guint i;
690 const guint8 *data;
691 guint8 tmp;
692 size_t nchars;
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;
700 else
701 saveinfo->saved = TRUE;
704 /* save the voice information */
705 /* if there was already an error, we quit */
706 if (saveinfo->saved == FALSE)
707 return 0;
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;
715 return 0;
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;
724 return 0;
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 */
736 for (i = 0;
737 (i < (statinfo->delta_timestamp - rtpinfo->info_payload_len - rtpinfo->info_padding_count))
738 && (i < MAX_SILENCE_TICKS);
739 i++) {
740 switch (statinfo->reg_pt) {
741 case PT_PCMU:
742 tmp = SILENCE_PCMU;
743 break;
744 case PT_PCMA:
745 tmp = SILENCE_PCMA;
746 break;
747 default:
748 tmp = 0;
749 break;
751 nchars = fwrite(&tmp, 1, 1, saveinfo->fp);
752 if (nchars != 1) {
753 /* Write error or short write */
754 saveinfo->saved = FALSE;
755 saveinfo->error_type = TAP_RTP_FILE_WRITE_ERROR;
756 return 0;
758 saveinfo->count++;
760 fflush(saveinfo->fp);
764 if ((rtpinfo->info_payload_type == PT_CN)
765 || (rtpinfo->info_payload_type == PT_CN_OLD)) {
767 /* all other payloads */
768 else {
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;
773 return 0;
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
779 * of the RTP data */
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),
783 saveinfo->fp);
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;
788 return 0;
790 saveinfo->count += (rtpinfo->info_payload_len - rtpinfo->info_padding_count);
792 fflush(saveinfo->fp);
793 saveinfo->saved = TRUE;
794 return 0;
797 return 0;
801 /****************************************************************************/
802 /* CALLBACKS */
804 /****************************************************************************/
806 /****************************************************************************/
807 /* close the dialog window and remove the tap listener */
808 static void
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);
827 #endif
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);
838 g_free(user_data);
842 /****************************************************************************/
843 static void
844 on_notebook_switch_page(GtkNotebook *notebook _U_,
845 gpointer *page _U_,
846 gint page_num,
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 /****************************************************************************/
857 static void
858 on_list_select_row(GtkTreeSelection *selection,
859 user_data_t *user_data /*gpointer data */)
861 user_data->dlg.selected_list_sel = selection;
865 /****************************************************************************/
866 static void
867 dialog_graph_set_title(user_data_t* user_data)
869 char *title;
871 if (!user_data->dlg.dialog_graph.window) {
872 return;
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);
886 g_free(title);
891 /****************************************************************************/
892 static void
893 dialog_graph_reset(user_data_t* user_data)
895 int i, j;
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];
902 dggi->value = 0;
903 dggi->flags = 0;
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++) {
912 /* it is forward */
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)",
917 graph_descr[i],
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);
923 /* it is reverse */
924 } else {
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)",
928 graph_descr[i],
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 /****************************************************************************/
941 static guint32
942 get_it_value(dialog_graph_graph_t *dgg, int idx)
944 dialog_graph_graph_item_t *it;
946 it = &dgg->items[idx];
948 return it->value;
951 /****************************************************************************/
952 static void
953 print_time_scale_string(char *buf, int buf_len, guint32 t)
955 if (t >= 10000000) {
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);
963 } else {
964 g_snprintf(buf, buf_len, "%dus", t);
968 /****************************************************************************/
969 static void
970 dialog_graph_draw(user_data_t* user_data)
972 int i, lwidth;
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;
979 PangoLayout *layout;
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;
985 cairo_t *cr;
987 /* new variables */
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) {
993 return;
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) {
1004 return;
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");
1010 return;
1014 * find the max value so we can autoscale the y axis
1016 max_value = 0;
1017 for (i = 0; i < MAX_GRAPHS; i++) {
1018 int idx;
1020 if (!user_data->dlg.dialog_graph.graph[i].display) {
1021 continue;
1023 for (idx = 0; (guint32)(idx) < num_time_intervals; idx++) {
1024 guint32 val;
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) {
1030 max_value = val;
1036 * Clear out old plot
1038 #if GTK_CHECK_VERSION(2,22,0)
1039 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1040 #else
1041 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1042 #endif
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,
1048 widget_alloc.width,
1049 widget_alloc.height);
1050 cairo_fill (cr);
1051 cairo_destroy (cr);
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];
1063 } else {
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;
1084 left_x_border = 10;
1085 right_x_border = label_width + 20;
1086 top_y_border = 10;
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);
1103 #else
1104 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1105 #endif
1106 cairo_set_line_width (cr, 1.0);
1107 cairo_move_to(cr,
1108 user_data->dlg.dialog_graph.surface_width - right_x_border + 1.5,
1109 top_y_border + 0.5);
1110 cairo_line_to(cr,
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);
1113 cairo_stroke(cr);
1114 cairo_destroy(cr);
1116 for (i = 0; i <= 10; i++) {
1117 int xwidth;
1119 xwidth = 5;
1120 if (!(i%5)) {
1121 /* first, middle and last tick are slightly longer */
1122 xwidth = 10;
1124 /* draw the tick */
1125 #if GTK_CHECK_VERSION(2,22,0)
1126 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1127 #else
1128 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1129 #endif
1130 cairo_set_line_width (cr, 1.0);
1131 cairo_move_to(cr,
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);
1135 cairo_line_to(cr,
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);
1138 cairo_stroke(cr);
1139 cairo_destroy(cr);
1140 /* draw the labels */
1141 if (i == 0) {
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);
1147 #else
1148 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1149 #endif
1150 cairo_move_to (cr,
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);
1154 cairo_destroy (cr);
1155 cr = NULL;
1157 if (i == 5) {
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);
1163 #else
1164 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1165 #endif
1166 cairo_move_to (cr,
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);
1170 cairo_destroy (cr);
1171 cr = NULL;
1173 if (i == 10) {
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);
1179 #else
1180 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1181 #endif
1182 cairo_move_to (cr,
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);
1186 cairo_destroy (cr);
1187 cr = NULL;
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;
1200 } else {
1201 last_interval = user_data->dlg.dialog_graph.last_interval;
1207 /*XXX*/
1208 /* plot the x-scale */
1209 #if GTK_CHECK_VERSION(2,22,0)
1210 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1211 #else
1212 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1213 #endif
1214 cairo_set_line_width (cr, 1.0);
1215 cairo_move_to(cr,
1216 left_x_border + 0.5,
1217 user_data->dlg.dialog_graph.surface_height-bottom_y_border + 1.5);
1218 cairo_line_to(cr,
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);
1221 cairo_stroke(cr);
1222 cairo_destroy(cr);
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;
1228 } else {
1229 first_interval = 0;
1232 interval_delta = 1;
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;
1238 } else {
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) {
1246 int x, xlen;
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))) {
1251 continue;
1254 if (current_interval%interval_delta) {
1255 xlen = 5;
1256 } else {
1257 xlen = 17;
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);
1265 #else
1266 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1267 #endif
1268 cairo_set_line_width (cr, 1.0);
1269 cairo_move_to(cr,
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);
1273 cairo_stroke(cr);
1274 cairo_destroy(cr);
1276 if (xlen == 17) {
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);
1286 } else {
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);
1294 #else
1295 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1296 #endif
1297 cairo_move_to (cr,
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);
1301 cairo_destroy (cr);
1302 cr = NULL;
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);
1321 #else
1322 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1323 #endif
1324 cairo_move_to (cr,
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);
1328 cairo_destroy (cr);
1329 cr = NULL;
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);
1336 #else
1337 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1338 #endif
1339 cairo_move_to (cr,
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);
1343 cairo_destroy (cr);
1344 cr = NULL;
1346 /* Draw the marks */
1347 for (i = MAX_GRAPHS-1; i >= 0; i--) {
1348 guint32 interval;
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) {
1353 continue;
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))
1364 + left_x_border;
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));
1371 } else {
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);
1379 #else
1380 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1381 #endif
1382 cairo_move_to (cr,
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);
1386 cairo_destroy (cr);
1387 cr = NULL;
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--) {
1401 guint32 interval;
1402 guint32 x_pos, y_pos, /*prev_x_pos,*/ prev_y_pos;
1403 if (! user_data->dlg.dialog_graph.graph[i].display) {
1404 continue;
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) {
1413 guint32 val;
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))
1417 + left_x_border;
1418 val = get_it_value(&user_data->dlg.dialog_graph.graph[i],
1419 interval/user_data->dlg.dialog_graph.interval);
1420 if (val>max_y) {
1421 y_pos = 0;
1422 } else {
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) ) {
1430 prev_y_pos = y_pos;
1431 /*prev_x_pos = x_pos;*/
1432 continue;
1435 if (val) {
1436 #if GTK_CHECK_VERSION(2,22,0)
1437 cr = cairo_create (user_data->dlg.dialog_graph.surface);
1438 #else
1439 cr = gdk_cairo_create (user_data->dlg.dialog_graph.pixmap);
1440 #endif
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);
1445 cairo_stroke(cr);
1446 cairo_destroy(cr);
1449 prev_y_pos = y_pos;
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);
1458 #else
1459 gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1460 #endif
1461 cairo_rectangle (cr, 0, 0,
1462 user_data->dlg.dialog_graph.surface_width,
1463 user_data->dlg.dialog_graph.surface_height);
1464 cairo_fill (cr);
1466 cairo_destroy (cr);
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));
1478 } else {
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,
1483 last_interval
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 /****************************************************************************/
1491 static void
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 /****************************************************************************/
1499 static void
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)
1511 static gboolean
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);
1521 cairo_fill (cr);
1523 return FALSE;
1525 #else
1526 static gint
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);
1535 #else
1536 gdk_cairo_set_source_pixmap (cr, user_data->dlg.dialog_graph.pixmap, 0, 0);
1537 #endif
1538 cairo_rectangle (cr, event->area.x, event->area.y, event->area.width, event->area.height);
1539 cairo_fill (cr);
1541 cairo_destroy (cr);
1543 return FALSE;
1545 #endif
1547 /****************************************************************************/
1548 static gint
1549 configure_event(GtkWidget *widget, GdkEventConfigure *event _U_, gpointer data)
1551 user_data_t *user_data = (user_data_t *)data;
1552 GtkWidget *bt_save;
1553 GtkAllocation widget_alloc;
1554 cairo_t *cr;
1555 #if GTK_CHECK_VERSION(2,22,0)
1556 surface_info_t *surface_info = g_new(surface_info_t, 1);
1557 #endif
1559 if (!user_data) {
1560 exit(10);
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,
1571 widget_alloc.width,
1572 widget_alloc.height);
1573 #else
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),
1581 widget_alloc.width,
1582 widget_alloc.height,
1583 -1);
1584 #endif
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);
1597 #else
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);
1602 #endif
1603 cairo_rectangle (cr, 0, 0, widget_alloc.width, widget_alloc.height);
1604 cairo_set_source_rgb (cr, 1, 1, 1);
1605 cairo_fill (cr);
1606 cairo_destroy (cr);
1608 dialog_graph_redraw(user_data);
1609 return TRUE;
1612 /****************************************************************************/
1613 static gint
1614 scrollbar_changed(GtkWidget *widget _U_, gpointer data)
1616 user_data_t *user_data = (user_data_t *)data;
1617 guint32 mi;
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) {
1622 return TRUE;
1624 if ( (user_data->dlg.dialog_graph.last_interval == 0xffffffff)
1625 && (mi == user_data->dlg.dialog_graph.max_interval) ) {
1626 return TRUE;
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);
1633 return TRUE;
1636 /****************************************************************************/
1637 static void
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);
1650 #else
1651 g_signal_connect(user_data->dlg.dialog_graph.draw_area, "expose_event", G_CALLBACK(expose_event), user_data);
1652 #endif
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,
1665 "value_changed",
1666 G_CALLBACK(scrollbar_changed),
1667 user_data);
1670 /****************************************************************************/
1671 static void
1672 disable_graph(dialog_graph_graph_t *dgg)
1674 if (dgg->display) {
1675 dgg->display = FALSE;
1676 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(dgg->display_button),
1677 FALSE);
1681 /****************************************************************************/
1682 static gint
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))) {
1687 disable_graph(dgg);
1688 dialog_graph_redraw(dgg->ud);
1689 return 0;
1692 enable_graph(dgg);
1693 cf_retap_packets(&cfile);
1694 dialog_graph_redraw(dgg->ud);
1696 return 0;
1699 /****************************************************************************/
1700 static void
1701 create_filter_box(dialog_graph_graph_t *dgg, GtkWidget *vbox, int num)
1703 GtkWidget *hbox;
1704 GtkWidget *label;
1705 char str[256];
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);
1728 #else
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);
1734 #endif
1735 return;
1738 /****************************************************************************/
1739 static void
1740 create_filter_area(user_data_t* user_data, GtkWidget *box)
1742 GtkWidget *frame;
1743 GtkWidget *vbox;
1744 int i;
1745 GtkWidget *label;
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);
1764 return;
1767 /****************************************************************************/
1768 static void
1769 yscale_select(GtkWidget *item, gpointer key)
1771 int i;
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 /****************************************************************************/
1782 static void
1783 pixels_per_tick_select(GtkWidget *item, gpointer key)
1785 int i;
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 /****************************************************************************/
1796 static void
1797 tick_interval_select(GtkWidget *item, gpointer key)
1799 int i;
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 /****************************************************************************/
1811 static GtkWidget *
1812 create_yscale_max_menu_items(user_data_t* user_data)
1814 char str[15];
1815 GtkWidget *combo_box;
1816 int i;
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);
1825 } else {
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);
1833 return combo_box;
1836 /****************************************************************************/
1837 static GtkWidget *
1838 create_pixels_per_tick_menu_items(user_data_t *user_data)
1840 char str[5];
1841 GtkWidget *combo_box;
1842 int i;
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);
1854 return combo_box;
1857 /****************************************************************************/
1858 static GtkWidget *
1859 create_tick_interval_menu_items(user_data_t *user_data)
1861 GtkWidget *combo_box;
1862 char str[15];
1863 int i;
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);
1874 } else {
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);
1882 return combo_box;
1885 /****************************************************************************/
1886 static void
1887 create_ctrl_menu(user_data_t* user_data, GtkWidget *vbox, const char *name, GtkWidget *(*func)(user_data_t* user_data))
1889 GtkWidget *hbox;
1890 GtkWidget *label;
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 /****************************************************************************/
1907 static void
1908 create_ctrl_area(user_data_t* user_data, GtkWidget *box)
1910 GtkWidget *frame_vbox;
1911 GtkWidget *frame;
1912 GtkWidget *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);
1941 return;
1944 /****************************************************************************/
1945 static void
1946 dialog_graph_init_window(user_data_t* user_data)
1948 GtkWidget *vbox;
1949 GtkWidget *hbox;
1950 GtkWidget *bt_close;
1951 GtkWidget *bt_save;
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 /****************************************************************************/
1994 static void
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);
2000 return;
2003 dialog_graph_init_window(user_data);
2007 /****************************************************************************/
2009 static void
2010 on_goto_bt_clicked_lst(GtkWidget *bt _U_, user_data_t *user_data _U_)
2012 GtkTreeIter iter;
2013 GtkTreeModel *model;
2014 GtkTreeSelection *selection;
2015 guint fnumber;
2017 selection = user_data->dlg.selected_list_sel;
2019 if (selection == NULL)
2020 return;
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 */
2033 static void
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);
2047 return;
2050 /* retap all packets */
2051 cf_retap_packets(&cfile);
2053 /* draw statistics info */
2054 draw_stat(user_data);
2058 #ifdef HAVE_LIBPORTAUDIO
2059 /****************************************************************************/
2060 static void
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 */
2068 static void
2069 on_next_bt_clicked_list(GtkWidget *bt _U_, user_data_t *user_data _U_)
2071 GtkTreeIter iter;
2072 GtkTreeModel *model;
2073 gchar *text;
2074 GtkTreeSelection *selection;
2075 GtkTreePath *path;
2077 selection = user_data->dlg.selected_list_sel;
2079 if (selection == NULL)
2080 return;
2082 try_again:
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),
2090 path,
2091 NULL, FALSE, 0, 0);
2092 gtk_tree_path_free(path);
2093 g_free (text);
2094 return;
2096 g_free (text);
2098 /* wrap around */
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);
2103 goto try_again;
2109 /****************************************************************************/
2110 /* when we want to save the information */
2111 static gboolean
2112 save_csv_as_ok_cb(GtkWidget *w _U_, gpointer fc /*user_data_t *user_data*/)
2114 gchar *g_dest;
2115 GtkWidget *rev, *forw, *both;
2116 user_data_t *user_data;
2118 GtkListStore *store;
2119 GtkTreeIter iter;
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 */
2136 FILE *fp;
2137 int j;
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);
2147 g_free(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");
2161 if (fp == NULL) {
2162 open_failure_alert_box(g_dest, errno, TRUE);
2163 g_free(g_dest);
2164 return TRUE; /* we're done */
2167 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(both))) {
2168 fprintf(fp, "Forward\n");
2169 if (ferror(fp)) {
2170 write_failure_alert_box(g_dest, errno);
2171 fclose(fp);
2172 g_free(g_dest);
2173 return TRUE; /* we're done */
2177 for (j = 0; j < NUM_COLS; j++) {
2178 if (j == 0) {
2179 fprintf(fp, "\"%s\"", titles[j]);
2180 } else {
2181 fprintf(fp, ",\"%s\"", titles[j]);
2184 fprintf(fp, "\n");
2185 if (ferror(fp)) {
2186 write_failure_alert_box(g_dest, errno);
2187 fclose(fp);
2188 g_free(g_dest);
2189 return TRUE;
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, &timestamp,
2200 DELTA_COLUMN, &delta,
2201 JITTER_COLUMN, &jitter,
2202 SKEW_COLUMN, &skew,
2203 IPBW_COLUMN, &ipbw,
2204 MARKER_COLUMN, &marker,
2205 STATUS_COLUMN, &status_str,
2206 DATE_COLUMN, &date_str,
2207 LENGTH_COLUMN, &length,
2208 -1);
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);
2220 fprintf(fp,"\n");
2221 g_free(status_str);
2222 g_free(date_str);
2223 if (ferror(fp)) {
2224 write_failure_alert_box(g_dest, errno);
2225 fclose(fp);
2226 g_free(g_dest);
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);
2236 g_free(g_dest);
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");
2246 if (fp == NULL) {
2247 open_failure_alert_box(g_dest, errno, TRUE);
2248 g_free(g_dest);
2249 return TRUE; /* we're done */
2251 fprintf(fp, "\nReverse\n");
2252 if (ferror(fp)) {
2253 write_failure_alert_box(g_dest, errno);
2254 fclose(fp);
2255 g_free(g_dest);
2256 return TRUE; /* we're done */
2258 } else {
2259 fp = ws_fopen(g_dest, "w");
2260 if (fp == NULL) {
2261 open_failure_alert_box(g_dest, errno, TRUE);
2262 g_free(g_dest);
2263 return TRUE; /* we're done */
2266 for (j = 0; j < NUM_COLS; j++) {
2267 if (j == 0) {
2268 fprintf(fp, "\"%s\"", titles[j]);
2269 } else {
2270 fprintf(fp, ",\"%s\"", titles[j]);
2273 fprintf(fp, "\n");
2274 if (ferror(fp)) {
2275 write_failure_alert_box(g_dest, errno);
2276 fclose(fp);
2277 g_free(g_dest);
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) ) {
2284 more_items = TRUE;
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, &timestamp,
2291 DELTA_COLUMN, &delta,
2292 JITTER_COLUMN, &jitter,
2293 SKEW_COLUMN, &skew,
2294 IPBW_COLUMN, &ipbw,
2295 MARKER_COLUMN, &marker,
2296 STATUS_COLUMN, &status_str,
2297 DATE_COLUMN, &date_str,
2298 LENGTH_COLUMN, &length,
2299 -1);
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);
2311 fprintf(fp, "\n");
2312 g_free(status_str);
2313 g_free(date_str);
2314 if (ferror(fp)) {
2315 write_failure_alert_box(g_dest, errno);
2316 fclose(fp);
2317 g_free(g_dest);
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);
2326 g_free(g_dest);
2327 return TRUE; /* we're done */
2331 g_free(g_dest);
2332 return TRUE; /* we're done */
2335 static void
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 */
2342 static void
2343 save_csv_as_cb(GtkWidget *bt _U_, user_data_t *user_data)
2345 GtkWidget *vertb;
2346 GtkWidget *grid1;
2347 GtkWidget *label_format;
2348 GtkWidget *channels_label;
2349 GtkWidget *forward_rb;
2350 GtkWidget *reversed_rb;
2351 GtkWidget *both_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);
2357 return;
2359 #endif
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,
2365 NULL);
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. */
2439 /* */
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 /****************************************************************************/
2452 static void
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? */
2463 static gboolean
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;
2469 gint16 sample;
2470 gchar pd[4];
2471 guint32 f_write_silence = 0;
2472 guint32 r_write_silence = 0;
2473 progdlg_t *progbar;
2474 guint32 progbar_count, progbar_quantum;
2475 guint32 progbar_nextstep = 0;
2476 guint32 count = 0;
2477 gboolean stop_flag = FALSE;
2478 size_t nchars;
2479 gboolean ret_val;
2481 forw_stream = ws_fopen(user_data->f_tempname, "rb");
2482 if (forw_stream == NULL)
2483 return FALSE;
2484 rev_stream = ws_fopen(user_data->r_tempname, "rb");
2485 if (rev_stream == NULL) {
2486 fclose(forw_stream);
2487 return FALSE;
2490 /* open file for saving */
2491 to_stream = ws_fopen(dest, "wb");
2492 if (to_stream == NULL) {
2493 fclose(forw_stream);
2494 fclose(rev_stream);
2495 return FALSE;
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);
2506 if (nchars != 4)
2507 goto copy_file_err;
2508 /* header offset == 24 bytes */
2509 phtonl(pd, 24);
2510 nchars = fwrite(pd, 1, 4, to_stream);
2511 if (nchars != 4)
2512 goto copy_file_err;
2513 /* total length; it is permitted to set this to 0xffffffff */
2514 phtonl(pd, -1);
2515 nchars = fwrite(pd, 1, 4, to_stream);
2516 if (nchars != 4)
2517 goto copy_file_err;
2518 /* encoding format == 16-bit linear PCM */
2519 phtonl(pd, 3);
2520 nchars = fwrite(pd, 1, 4, to_stream);
2521 if (nchars != 4)
2522 goto copy_file_err;
2523 /* sample rate == 8000 Hz */
2524 phtonl(pd, 8000);
2525 nchars = fwrite(pd, 1, 4, to_stream);
2526 if (nchars != 4)
2527 goto copy_file_err;
2528 /* channels == 1 */
2529 phtonl(pd, 1);
2530 nchars = fwrite(pd, 1, 4, to_stream);
2531 if (nchars != 4)
2532 goto copy_file_err;
2535 switch (channels) {
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) {
2541 if (stop_flag)
2542 break;
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;
2548 count++;
2550 if (user_data->forward.statinfo.pt == PT_PCMU) {
2551 sample = ulaw2linear((unsigned char)f_rawvalue);
2552 phtons(pd, sample);
2554 else if (user_data->forward.statinfo.pt == PT_PCMA) {
2555 sample = alaw2linear((unsigned char)f_rawvalue);
2556 phtons(pd, sample);
2558 else{
2559 goto copy_file_err;
2562 fwritten = fwrite(pd, 1, 2, to_stream);
2563 if (fwritten < 2) {
2564 goto copy_file_err;
2567 break;
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) {
2574 if (stop_flag)
2575 break;
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;
2581 count++;
2583 if (user_data->reversed.statinfo.pt == PT_PCMU) {
2584 sample = ulaw2linear((unsigned char)r_rawvalue);
2585 phtons(pd, sample);
2587 else if (user_data->reversed.statinfo.pt == PT_PCMA) {
2588 sample = alaw2linear((unsigned char)r_rawvalue);
2589 phtons(pd, sample);
2591 else{
2592 goto copy_file_err;
2595 rwritten = fwrite(pd, 1, 2, to_stream);
2596 if (rwritten < 2) {
2597 goto copy_file_err;
2600 break;
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)
2614 * (8000/1000));
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)
2620 * (8000/1000));
2622 for (;;) {
2623 if (stop_flag)
2624 break;
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;
2630 count++;
2631 if (f_write_silence > 0) {
2632 r_rawvalue = getc(rev_stream);
2633 switch (user_data->forward.statinfo.reg_pt) {
2634 case PT_PCMU:
2635 f_rawvalue = SILENCE_PCMU;
2636 break;
2637 case PT_PCMA:
2638 f_rawvalue = SILENCE_PCMA;
2639 break;
2640 default:
2641 f_rawvalue = 0;
2642 break;
2644 f_write_silence--;
2646 else if (r_write_silence > 0) {
2647 f_rawvalue = getc(forw_stream);
2648 switch (user_data->reversed.statinfo.reg_pt) {
2649 case PT_PCMU:
2650 r_rawvalue = SILENCE_PCMU;
2651 break;
2652 case PT_PCMA:
2653 r_rawvalue = SILENCE_PCMA;
2654 break;
2655 default:
2656 r_rawvalue = 0;
2657 break;
2659 r_write_silence--;
2661 else {
2662 f_rawvalue = getc(forw_stream);
2663 r_rawvalue = getc(rev_stream);
2665 if ((r_rawvalue == EOF) && (f_rawvalue == EOF))
2666 break;
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;
2671 phtons(pd, sample);
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;
2677 phtons(pd, sample);
2679 else
2681 goto copy_file_err;
2685 rwritten = fwrite(pd, 1, 2, to_stream);
2686 if (rwritten < 2) {
2687 goto copy_file_err;
2693 else if (format == SAVE_RAW_FORMAT) /* raw format */
2695 FILE *stream;
2696 switch (channels) {
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;
2702 break;
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;
2709 break;
2711 default: {
2712 goto copy_file_err;
2718 /* XXX how do you just copy the file? */
2719 while ((rawvalue = getc(stream)) != EOF) {
2720 if (stop_flag)
2721 break;
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;
2727 count++;
2729 if (putc(rawvalue, to_stream) == EOF) {
2730 goto copy_file_err;
2735 ret_val = TRUE;
2736 goto copy_file_xit;
2738 copy_file_err:
2739 ret_val = FALSE;
2740 goto copy_file_xit;
2742 copy_file_xit:
2743 destroy_progress_dlg(progbar);
2744 fclose(forw_stream);
2745 fclose(rev_stream);
2746 fclose(to_stream);
2747 return ret_val;
2751 /****************************************************************************/
2752 /* the user wants to save in a file */
2753 /* XXX support for different formats is currently commented out */
2754 static gboolean
2755 save_voice_as_ok_cb(GtkWidget *w _U_, gpointer fc)
2757 gchar *g_dest;
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);
2772 g_free(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 */
2778 #if 0
2779 wav = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "wav_rb");
2780 sw = (GtkWidget *)g_object_get_data(G_OBJECT(fc), "sw_rb");
2781 #endif
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.");
2818 else
2819 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2820 "Can't save in a file: File I/O problem.");
2821 g_free(g_dest);
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.");
2840 else
2841 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2842 "Can't save forward direction in a file: File I/O problem.");
2843 g_free(g_dest);
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.");
2865 else
2866 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2867 "Can't save reversed direction in a file: File I/O problem.");
2868 g_free(g_dest);
2869 return TRUE; /* we're done */
2872 #if 0
2873 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (wav)))
2874 format = SAVE_WAV_FORMAT;
2875 else
2876 #endif
2877 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (au)))
2878 format = SAVE_AU_FORMAT;
2879 #if 0
2880 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (sw)))
2881 format = SAVE_SW_FORMAT;
2882 #endif
2883 else if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (raw)))
2884 format = SAVE_RAW_FORMAT;
2885 else
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;
2892 else
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");
2904 g_free(g_dest);
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");
2912 g_free(g_dest);
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");
2920 g_free(g_dest);
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");
2930 g_free(g_dest);
2931 return TRUE; /* we're done */
2934 else
2936 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
2937 "Can't save in a file: Invalid save format");
2938 g_free(g_dest);
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.");
2946 g_free(g_dest);
2947 return TRUE; /* we're done */
2950 g_free(g_dest);
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 */
2957 static void
2958 on_save_bt_clicked(GtkWidget *bt _U_, user_data_t *user_data)
2960 GtkWidget *vertb;
2961 GtkWidget *grid1;
2962 GtkWidget *label_format;
2963 GtkWidget *channels_label;
2964 GtkWidget *forward_rb;
2965 GtkWidget *reversed_rb;
2966 GtkWidget *both_rb;
2967 /*GtkWidget *wav_rb; GtkWidget *sw_rb;*/
2968 GtkWidget *au_rb;
2969 GtkWidget *raw_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);
2980 return;
2982 #endif
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,
2989 NULL);
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);
3004 #if 0
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);
3010 #endif
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);
3033 #if 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);
3051 #endif
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);
3081 #if 0
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
3085 button is clicked
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);
3096 #endif
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. */
3125 /* */
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 */
3140 static void
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) {
3170 f_clock_rate = 1;
3173 if (r_clock_rate == 0) {
3174 r_clock_rate = 1;
3177 if (f_expected) {
3178 f_perc = (double)(f_lost*100)/(double)f_expected;
3179 } else {
3180 f_perc = 0;
3182 if (r_expected) {
3183 r_perc = (double)(r_lost*100)/(double)r_expected;
3184 } else {
3185 r_perc = 0;
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,
3205 f_duration / 1000,
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,
3224 r_duration / 1000,
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);
3232 return ;
3237 /****************************************************************************/
3238 /* append a line to list */
3239 static void
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,
3264 SKEW_COLUMN, skew,
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,
3272 -1);
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 */
3286 static void
3287 rtp_boolean_data_func (GtkTreeViewColumn *column _U_,
3288 GtkCellRenderer *renderer,
3289 GtkTreeModel *model,
3290 GtkTreeIter *iter,
3291 gpointer user_data)
3293 gboolean bool_val;
3294 gchar buf[20];
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);
3300 switch(bool_col) {
3301 case MARKER_COLUMN:
3302 g_strlcpy(buf, bool_val ? "SET" : "", sizeof(buf));
3303 break;
3304 default:
3305 g_assert_not_reached();
3306 break;
3308 g_object_set(renderer, "text", buf, NULL);
3311 /* Create list */
3312 static GtkWidget *
3313 create_list(user_data_t* user_data)
3316 GtkListStore *list_store;
3317 GtkWidget *list;
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 */
3340 /* Create a view */
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,
3365 NULL);
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);
3374 /* Sequence. */
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,
3380 NULL);
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);
3387 #if 0
3388 Currently not visible
3389 /* Time stamp. */
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,
3395 NULL);
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);
3401 #endif
3402 /* Delta(ms). */
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,
3408 NULL);
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);
3419 /* Jitter(ms). */
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,
3425 NULL);
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);
3436 /* Skew(ms). */
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,
3442 NULL);
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);
3453 /* IP BW(kbps). */
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,
3459 NULL);
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);
3470 /* Marker. */
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,
3476 NULL);
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);
3487 /* Status. */
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,
3493 NULL);
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),
3510 user_data);
3511 return list;
3514 /****************************************************************************/
3515 /* Create the dialog box with all widgets */
3516 static void
3517 create_rtp_dialog(user_data_t* user_data)
3519 GtkWidget *window;
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;
3527 GtkWidget *label;
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];
3538 gchar str_src[16];
3539 gchar str_dst[16];
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);
3550 /* Notebooks... */
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);
3595 /* packet list */
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);
3602 /* tab */
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 */
3626 #if 0
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);
3636 #endif
3638 /* show all notebooks */
3639 gtk_widget_show_all(notebook);
3641 /* buttons */
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 /****************************************************************************/
3719 static gboolean
3720 process_node(proto_node *ptree_node, header_field_info *hfinformation,
3721 const gchar* proto_field, guint32* p_result)
3723 field_info *finfo;
3724 proto_node *proto_sibling_node;
3725 header_field_info *hfssrc;
3726 ipv4_addr *ipv4;
3728 finfo = PNODE_FINFO(ptree_node);
3730 /* Caller passed top of the protocol tree. Expected child node */
3731 g_assert(finfo);
3733 if (hfinformation == (finfo->hfinfo)) {
3734 hfssrc = proto_registrar_get_byname(proto_field);
3735 if (hfssrc == NULL)
3736 return FALSE;
3737 for (ptree_node = ptree_node->first_child;
3738 ptree_node != NULL;
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);
3746 else {
3747 *p_result = fvalue_get_uinteger(&finfo->value);
3749 return TRUE;
3752 if (!ptree_node)
3753 return FALSE;
3756 proto_sibling_node = ptree_node->next;
3758 if (proto_sibling_node) {
3759 return process_node(proto_sibling_node, hfinformation, proto_field, p_result);
3761 else
3762 return FALSE;
3765 /****************************************************************************/
3766 static gboolean
3767 get_int_value_from_proto_tree(proto_tree *protocol_tree,
3768 const gchar *proto_name,
3769 const gchar *proto_field,
3770 guint32 *p_result)
3772 proto_node *ptree_node;
3773 header_field_info *hfinformation;
3775 hfinformation = proto_registrar_get_byname(proto_name);
3776 if (hfinformation == NULL)
3777 return FALSE;
3779 ptree_node = ((proto_node *)protocol_tree)->first_child;
3780 if (!ptree_node)
3781 return FALSE;
3783 return process_node(ptree_node, hfinformation, proto_field, p_result);
3787 /****************************************************************************/
3788 void
3789 rtp_analysis(address *src_fwd,
3790 guint32 port_src_fwd,
3791 address *dst_fwd,
3792 guint32 port_dst_fwd,
3793 guint32 ssrc_fwd,
3794 address *src_rev,
3795 guint32 port_src_rev,
3796 address *dst_rev,
3797 guint32 port_dst_rev,
3798 guint32 ssrc_rev)
3800 user_data_t *user_data;
3801 int fd;
3802 int i;
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 */
3820 char *tempname;
3822 /* init */
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");
3839 if (fd < 0) {
3840 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3841 "Can't create temporary file for RTP analysis:\n%s.",
3842 g_strerror(errno));
3843 g_free(user_data);
3844 return;
3846 user_data->f_tempname = g_strdup(tempname);
3847 ws_close(fd);
3848 fd = create_tempfile(&tempname, "wireshark_rtp_r");
3849 if (fd < 0) {
3850 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
3851 "Can't create temporary file for RTP analysis:\n%s.",
3852 g_strerror(errno));
3853 g_free(user_data->f_tempname);
3854 g_free(user_data);
3855 return;
3857 user_data->r_tempname = g_strdup(tempname);
3858 ws_close(fd);
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;
3871 #else
3872 user_data->dlg.dialog_graph.pixmap = NULL;
3873 #endif
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 */
3908 void
3909 rtp_analysis_cb(GtkAction *action _U_, gpointer user_data _U_)
3911 address src_fwd;
3912 guint32 port_src_fwd;
3913 address dst_fwd;
3914 guint32 port_dst_fwd;
3915 guint32 ssrc_fwd = 0;
3916 address src_rev;
3917 guint32 port_src_rev;
3918 address dst_rev;
3919 guint32 port_dst_rev;
3920 guint32 ssrc_rev = 0;
3921 unsigned int version_fwd;
3923 gchar filter_text[256];
3924 dfilter_t *sfcode;
3925 capture_file *cf;
3926 gboolean frame_matched;
3927 frame_data *fdata;
3928 GList *strinfo_list;
3929 GList *filtered_list = NULL;
3930 guint nfound;
3931 epan_dissect_t edt;
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);
3938 return;
3940 /* we load the current file into cf variable */
3941 cf = &cfile;
3942 fdata = cf->current_frame;
3944 /* we are on the selected frame now */
3945 if (fdata == NULL)
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.");
3961 return;
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.");
3980 return;
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.");
3987 return;
3990 /* Scan for rtpstream */
3991 rtpstream_scan();
3992 /* search for reversed direction in the global rtp streams list */
3993 nfound = 0;
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))
4011 ++nfound;
4012 filtered_list = g_list_append(filtered_list, strinfo);
4013 if (ssrc_rev == 0)
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 */
4021 if (nfound>1) {
4022 rtpstream_dlg_show(filtered_list);
4023 return;
4025 else {
4026 rtp_analysis(
4027 &src_fwd,
4028 port_src_fwd,
4029 &dst_fwd,
4030 port_dst_fwd,
4031 ssrc_fwd,
4032 &src_rev,
4033 port_src_rev,
4034 &dst_rev,
4035 port_dst_rev,
4036 ssrc_rev
4041 /****************************************************************************/
4042 static void
4043 rtp_analysis_init(const char *dummy _U_, void *userdata _U_)
4045 rtp_analysis_cb(NULL, NULL);
4048 /****************************************************************************/
4049 void
4050 register_tap_listener_rtp_analysis(void)
4052 register_stat_cmd_arg("rtp", rtp_analysis_init, NULL);