2 * RTP streams summary addition for Wireshark
6 * Copyright 2003, Alcatel Business Systems
7 * By Lars Ruoff <lars.ruoff@gmx.net>
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
34 #include <epan/rtp_pt.h>
35 #include <epan/address.h>
36 #include <epan/addr_resolv.h>
37 #include <epan/strutil.h>
38 #include "epan/filesystem.h"
40 #include "../globals.h"
41 #include "../stat_menu.h"
43 #include "ui/last_open_dir.h"
44 #include "ui/simple_dialog.h"
47 #include "ui/gtk/rtp_stream_dlg.h"
48 #include "ui/gtk/gui_stat_menu.h"
49 #include "ui/gtk/dlg_utils.h"
50 #include "ui/gtk/file_dlg.h"
51 #include "ui/gtk/gui_utils.h"
52 #include "ui/gtk/gtkglobals.h"
53 #include "ui/rtp_stream.h"
54 #include "ui/rtp_analysis.h"
55 #include "ui/gtk/stock_icons.h"
56 #include "ui/gtk/old-gtk-compat.h"
58 static const gchar FWD_LABEL_TEXT
[] = "Select a forward stream with left mouse button, and then";
59 static const gchar FWD_ONLY_LABEL_TEXT
[] = "Select a forward stream with Ctrl + left mouse button";
60 static const gchar REV_LABEL_TEXT
[] = "Select a reverse stream with Ctrl + left mouse button";
62 /****************************************************************************/
63 /* pointer to the one and only dialog window */
64 static GtkWidget
*rtp_stream_dlg
= NULL
;
66 /* save as dialog box */
67 static GtkWidget
*rtpstream_save_dlg
= NULL
;
68 static GtkListStore
*list_store
= NULL
;
69 static GtkTreeIter list_iter
;
70 static GtkWidget
*list
= NULL
;
71 static GtkWidget
*top_label
= NULL
;
72 static GtkWidget
*label_fwd
= NULL
;
73 static GtkWidget
*label_rev
= NULL
;
75 static rtp_stream_info_t
* selected_stream_fwd
= NULL
; /* current selection */
76 static rtp_stream_info_t
* selected_stream_rev
= NULL
; /* current selection for reversed */
77 static GList
*last_list
= NULL
;
79 static guint32 streams_nb
= 0; /* number of displayed streams */
96 NUM_COLS
/* The number of columns */
100 /****************************************************************************/
101 static void save_stream_destroy_cb(GtkWidget
*win _U_
, gpointer user_data _U_
)
103 /* Note that we no longer have a Save voice info dialog box. */
104 rtpstream_save_dlg
= NULL
;
107 /****************************************************************************/
109 static gboolean
save_stream_ok_cb(GtkWidget
*ok_bt _U_
, gpointer fs
)
113 if (!selected_stream_fwd
) {
117 g_dest
= gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs
));
119 /* Perhaps the user specified a directory instead of a file.
120 Check whether they did. */
121 if (test_for_directory(g_dest
) == EISDIR
) {
122 /* It's a directory - set the file selection box to display it. */
123 set_last_open_dir(g_dest
);
125 file_selection_set_current_folder((GtkWidget
*)fs
, get_last_open_dir());
126 gtk_file_chooser_set_current_name((GtkFileChooser
*)fs
, "");
130 #if 0 /* GtkFileChooser/gtk_dialog_run currently being used. */
131 /* So: Leaving the dialog box displayed after popping-up an */
132 /* alert box won't work. */
134 * Don't dismiss the dialog box if the save operation fails.
136 if (!rtpstream_save(selected_stream_fwd
, g_dest
)) {
141 window_destroy(GTK_WIDGET(rtpstream_save_dlg
));
144 /* Dialog box needs to be always destroyed. Return TRUE */
145 /* so that caller will destroy the dialog box. */
146 /* See comment under rtpstream_on_save. */
147 rtpstream_save(selected_stream_fwd
, g_dest
);
154 /****************************************************************************/
156 /****************************************************************************/
158 rtpstream_on_destroy(GObject
*object _U_
, gpointer user_data _U_
)
160 /* Remove the stream tap listener */
161 remove_tap_listener_rtp_stream();
163 /* Is there a save voice window open? */
164 if (rtpstream_save_dlg
!= NULL
)
165 window_destroy(rtpstream_save_dlg
);
167 /* Clean up memory used by stream tap */
168 rtpstream_reset((rtpstream_tapinfo_t
*)rtpstream_get_info());
170 /* Note that we no longer have a "RTP Streams" dialog box. */
171 rtp_stream_dlg
= NULL
;
175 /****************************************************************************/
177 rtpstream_on_unselect(GtkButton
*button _U_
, gpointer user_data _U_
)
179 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
180 gtk_tree_selection_unselect_all(selection
);
182 selected_stream_fwd
= NULL
;
183 selected_stream_rev
= NULL
;
184 gtk_label_set_text(GTK_LABEL(label_fwd
), FWD_LABEL_TEXT
);
185 gtk_label_set_text(GTK_LABEL(label_rev
), REV_LABEL_TEXT
);
189 /****************************************************************************/
190 static gint
rtp_stream_info_cmp_reverse(gconstpointer aa
, gconstpointer bb
)
192 const struct _rtp_stream_info
* a
= (struct _rtp_stream_info
*)aa
;
193 const struct _rtp_stream_info
* b
= (struct _rtp_stream_info
*)bb
;
195 if (a
==NULL
|| b
==NULL
)
197 if ((ADDRESSES_EQUAL(&(a
->src_addr
), &(b
->dest_addr
)))
198 && (a
->src_port
== b
->dest_port
)
199 && (ADDRESSES_EQUAL(&(a
->dest_addr
), &(b
->src_addr
)))
200 && (a
->dest_port
== b
->src_port
))
206 /****************************************************************************/
208 rtpstream_on_findrev(GtkButton
*button _U_
, gpointer user_data _U_
)
210 GtkTreeSelection
*selection
;
212 GList
*path_list_item
= NULL
;
213 GtkTreePath
*path
= NULL
;
214 GtkTreePath
*path_fwd
= NULL
;
215 GtkTreePath
*path_rev
= NULL
;
217 rtp_stream_info_t
*stream
= NULL
;
218 gboolean found_it
= FALSE
;
220 if (selected_stream_fwd
==NULL
)
223 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(list
));
224 path_list
= gtk_tree_selection_get_selected_rows(selection
, NULL
);
227 path_list_item
= g_list_first(path_list
);
228 path
= (GtkTreePath
*)(path_list_item
->data
);
231 if (path
&& gtk_tree_model_get_iter(GTK_TREE_MODEL(list_store
), &iter
, path
)) {
232 gtk_tree_model_get(GTK_TREE_MODEL(list_store
), &iter
, RTP_COL_DATA
, &stream
, -1);
233 if (stream
== selected_stream_fwd
) {
236 if (stream
== selected_stream_rev
) {
242 if (path_list_item
) {
243 path_list_item
= g_list_next(path_list_item
);
245 path
= (GtkTreePath
*)(path_list_item
->data
);
248 if (path
&& gtk_tree_model_get_iter(GTK_TREE_MODEL(list_store
), &iter
, path
)) {
249 gtk_tree_model_get(GTK_TREE_MODEL(list_store
), &iter
, RTP_COL_DATA
, &stream
, -1);
250 if (stream
== selected_stream_fwd
) {
253 if (stream
== selected_stream_rev
) {
258 /* Find it from the forward stream on */
259 gtk_tree_model_get_iter(GTK_TREE_MODEL(list_store
), &iter
, path_fwd
);
260 while (gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store
), &iter
)) {
261 gtk_tree_model_get(GTK_TREE_MODEL(list_store
), &iter
, RTP_COL_DATA
, &stream
, -1);
262 if (rtp_stream_info_cmp_reverse(selected_stream_fwd
, stream
) == 0) {
269 /* If we're not done yet, restart at the beginning */
270 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store
), &iter
);
272 gtk_tree_model_get(GTK_TREE_MODEL(list_store
), &iter
, RTP_COL_DATA
, &stream
, -1);
273 if (rtp_stream_info_cmp_reverse(selected_stream_fwd
, stream
) == 0) {
277 if (stream
== selected_stream_fwd
)
279 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store
), &iter
));
284 gtk_tree_selection_unselect_path(selection
, path_rev
);
285 gtk_tree_selection_select_iter(selection
, &iter
);
288 g_list_foreach(path_list
, (GFunc
)gtk_tree_path_free
, NULL
);
289 g_list_free(path_list
);
293 /****************************************************************************/
296 rtpstream_on_goto (GtkButton *button _U_,
297 gpointer user_data _U_)
299 if (selected_stream_fwd)
301 cf_goto_frame(&cfile, selected_stream_fwd->first_frame_num);
307 /****************************************************************************/
309 rtpstream_on_save(GtkButton
*button _U_
, gpointer data _U_
)
312 rtpstream_tapinfo_t* tapinfo = data;
315 if (!selected_stream_fwd
) {
316 simple_dialog(ESD_TYPE_ERROR
, ESD_BTN_OK
,
317 "Please select a forward stream");
321 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
322 if (rtpstream_save_dlg
!= NULL
) {
323 /* There's already a Save dialog box; reactivate it. */
324 reactivate_window(rtpstream_save_dlg
);
329 rtpstream_save_dlg
= gtk_file_chooser_dialog_new(
330 "Wireshark: Save selected stream in rtpdump ('-F dump') format",
331 GTK_WINDOW(rtp_stream_dlg
), GTK_FILE_CHOOSER_ACTION_SAVE
,
332 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
,
333 GTK_STOCK_CANCEL
, GTK_RESPONSE_CANCEL
,
335 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(rtpstream_save_dlg
), TRUE
);
337 g_signal_connect(rtpstream_save_dlg
, "delete_event", G_CALLBACK(window_delete_event_cb
), NULL
);
338 g_signal_connect(rtpstream_save_dlg
, "destroy", G_CALLBACK(save_stream_destroy_cb
), NULL
);
340 gtk_widget_show(rtpstream_save_dlg
);
341 window_present(rtpstream_save_dlg
);
343 if (gtk_dialog_run(GTK_DIALOG(rtpstream_save_dlg
)) == GTK_RESPONSE_ACCEPT
){
344 save_stream_ok_cb(rtpstream_save_dlg
, rtpstream_save_dlg
);
346 window_destroy(rtpstream_save_dlg
);
349 /* "Run" the GtkFileChooserDialog. */
350 /* Upon exit: If "Accept" run the OK callback. */
351 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
352 /* If not accept (ie: cancel) destroy the window. */
353 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
354 /* return with a TRUE status so that the dialog window will be destroyed. */
355 /* Trying to re-run the dialog after popping up an alert box will not work */
356 /* since the user will not be able to dismiss the alert box. */
357 /* The (somewhat unfriendly) effect: the user must re-invoke the */
358 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
360 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
361 /* GtkFileChooserDialog. */
362 while (gtk_dialog_run(GTK_DIALOG(rtpstream_save_dlg
)) == GTK_RESPONSE_ACCEPT
) {
363 if (save_stream_ok_cb(NULL
, rtpstream_save_dlg
)) {
364 break; /* we're done */
367 window_destroy(rtpstream_save_dlg
);
371 /****************************************************************************/
373 rtpstream_on_mark(GtkButton
*button _U_
, gpointer user_data _U_
)
375 if (selected_stream_fwd
==NULL
&& selected_stream_rev
==NULL
)
377 rtpstream_mark(selected_stream_fwd
, selected_stream_rev
);
381 /****************************************************************************/
383 rtpstream_on_filter(GtkButton
*button _U_
, gpointer user_data _U_
)
385 gchar
*filter_string
= NULL
;
386 gchar
*filter_string_fwd
= NULL
;
387 gchar
*filter_string_rev
= NULL
;
390 if (selected_stream_fwd
==NULL
&& selected_stream_rev
==NULL
)
393 if (selected_stream_fwd
)
395 if (selected_stream_fwd
->src_addr
.type
==AT_IPv6
) {
396 g_strlcpy(ip_version
,"v6",sizeof(ip_version
));
398 ip_version
[0] = '\0';
400 filter_string_fwd
= g_strdup_printf(
401 "(ip%s.src==%s && udp.srcport==%u && ip%s.dst==%s && udp.dstport==%u && rtp.ssrc==0x%X)",
403 ep_address_to_str(&(selected_stream_fwd
->src_addr
)),
404 selected_stream_fwd
->src_port
,
406 ep_address_to_str(&(selected_stream_fwd
->dest_addr
)),
407 selected_stream_fwd
->dest_port
,
408 selected_stream_fwd
->ssrc
);
410 filter_string
= filter_string_fwd
;
413 if (selected_stream_rev
)
415 if (selected_stream_rev
->src_addr
.type
==AT_IPv6
) {
416 g_strlcpy(ip_version
,"v6",sizeof(ip_version
));
418 ip_version
[0] = '\0';
420 filter_string_rev
= g_strdup_printf(
421 "(ip%s.src==%s && udp.srcport==%u && ip%s.dst==%s && udp.dstport==%u && rtp.ssrc==0x%X)",
423 ep_address_to_str(&(selected_stream_rev
->src_addr
)),
424 selected_stream_rev
->src_port
,
426 ep_address_to_str(&(selected_stream_rev
->dest_addr
)),
427 selected_stream_rev
->dest_port
,
428 selected_stream_rev
->ssrc
);
430 filter_string
= filter_string_rev
;
433 if ((selected_stream_fwd
) && (selected_stream_rev
))
435 filter_string
= g_strdup_printf("%s || %s", filter_string_fwd
, filter_string_rev
);
436 g_free(filter_string_fwd
);
437 g_free(filter_string_rev
);
440 gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget
), filter_string
);
441 g_free(filter_string
);
444 main_filter_packets(&cfile, filter_string, FALSE);
445 rtpstream_dlg_update(rtpstream_get_info()->strinfo_list);
450 /****************************************************************************/
452 rtpstream_on_copy_as_csv(GtkWindow
*win _U_
, gpointer data _U_
)
454 GtkTreeViewColumn
*column
;
459 guint table_entry_uint
;
464 CSV_str
= g_string_sized_new(240*(1+streams_nb
));
465 /* Add the column headers to the CSV data */
466 for (j
=0; j
<NUM_COLS
-1; j
++) {
467 column
= gtk_tree_view_get_column(GTK_TREE_VIEW(list
), j
);
468 title
= gtk_tree_view_column_get_title(column
);
469 g_string_append_printf(CSV_str
, "\"%s\"", title
);
470 if (j
<NUM_COLS
-2) g_string_append(CSV_str
, ",");
472 g_string_append(CSV_str
,"\n");
474 /* Add the column values to the CSV data */
475 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store
), &iter
)) {
476 for (i
=0; i
<streams_nb
; i
++) {
477 for (j
=0; j
<NUM_COLS
-1; j
++) {
478 if (j
== RTP_COL_SRC_PORT
|| j
== RTP_COL_DST_PORT
|| j
== RTP_COL_PACKETS
) {
479 gtk_tree_model_get(GTK_TREE_MODEL(list_store
), &iter
, j
, &table_entry_uint
, -1);
480 g_string_append_printf(CSV_str
, "\"%u\"", table_entry_uint
);
482 gtk_tree_model_get(GTK_TREE_MODEL(list_store
), &iter
, j
, &table_entry
, -1);
483 g_string_append_printf(CSV_str
, "\"%s\"", table_entry
);
486 if (j
<NUM_COLS
-2) g_string_append(CSV_str
,",");
488 g_string_append(CSV_str
,"\n");
489 gtk_tree_model_iter_next (GTK_TREE_MODEL(list_store
),&iter
);
493 /* Now that we have the CSV data, copy it into the default clipboard */
494 cb
= gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
);
495 gtk_clipboard_set_text(cb
, CSV_str
->str
, (gint
)CSV_str
->len
);
496 g_string_free(CSV_str
, TRUE
);
499 /****************************************************************************/
501 rtpstream_on_analyse(GtkButton
*button _U_
, gpointer user_data _U_
)
504 guint32 port_src_fwd
= 0;
506 guint32 port_dst_fwd
= 0;
507 guint32 ssrc_fwd
= 0;
509 guint32 port_src_rev
= 0;
511 guint32 port_dst_rev
= 0;
512 guint32 ssrc_rev
= 0;
514 if (!(selected_stream_fwd
|| selected_stream_rev
))
519 SET_ADDRESS(&src_fwd
,AT_NONE
,0,NULL
);
520 SET_ADDRESS(&dst_fwd
,AT_NONE
,0,NULL
);
521 SET_ADDRESS(&src_rev
,AT_NONE
,0,NULL
);
522 SET_ADDRESS(&dst_rev
,AT_NONE
,0,NULL
);
524 if (selected_stream_fwd
) {
525 COPY_ADDRESS(&(src_fwd
), &(selected_stream_fwd
->src_addr
));
526 port_src_fwd
= selected_stream_fwd
->src_port
;
527 COPY_ADDRESS(&(dst_fwd
), &(selected_stream_fwd
->dest_addr
));
528 port_dst_fwd
= selected_stream_fwd
->dest_port
;
529 ssrc_fwd
= selected_stream_fwd
->ssrc
;
532 if (selected_stream_rev
) {
533 COPY_ADDRESS(&(src_rev
), &(selected_stream_rev
->src_addr
));
534 port_src_rev
= selected_stream_rev
->src_port
;
535 COPY_ADDRESS(&(dst_rev
), &(selected_stream_rev
->dest_addr
));
536 port_dst_rev
= selected_stream_rev
->dest_port
;
537 ssrc_rev
= selected_stream_rev
->ssrc
;
556 /****************************************************************************/
557 /* when the user selects a row in the stream list */
559 rtpstream_view_selection_func(GtkTreeSelection
*selection
, GtkTreeModel
*model
, GtkTreePath
*path
, gboolean path_currently_selected
, gpointer userdata _U_
)
563 rtp_stream_info_t
* selected_stream
;
564 gboolean result
= TRUE
;
565 gchar label_text
[80];
568 * nb_selected path_currently_selected forward reverse action result
569 * 0 must be false any any assign forward true
570 * 1 true match any delete forward true
571 * 1 true other any delete reverse true
572 * 1 false match any invalid true
573 * 1 false other none assign reverse true
574 * 1 false other any assign forward true
575 * 2 true match any delete forward path_currently_selected
576 * 2 true other match delete reverse path_currently_selected
577 * 2 true other other invalid path_currently_selected
578 * 2 false match any invalid path_currently_selected
579 * 2 false any match invalid path_currently_selected
580 * 2 false other other assign reverse path_currently_selected
581 * >2 any any any invalid path_currently_selected
584 nb_selected
= gtk_tree_selection_count_selected_rows(selection
);
585 if (gtk_tree_model_get_iter(model
, &iter
, path
)) {
586 gtk_tree_model_get(GTK_TREE_MODEL(list_store
), &iter
, RTP_COL_DATA
, &selected_stream
, -1);
592 if (path_currently_selected
)
593 g_print("Select: He, we've got a selected path while none is selected?\n");
595 selected_stream_fwd
= selected_stream
;
600 if (path_currently_selected
)
601 if (selected_stream
== selected_stream_fwd
)
602 selected_stream_fwd
= NULL
;
604 selected_stream_rev
= NULL
;
606 if (selected_stream
== selected_stream_fwd
)
607 g_print("Select: He, this can't be. 1 not selected but equal to fwd\n");
609 if (selected_stream_rev
)
610 selected_stream_fwd
= selected_stream
;
612 selected_stream_rev
= selected_stream
;
617 if (path_currently_selected
) {
618 if (selected_stream
== selected_stream_fwd
)
619 selected_stream_fwd
= NULL
;
620 else if (selected_stream
== selected_stream_rev
)
621 selected_stream_rev
= NULL
;
623 g_print("Select: He, this can't be. 2 selected but not equal to fwd or rev\n");
625 result
= path_currently_selected
;
630 g_print("Select: He, we're getting a too high selection count\n");
631 result
= path_currently_selected
;
636 if (selected_stream_fwd
) {
637 g_snprintf(label_text
, sizeof(label_text
), "Forward: %s:%u -> %s:%u, SSRC=0x%X",
638 get_addr_name(&(selected_stream_fwd
->src_addr
)),
639 selected_stream_fwd
->src_port
,
640 get_addr_name(&(selected_stream_fwd
->dest_addr
)),
641 selected_stream_fwd
->dest_port
,
642 selected_stream_fwd
->ssrc
644 gtk_label_set_text(GTK_LABEL(label_fwd
), label_text
);
646 if (selected_stream_rev
)
647 gtk_label_set_text(GTK_LABEL(label_fwd
), FWD_ONLY_LABEL_TEXT
);
649 gtk_label_set_text(GTK_LABEL(label_fwd
), FWD_LABEL_TEXT
);
652 if (selected_stream_rev
) {
653 g_snprintf(label_text
, sizeof(label_text
), "Reverse: %s:%u -> %s:%u, SSRC=0x%X",
654 get_addr_name(&(selected_stream_rev
->src_addr
)),
655 selected_stream_rev
->src_port
,
656 get_addr_name(&(selected_stream_rev
->dest_addr
)),
657 selected_stream_rev
->dest_port
,
658 selected_stream_rev
->ssrc
660 gtk_label_set_text(GTK_LABEL(label_rev
), label_text
);
662 gtk_label_set_text(GTK_LABEL(label_rev
), REV_LABEL_TEXT
);
668 /****************************************************************************/
670 /****************************************************************************/
671 /* append a line to list */
673 add_to_list_store(rtp_stream_info_t
* strinfo
)
675 gchar label_text
[256];
676 gchar
*data
[NUM_COLS
];
683 /* save the current locale */
684 savelocale
= setlocale(LC_NUMERIC
, NULL
);
685 /* switch to "C" locale to avoid problems with localized decimal separators
686 in g_snprintf("%f") functions */
687 setlocale(LC_NUMERIC
, "C");
689 data
[0] = g_strdup(get_addr_name(&(strinfo
->src_addr
)));
691 data
[2] = g_strdup(get_addr_name(&(strinfo
->dest_addr
)));
693 data
[4] = g_strdup_printf("0x%X", strinfo
->ssrc
);
694 if (strinfo
->info_payload_type_str
!= NULL
) {
695 data
[5] = g_strdup(strinfo
->info_payload_type_str
);
697 data
[5] = g_strdup(val_to_str_ext(strinfo
->pt
, &rtp_payload_type_short_vals_ext
,
702 expected
= (strinfo
->rtp_stats
.stop_seq_nr
+ strinfo
->rtp_stats
.cycles
*65536)
703 - strinfo
->rtp_stats
.start_seq_nr
+ 1;
704 lost
= expected
- strinfo
->rtp_stats
.total_nr
;
706 perc
= (double)(lost
*100)/(double)expected
;
710 data
[7] = g_strdup_printf("%d (%.1f%%)", lost
, perc
);
711 data
[8] = g_strdup_printf("%.2f", strinfo
->rtp_stats
.max_delta
);
712 data
[9] = g_strdup_printf("%.2f", strinfo
->rtp_stats
.max_jitter
);
713 data
[10] = g_strdup_printf("%.2f", strinfo
->rtp_stats
.mean_jitter
);
714 if (strinfo
->problem
)
715 data
[11] = g_strdup("X");
717 data
[11] = g_strdup("");
719 /* restore previous locale setting */
720 setlocale(LC_NUMERIC
, savelocale
);
722 /* Acquire an iterator */
723 gtk_list_store_append(list_store
, &list_iter
);
725 /* Fill the new row */
726 gtk_list_store_set(list_store
, &list_iter
,
727 RTP_COL_SRC_ADDR
, data
[0],
728 RTP_COL_SRC_PORT
, strinfo
->src_port
,
729 RTP_COL_DST_ADDR
, data
[2],
730 RTP_COL_DST_PORT
, strinfo
->dest_port
,
731 RTP_COL_SSRC
, data
[4],
732 RTP_COL_PAYLOAD
, data
[5],
733 RTP_COL_PACKETS
, strinfo
->npackets
,
734 RTP_COL_LOST
, data
[7],
735 RTP_COL_MAX_DELTA
, data
[8],
736 RTP_COL_MAX_JITTER
, data
[9],
737 RTP_COL_MEAN_JITTER
, data
[10],
738 RTP_COL_PROBLEM
, data
[11],
739 RTP_COL_DATA
, strinfo
,
742 for (i
= 0; i
< NUM_COLS
-1; i
++)
745 /* Update the top label with the number of detected streams */
746 g_snprintf(label_text
, sizeof(label_text
),
747 "Detected %d RTP streams. Choose one for forward and reverse direction for analysis",
749 gtk_label_set_text(GTK_LABEL(top_label
), label_text
);
752 /****************************************************************************/
753 /* Create list view */
755 create_list_view(void)
757 GtkTreeViewColumn
*column
;
758 GtkCellRenderer
*renderer
;
759 GtkTreeSortable
*sortable
;
760 GtkTreeView
*list_view
;
761 GtkTreeSelection
*selection
;
763 /* Create the store */
764 list_store
= gtk_list_store_new(NUM_COLS
, /* Total number of columns */
765 G_TYPE_STRING
, /* Source address */
766 G_TYPE_UINT
, /* Source port */
767 G_TYPE_STRING
, /* Destination address */
768 G_TYPE_UINT
, /* Destination port */
769 G_TYPE_STRING
, /* SSRC */
770 G_TYPE_STRING
, /* Payload */
771 G_TYPE_UINT
, /* Packets */
772 G_TYPE_STRING
, /* Lost */
773 G_TYPE_STRING
, /* Max. delta */
774 G_TYPE_STRING
, /* Max. jitter */
775 G_TYPE_STRING
, /* Mean jitter */
776 G_TYPE_STRING
, /* Problem */
777 G_TYPE_POINTER
/* Data */
781 list
= gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store
));
783 list_view
= GTK_TREE_VIEW(list
);
784 sortable
= GTK_TREE_SORTABLE(list_store
);
786 /* Speed up the list display */
787 gtk_tree_view_set_fixed_height_mode(list_view
, TRUE
);
789 /* Setup the sortable columns */
790 gtk_tree_sortable_set_sort_column_id(sortable
, RTP_COL_SRC_ADDR
, GTK_SORT_ASCENDING
);
791 gtk_tree_view_set_headers_clickable(list_view
, FALSE
);
793 /* The view now holds a reference. We can get rid of our own reference */
794 g_object_unref(G_OBJECT(list_store
));
797 * Create the first column packet, associating the "text" attribute of the
798 * cell_renderer to the first column of the model
800 renderer
= gtk_cell_renderer_text_new();
801 column
= gtk_tree_view_column_new_with_attributes("Src addr", renderer
,
802 "text", RTP_COL_SRC_ADDR
,
804 gtk_tree_view_column_set_sort_column_id(column
, RTP_COL_SRC_ADDR
);
805 gtk_tree_view_column_set_resizable(column
, TRUE
);
806 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
807 gtk_tree_view_column_set_min_width(column
, 60);
808 gtk_tree_view_column_set_fixed_width(column
, 100);
809 /* Add the column to the view. */
810 gtk_tree_view_append_column(list_view
, column
);
813 renderer
= gtk_cell_renderer_text_new();
814 column
= gtk_tree_view_column_new_with_attributes("Src port", renderer
,
815 "text", RTP_COL_SRC_PORT
,
817 gtk_tree_view_column_set_sort_column_id(column
, RTP_COL_SRC_PORT
);
818 gtk_tree_view_column_set_resizable(column
, TRUE
);
819 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
820 gtk_tree_view_column_set_min_width(column
, 60);
821 gtk_tree_view_column_set_fixed_width(column
, 80);
822 gtk_tree_view_append_column(list_view
, column
);
824 /* Destination address */
825 renderer
= gtk_cell_renderer_text_new();
826 column
= gtk_tree_view_column_new_with_attributes("Dst addr", renderer
,
827 "text", RTP_COL_DST_ADDR
,
829 gtk_tree_view_column_set_sort_column_id(column
, RTP_COL_DST_ADDR
);
830 gtk_tree_view_column_set_resizable(column
, TRUE
);
831 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
832 gtk_tree_view_column_set_min_width(column
, 60);
833 gtk_tree_view_column_set_fixed_width(column
, 100);
834 gtk_tree_view_append_column(list_view
, column
);
836 /* Destination port */
837 renderer
= gtk_cell_renderer_text_new();
838 column
= gtk_tree_view_column_new_with_attributes("Dst port", renderer
,
839 "text", RTP_COL_DST_PORT
,
841 gtk_tree_view_column_set_sort_column_id(column
, RTP_COL_DST_PORT
);
842 gtk_tree_view_column_set_resizable(column
, TRUE
);
843 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
844 gtk_tree_view_column_set_min_width(column
, 60);
845 gtk_tree_view_column_set_fixed_width(column
, 80);
846 gtk_tree_view_append_column(list_view
, column
);
849 renderer
= gtk_cell_renderer_text_new();
850 column
= gtk_tree_view_column_new_with_attributes("SSRC", renderer
,
851 "text", RTP_COL_SSRC
,
853 gtk_tree_view_column_set_sort_column_id(column
, RTP_COL_SSRC
);
854 gtk_tree_view_column_set_resizable(column
, TRUE
);
855 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
856 gtk_tree_view_column_set_min_width(column
, 70);
857 gtk_tree_view_column_set_fixed_width(column
, 90);
858 gtk_tree_view_append_column(list_view
, column
);
861 renderer
= gtk_cell_renderer_text_new();
862 column
= gtk_tree_view_column_new_with_attributes("Payload", renderer
,
863 "text", RTP_COL_PAYLOAD
,
865 gtk_tree_view_column_set_sort_column_id(column
, RTP_COL_PAYLOAD
);
866 gtk_tree_view_column_set_resizable(column
, TRUE
);
867 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
868 gtk_tree_view_column_set_min_width(column
, 80);
869 gtk_tree_view_column_set_fixed_width(column
, 100);
870 gtk_tree_view_append_column(list_view
, column
);
873 renderer
= gtk_cell_renderer_text_new();
874 column
= gtk_tree_view_column_new_with_attributes("Packets", renderer
,
875 "text", RTP_COL_PACKETS
,
877 gtk_tree_view_column_set_sort_column_id(column
, RTP_COL_PACKETS
);
878 gtk_tree_view_column_set_resizable(column
, TRUE
);
879 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
880 gtk_tree_view_column_set_min_width(column
, 60);
881 gtk_tree_view_column_set_fixed_width(column
, 70);
882 gtk_tree_view_append_column(list_view
, column
);
885 renderer
= gtk_cell_renderer_text_new();
886 column
= gtk_tree_view_column_new_with_attributes("Lost", renderer
,
887 "text", RTP_COL_LOST
,
889 gtk_tree_view_column_set_sort_column_id(column
, RTP_COL_LOST
);
890 gtk_tree_view_column_set_resizable(column
, TRUE
);
891 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
892 gtk_tree_view_column_set_min_width(column
, 60);
893 gtk_tree_view_column_set_fixed_width(column
, 90);
894 gtk_tree_view_append_column(list_view
, column
);
897 renderer
= gtk_cell_renderer_text_new();
898 column
= gtk_tree_view_column_new_with_attributes("Max Delta (ms)", renderer
,
899 "text", RTP_COL_MAX_DELTA
,
901 gtk_tree_view_column_set_sort_column_id(column
, RTP_COL_MAX_DELTA
);
902 gtk_tree_view_column_set_resizable(column
, TRUE
);
903 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
904 gtk_tree_view_column_set_min_width(column
, 90);
905 gtk_tree_view_column_set_fixed_width(column
, 130);
906 gtk_tree_view_append_column(list_view
, column
);
908 /* Max Jitter (ms) */
909 renderer
= gtk_cell_renderer_text_new();
910 column
= gtk_tree_view_column_new_with_attributes("Max Jitter (ms)", renderer
,
911 "text", RTP_COL_MAX_JITTER
,
913 gtk_tree_view_column_set_sort_column_id(column
, RTP_COL_MAX_JITTER
);
914 gtk_tree_view_column_set_resizable(column
, TRUE
);
915 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
916 gtk_tree_view_column_set_min_width(column
, 50);
917 gtk_tree_view_column_set_fixed_width(column
, 120);
918 gtk_tree_view_append_column(list_view
, column
);
920 /* Mean Jitter (ms) */
921 renderer
= gtk_cell_renderer_text_new();
922 column
= gtk_tree_view_column_new_with_attributes("Mean Jitter (ms)", renderer
,
923 "text", RTP_COL_MEAN_JITTER
,
925 gtk_tree_view_column_set_sort_column_id(column
, RTP_COL_MEAN_JITTER
);
926 gtk_tree_view_column_set_resizable(column
, TRUE
);
927 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
928 gtk_tree_view_column_set_min_width(column
, 50);
929 gtk_tree_view_column_set_fixed_width(column
, 130);
930 gtk_tree_view_append_column(list_view
, column
);
933 renderer
= gtk_cell_renderer_text_new();
934 column
= gtk_tree_view_column_new_with_attributes("Pb?", renderer
,
935 "text", RTP_COL_PROBLEM
,
937 gtk_tree_view_column_set_sort_column_id(column
, RTP_COL_PROBLEM
);
938 gtk_tree_view_column_set_resizable(column
, TRUE
);
939 gtk_tree_view_column_set_sizing(column
, GTK_TREE_VIEW_COLUMN_FIXED
);
940 gtk_tree_view_column_set_min_width(column
, 30);
941 gtk_tree_view_column_set_fixed_width(column
, 50);
942 gtk_tree_view_append_column(list_view
, column
);
944 /* Now enable the sorting of each column */
945 gtk_tree_view_set_rules_hint(list_view
, TRUE
);
946 gtk_tree_view_set_headers_clickable(list_view
, TRUE
);
948 /* Setup the selection handler */
949 selection
= gtk_tree_view_get_selection(list_view
);
951 gtk_tree_selection_set_mode(selection
, GTK_SELECTION_MULTIPLE
);
952 gtk_tree_selection_set_select_function(selection
, rtpstream_view_selection_func
, NULL
, NULL
);
956 /****************************************************************************/
959 rtpstream_dlg_create (void)
961 GtkWidget
*rtpstream_dlg_w
;
963 GtkWidget
*scrolledwindow
;
964 GtkWidget
*hbuttonbox
;
965 /* GtkWidget *bt_goto;*/
966 GtkWidget
*bt_unselect
;
967 GtkWidget
*bt_findrev
;
970 GtkWidget
*bt_filter
;
971 GtkWidget
*bt_analyze
;
975 rtpstream_dlg_w
= dlg_window_new("Wireshark: RTP Streams");
976 gtk_window_set_default_size(GTK_WINDOW(rtpstream_dlg_w
), 620, 400);
978 main_vb
= ws_gtk_box_new(GTK_ORIENTATION_VERTICAL
, 0, FALSE
);
979 gtk_container_add(GTK_CONTAINER(rtpstream_dlg_w
), main_vb
);
980 gtk_container_set_border_width (GTK_CONTAINER (main_vb
), 12);
982 top_label
= gtk_label_new ("Detected 0 RTP streams. Choose one for forward and reverse direction for analysis");
983 gtk_box_pack_start (GTK_BOX (main_vb
), top_label
, FALSE
, FALSE
, 8);
985 scrolledwindow
= scrolled_window_new (NULL
, NULL
);
986 gtk_box_pack_start (GTK_BOX (main_vb
), scrolledwindow
, TRUE
, TRUE
, 0);
989 gtk_container_add(GTK_CONTAINER(scrolledwindow
), list
);
991 gtk_widget_show(rtpstream_dlg_w
);
993 label_fwd
= gtk_label_new (FWD_LABEL_TEXT
);
994 gtk_box_pack_start (GTK_BOX (main_vb
), label_fwd
, FALSE
, FALSE
, 0);
996 label_rev
= gtk_label_new (REV_LABEL_TEXT
);
997 gtk_box_pack_start (GTK_BOX (main_vb
), label_rev
, FALSE
, FALSE
, 0);
1000 hbuttonbox
= gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL
);
1001 gtk_box_pack_start (GTK_BOX (main_vb
), hbuttonbox
, FALSE
, FALSE
, 0);
1002 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox
), GTK_BUTTONBOX_END
);
1003 gtk_box_set_spacing (GTK_BOX (hbuttonbox
), 0);
1005 bt_unselect
= gtk_button_new_with_label ("Unselect");
1006 gtk_container_add (GTK_CONTAINER (hbuttonbox
), bt_unselect
);
1007 gtk_widget_set_tooltip_text (bt_unselect
, "Undo stream selection");
1009 bt_findrev
= gtk_button_new_with_label ("Find Reverse");
1010 gtk_container_add (GTK_CONTAINER (hbuttonbox
), bt_findrev
);
1011 gtk_widget_set_tooltip_text (bt_findrev
, "Find the reverse stream matching the selected forward stream");
1013 bt_goto = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
1014 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_goto);
1016 bt_save
= gtk_button_new_from_stock(GTK_STOCK_SAVE_AS
);
1017 gtk_container_add (GTK_CONTAINER (hbuttonbox
), bt_save
);
1018 gtk_widget_set_tooltip_text (bt_save
, "Save stream payload in rtpdump format");
1020 bt_mark
= gtk_button_new_with_label ("Mark Packets");
1021 gtk_container_add (GTK_CONTAINER (hbuttonbox
), bt_mark
);
1022 gtk_widget_set_tooltip_text (bt_mark
, "Mark packets of the selected stream(s)");
1024 bt_filter
= gtk_button_new_from_stock(WIRESHARK_STOCK_PREPARE_FILTER
);
1025 gtk_container_add (GTK_CONTAINER (hbuttonbox
), bt_filter
);
1026 gtk_widget_set_tooltip_text (bt_filter
, "Prepare a display filter of the selected stream(s)");
1028 /* XXX - maybe we want to have a "Copy as CSV" stock button here? */
1029 /*bt_copy = gtk_button_new_with_label ("Copy content to clipboard as CSV");*/
1030 bt_copy
= gtk_button_new_from_stock(GTK_STOCK_COPY
);
1031 gtk_container_add (GTK_CONTAINER (hbuttonbox
), bt_copy
);
1032 gtk_widget_set_tooltip_text(bt_copy
,
1033 "Copy all statistical values of this page to the clipboard in CSV (Comma Separated Values) format.");
1035 bt_analyze
= gtk_button_new_from_stock(WIRESHARK_STOCK_ANALYZE
);
1036 gtk_container_add (GTK_CONTAINER (hbuttonbox
), bt_analyze
);
1037 gtk_widget_set_tooltip_text (bt_analyze
, "Open an analyze window of the selected stream(s)");
1039 bt_close
= gtk_button_new_from_stock(GTK_STOCK_CLOSE
);
1040 gtk_container_add (GTK_CONTAINER (hbuttonbox
), bt_close
);
1041 gtk_widget_set_tooltip_text (bt_close
, "Close this dialog");
1042 gtk_widget_set_can_default(bt_close
, TRUE
);
1044 g_signal_connect(bt_unselect
, "clicked", G_CALLBACK(rtpstream_on_unselect
), NULL
);
1045 g_signal_connect(bt_findrev
, "clicked", G_CALLBACK(rtpstream_on_findrev
), NULL
);
1047 g_signal_connect(bt_goto, "clicked", G_CALLBACK(rtpstream_on_goto), NULL);
1049 g_signal_connect(bt_save
, "clicked", G_CALLBACK(rtpstream_on_save
), NULL
);
1050 g_signal_connect(bt_mark
, "clicked", G_CALLBACK(rtpstream_on_mark
), NULL
);
1051 g_signal_connect(bt_filter
, "clicked", G_CALLBACK(rtpstream_on_filter
), NULL
);
1052 g_signal_connect(bt_copy
, "clicked", G_CALLBACK(rtpstream_on_copy_as_csv
), NULL
);
1053 g_signal_connect(bt_analyze
, "clicked", G_CALLBACK(rtpstream_on_analyse
), NULL
);
1055 window_set_cancel_button(rtpstream_dlg_w
, bt_close
, window_cancel_button_cb
);
1057 g_signal_connect(rtpstream_dlg_w
, "delete_event", G_CALLBACK(window_delete_event_cb
), NULL
);
1058 g_signal_connect(rtpstream_dlg_w
, "destroy", G_CALLBACK(rtpstream_on_destroy
), NULL
);
1060 gtk_widget_show_all(rtpstream_dlg_w
);
1061 window_present(rtpstream_dlg_w
);
1063 rtpstream_on_unselect(NULL
, NULL
);
1065 rtp_stream_dlg
= rtpstream_dlg_w
;
1069 /****************************************************************************/
1071 /****************************************************************************/
1073 /****************************************************************************/
1074 /* update the contents of the dialog box list_store */
1075 /* list: pointer to list of rtp_stream_info_t* */
1076 void rtpstream_dlg_update(GList
*list_lcl
)
1078 if (rtp_stream_dlg
!= NULL
) {
1079 gtk_list_store_clear(list_store
);
1082 list_lcl
= g_list_first(list_lcl
);
1085 add_to_list_store((rtp_stream_info_t
*)(list_lcl
->data
));
1086 list_lcl
= g_list_next(list_lcl
);
1089 rtpstream_on_unselect(NULL
, NULL
);
1092 last_list
= list_lcl
;
1096 /****************************************************************************/
1097 /* update the contents of the dialog box list_store */
1098 /* list: pointer to list of rtp_stream_info_t* */
1099 void rtpstream_dlg_show(GList
*list_lcl
)
1101 if (rtp_stream_dlg
!= NULL
) {
1102 /* There's already a dialog box; reactivate it. */
1103 reactivate_window(rtp_stream_dlg
);
1104 /* Another list since last call? */
1105 if (list_lcl
!= last_list
) {
1106 rtpstream_dlg_update(list_lcl
);
1110 /* Create and show the dialog box */
1111 rtpstream_dlg_create();
1112 rtpstream_dlg_update(list_lcl
);
1117 /****************************************************************************/
1118 /* entry point when called via the GTK menu */
1119 void rtpstream_launch(GtkAction
*action _U_
, gpointer user_data _U_
)
1121 /* Register the tap listener */
1122 register_tap_listener_rtp_stream();
1124 /* Scan for RTP streams (redissect all packets) */
1127 /* Show the dialog box with the list of streams */
1128 rtpstream_dlg_show(rtpstream_get_info()->strinfo_list
);
1130 /* Tap listener will be removed and cleaned up in rtpstream_on_destroy */
1133 /****************************************************************************/
1135 register_tap_listener_rtp_stream_dlg(void)