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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
36 #include <epan/rtp_pt.h>
37 #include <epan/address.h>
38 #include <epan/addr_resolv.h>
39 #include <epan/strutil.h>
40 #include "epan/filesystem.h"
42 #include "../globals.h"
43 #include "../stat_menu.h"
44 #include "../simple_dialog.h"
46 #include "gtk/rtp_stream_dlg.h"
47 #include "gtk/gui_stat_menu.h"
48 #include "gtk/dlg_utils.h"
49 #include "gtk/file_dlg.h"
50 #include "gtk/gui_utils.h"
51 #include "gtk/gtkglobals.h"
52 #include "gtk/rtp_stream.h"
53 #include "gtk/rtp_analysis.h"
54 #include "gtk/stock_icons.h"
56 static const gchar FWD_LABEL_TEXT[] = "Select a forward stream with left mouse button, and then";
57 static const gchar FWD_ONLY_LABEL_TEXT[] = "Select a forward stream with Ctrl + left mouse button";
58 static const gchar REV_LABEL_TEXT[] = "Select a reverse stream with Ctrl + left mouse button";
60 /****************************************************************************/
61 /* pointer to the one and only dialog window */
62 static GtkWidget *rtp_stream_dlg = NULL;
64 /* save as dialog box */
65 static GtkWidget *rtpstream_save_dlg = NULL;
66 static GtkListStore *list_store = NULL;
67 static GtkTreeIter list_iter;
68 static GtkWidget *list = NULL;
69 static GtkWidget *top_label = NULL;
70 static GtkWidget *label_fwd = NULL;
71 static GtkWidget *label_rev = NULL;
73 static rtp_stream_info_t* selected_stream_fwd = NULL; /* current selection */
74 static rtp_stream_info_t* selected_stream_rev = NULL; /* current selection for reversed */
75 static GList *last_list = NULL;
77 static guint32 streams_nb = 0; /* number of displayed streams */
94 NUM_COLS /* The number of columns */
98 /****************************************************************************/
99 static void save_stream_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
101 /* Note that we no longer have a Save voice info dialog box. */
102 rtpstream_save_dlg = NULL;
105 /****************************************************************************/
107 static gboolean save_stream_ok_cb(GtkWidget *ok_bt _U_, gpointer fs)
111 if (!selected_stream_fwd) {
115 g_dest = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(fs));
117 /* Perhaps the user specified a directory instead of a file.
118 Check whether they did. */
119 if (test_for_directory(g_dest) == EISDIR) {
120 /* It's a directory - set the file selection box to display it. */
121 set_last_open_dir(g_dest);
123 file_selection_set_current_folder(fs, get_last_open_dir());
124 gtk_file_chooser_set_current_name(fs, "");
128 #if 0 /* GtkFileChooser/gtk_dialog_run currently being used. */
129 /* So: Leaving the dialog box displayed after popping-up an */
130 /* alert box won't work. */
132 * Don't dismiss the dialog box if the save operation fails.
134 if (!rtpstream_save(selected_stream_fwd, g_dest)) {
139 window_destroy(GTK_WIDGET(rtpstream_save_dlg));
142 /* Dialog box needs to be always destroyed. Return TRUE */
143 /* so that caller will destroy the dialog box. */
144 /* See comment under rtpstream_on_save. */
145 rtpstream_save(selected_stream_fwd, g_dest);
152 /****************************************************************************/
154 /****************************************************************************/
156 rtpstream_on_destroy(GObject *object _U_, gpointer user_data _U_)
158 /* Remove the stream tap listener */
159 remove_tap_listener_rtp_stream();
161 /* Is there a save voice window open? */
162 if (rtpstream_save_dlg != NULL)
163 window_destroy(rtpstream_save_dlg);
165 /* Clean up memory used by stream tap */
166 rtpstream_reset((rtpstream_tapinfo_t *)rtpstream_get_info());
168 /* Note that we no longer have a "RTP Streams" dialog box. */
169 rtp_stream_dlg = NULL;
173 /****************************************************************************/
175 rtpstream_on_unselect(GtkButton *button _U_, gpointer user_data _U_)
177 GtkTreeSelection *selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
178 gtk_tree_selection_unselect_all(selection);
180 selected_stream_fwd = NULL;
181 selected_stream_rev = NULL;
182 gtk_label_set_text(GTK_LABEL(label_fwd), FWD_LABEL_TEXT);
183 gtk_label_set_text(GTK_LABEL(label_rev), REV_LABEL_TEXT);
187 /****************************************************************************/
188 static gint rtp_stream_info_cmp_reverse(gconstpointer aa, gconstpointer bb)
190 const struct _rtp_stream_info* a = aa;
191 const struct _rtp_stream_info* b = bb;
193 if (a==NULL || b==NULL)
195 if ((ADDRESSES_EQUAL(&(a->src_addr), &(b->dest_addr)))
196 && (a->src_port == b->dest_port)
197 && (ADDRESSES_EQUAL(&(a->dest_addr), &(b->src_addr)))
198 && (a->dest_port == b->src_port))
204 /****************************************************************************/
206 rtpstream_on_findrev(GtkButton *button _U_, gpointer user_data _U_)
208 GtkTreeSelection *selection;
210 GList *path_list_item = NULL;
211 GtkTreePath *path = NULL;
212 GtkTreePath *path_fwd = NULL;
213 GtkTreePath *path_rev = NULL;
215 rtp_stream_info_t *stream = NULL;
216 gboolean found_it = FALSE;
218 if (selected_stream_fwd==NULL)
221 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list));
222 path_list = gtk_tree_selection_get_selected_rows(selection, NULL);
225 path_list_item = g_list_first(path_list);
226 path = (GtkTreePath *)(path_list_item->data);
229 if (path && gtk_tree_model_get_iter(GTK_TREE_MODEL(list_store), &iter, path)) {
230 gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, RTP_COL_DATA, &stream, -1);
231 if (stream == selected_stream_fwd) {
234 if (stream == selected_stream_rev) {
240 if (path_list_item) {
241 path_list_item = g_list_next(path_list_item);
243 path = (GtkTreePath *)(path_list_item->data);
246 if (path && gtk_tree_model_get_iter(GTK_TREE_MODEL(list_store), &iter, path)) {
247 gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, RTP_COL_DATA, &stream, -1);
248 if (stream == selected_stream_fwd) {
251 if (stream == selected_stream_rev) {
256 /* Find it from the forward stream on */
257 gtk_tree_model_get_iter(GTK_TREE_MODEL(list_store), &iter, path_fwd);
258 while (gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store), &iter)) {
259 gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, RTP_COL_DATA, &stream, -1);
260 if (rtp_stream_info_cmp_reverse(selected_stream_fwd, stream) == 0) {
267 /* If we're not done yet, restart at the beginning */
268 gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store), &iter);
270 gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, RTP_COL_DATA, &stream, -1);
271 if (rtp_stream_info_cmp_reverse(selected_stream_fwd, stream) == 0) {
275 if (stream == selected_stream_fwd)
277 } while (gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store), &iter));
282 gtk_tree_selection_unselect_path(selection, path_rev);
283 gtk_tree_selection_select_iter(selection, &iter);
286 g_list_foreach(path_list, (GFunc)gtk_tree_path_free, NULL);
287 g_list_free(path_list);
291 /****************************************************************************/
294 rtpstream_on_goto (GtkButton *button _U_,
295 gpointer user_data _U_)
297 if (selected_stream_fwd)
299 cf_goto_frame(&cfile, selected_stream_fwd->first_frame_num);
305 /****************************************************************************/
307 rtpstream_on_save(GtkButton *button _U_, gpointer data _U_)
310 rtpstream_tapinfo_t* tapinfo = data;
313 if (!selected_stream_fwd) {
314 simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
315 "Please select a forward stream");
319 #if 0 /* XXX: GtkFileChooserDialog/gtk_dialog_run currently being used is effectively modal so this is not req'd */
320 if (rtpstream_save_dlg != NULL) {
321 /* There's already a Save dialog box; reactivate it. */
322 reactivate_window(rtpstream_save_dlg);
327 rtpstream_save_dlg = gtk_file_chooser_dialog_new(
328 "Wireshark: Save selected stream in rtpdump ('-F dump') format",
329 GTK_WINDOW(rtp_stream_dlg), GTK_FILE_CHOOSER_ACTION_SAVE,
330 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
331 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
333 gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(rtpstream_save_dlg), TRUE);
335 g_signal_connect(rtpstream_save_dlg, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
336 g_signal_connect(rtpstream_save_dlg, "destroy", G_CALLBACK(save_stream_destroy_cb), NULL);
338 gtk_widget_show(rtpstream_save_dlg);
339 window_present(rtpstream_save_dlg);
341 if (gtk_dialog_run(GTK_DIALOG(rtpstream_save_dlg)) == GTK_RESPONSE_ACCEPT){
342 save_stream_ok_cb(rtpstream_save_dlg, rtpstream_save_dlg);
344 window_destroy(rtpstream_save_dlg);
347 /* "Run" the GtkFileChooserDialog. */
348 /* Upon exit: If "Accept" run the OK callback. */
349 /* If the OK callback returns with a FALSE status, re-run the dialog.*/
350 /* If not accept (ie: cancel) destroy the window. */
351 /* XXX: If the OK callback pops up an alert box (eg: for an error) it *must* */
352 /* return with a TRUE status so that the dialog window will be destroyed. */
353 /* Trying to re-run the dialog after popping up an alert box will not work */
354 /* since the user will not be able to dismiss the alert box. */
355 /* The (somewhat unfriendly) effect: the user must re-invoke the */
356 /* GtkFileChooserDialog whenever the OK callback pops up an alert box. */
358 /* ToDo: use GtkFileChooserWidget in a dialog window instead of */
359 /* GtkFileChooserDialog. */
360 while (gtk_dialog_run(GTK_DIALOG(rtpstream_save_dlg)) == GTK_RESPONSE_ACCEPT) {
361 if (save_stream_ok_cb(NULL, rtpstream_save_dlg)) {
362 break; /* we're done */
365 window_destroy(rtpstream_save_dlg);
369 /****************************************************************************/
371 rtpstream_on_mark(GtkButton *button _U_, gpointer user_data _U_)
373 if (selected_stream_fwd==NULL && selected_stream_rev==NULL)
375 rtpstream_mark(selected_stream_fwd, selected_stream_rev);
379 /****************************************************************************/
381 rtpstream_on_filter(GtkButton *button _U_, gpointer user_data _U_)
383 gchar *filter_string = NULL;
384 gchar *filter_string_fwd = NULL;
385 gchar *filter_string_rev = NULL;
388 if (selected_stream_fwd==NULL && selected_stream_rev==NULL)
391 if (selected_stream_fwd)
393 if (selected_stream_fwd->src_addr.type==AT_IPv6) {
394 g_strlcpy(ip_version,"v6",sizeof(ip_version));
396 ip_version[0] = '\0';
398 filter_string_fwd = g_strdup_printf(
399 "(ip%s.src==%s && udp.srcport==%u && ip%s.dst==%s && udp.dstport==%u && rtp.ssrc==0x%X)",
401 ep_address_to_str(&(selected_stream_fwd->src_addr)),
402 selected_stream_fwd->src_port,
404 ep_address_to_str(&(selected_stream_fwd->dest_addr)),
405 selected_stream_fwd->dest_port,
406 selected_stream_fwd->ssrc);
408 filter_string = filter_string_fwd;
411 if (selected_stream_rev)
413 if (selected_stream_rev->src_addr.type==AT_IPv6) {
414 g_strlcpy(ip_version,"v6",sizeof(ip_version));
416 ip_version[0] = '\0';
418 filter_string_rev = g_strdup_printf(
419 "(ip%s.src==%s && udp.srcport==%u && ip%s.dst==%s && udp.dstport==%u && rtp.ssrc==0x%X)",
421 ep_address_to_str(&(selected_stream_rev->src_addr)),
422 selected_stream_rev->src_port,
424 ep_address_to_str(&(selected_stream_rev->dest_addr)),
425 selected_stream_rev->dest_port,
426 selected_stream_rev->ssrc);
428 filter_string = filter_string_rev;
431 if ((selected_stream_fwd) && (selected_stream_rev))
433 filter_string = g_strdup_printf("%s || %s", filter_string_fwd, filter_string_rev);
434 g_free(filter_string_fwd);
435 g_free(filter_string_rev);
438 gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), filter_string);
439 g_free(filter_string);
442 main_filter_packets(&cfile, filter_string, FALSE);
443 rtpstream_dlg_update(rtpstream_get_info()->strinfo_list);
448 /****************************************************************************/
450 rtpstream_on_copy_as_csv(GtkWindow *win _U_, gpointer data _U_)
452 GtkTreeViewColumn *column;
457 guint table_entry_uint;
462 CSV_str = g_string_sized_new(240*(1+streams_nb));
463 /* Add the column headers to the CSV data */
464 for (j=0; j<NUM_COLS-1; j++) {
465 column = gtk_tree_view_get_column(GTK_TREE_VIEW(list), j);
466 title = gtk_tree_view_column_get_title(column);
467 g_string_append_printf(CSV_str, "\"%s\"", title);
468 if (j<NUM_COLS-2) g_string_append(CSV_str, ",");
470 g_string_append(CSV_str,"\n");
472 /* Add the column values to the CSV data */
473 if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store), &iter)) {
474 for (i=0; i<streams_nb; i++) {
475 for (j=0; j<NUM_COLS-1; j++) {
476 if (j == RTP_COL_SRC_PORT || j == RTP_COL_DST_PORT || j == RTP_COL_PACKETS) {
477 gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, j, &table_entry_uint, -1);
478 g_string_append_printf(CSV_str, "\"%u\"", table_entry_uint);
480 gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, j, &table_entry, -1);
481 g_string_append_printf(CSV_str, "\"%s\"", table_entry);
484 if (j<NUM_COLS-2) g_string_append(CSV_str,",");
486 g_string_append(CSV_str,"\n");
487 gtk_tree_model_iter_next (GTK_TREE_MODEL(list_store),&iter);
491 /* Now that we have the CSV data, copy it into the default clipboard */
492 cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
493 gtk_clipboard_set_text(cb, CSV_str->str, (gint)CSV_str->len);
494 g_string_free(CSV_str, TRUE);
497 /****************************************************************************/
499 rtpstream_on_analyse(GtkButton *button _U_, gpointer user_data _U_)
502 guint16 port_src_fwd = 0;
504 guint16 port_dst_fwd = 0;
505 guint32 ssrc_fwd = 0;
507 guint16 port_src_rev = 0;
509 guint16 port_dst_rev = 0;
510 guint32 ssrc_rev = 0;
512 if (!(selected_stream_fwd || selected_stream_rev))
517 SET_ADDRESS(&ip_src_fwd,AT_NONE,0,NULL);
518 SET_ADDRESS(&ip_dst_fwd,AT_NONE,0,NULL);
519 SET_ADDRESS(&ip_src_rev,AT_NONE,0,NULL);
520 SET_ADDRESS(&ip_dst_rev,AT_NONE,0,NULL);
522 if (selected_stream_fwd) {
523 COPY_ADDRESS(&(ip_src_fwd), &(selected_stream_fwd->src_addr));
524 port_src_fwd = selected_stream_fwd->src_port;
525 COPY_ADDRESS(&(ip_dst_fwd), &(selected_stream_fwd->dest_addr));
526 port_dst_fwd = selected_stream_fwd->dest_port;
527 ssrc_fwd = selected_stream_fwd->ssrc;
530 if (selected_stream_rev) {
531 COPY_ADDRESS(&(ip_src_rev), &(selected_stream_rev->src_addr));
532 port_src_rev = selected_stream_rev->src_port;
533 COPY_ADDRESS(&(ip_dst_rev), &(selected_stream_rev->dest_addr));
534 port_dst_rev = selected_stream_rev->dest_port;
535 ssrc_rev = selected_stream_rev->ssrc;
554 /****************************************************************************/
555 /* when the user selects a row in the stream list */
557 rtpstream_view_selection_func(GtkTreeSelection *selection, GtkTreeModel *model, GtkTreePath *path, gboolean path_currently_selected, gpointer userdata _U_)
561 rtp_stream_info_t* selected_stream;
562 gboolean result = TRUE;
563 gchar label_text[80];
566 * nb_selected path_currently_selected forward reverse action result
567 * 0 must be false any any assign forward true
568 * 1 true match any delete forward true
569 * 1 true other any delete reverse true
570 * 1 false match any invalid true
571 * 1 false other none assign reverse true
572 * 1 false other any assign forward true
573 * 2 true match any delete forward path_currently_selected
574 * 2 true other match delete reverse path_currently_selected
575 * 2 true other other invalid path_currently_selected
576 * 2 false match any invalid path_currently_selected
577 * 2 false any match invalid path_currently_selected
578 * 2 false other other assign reverse path_currently_selected
579 * >2 any any any invalid path_currently_selected
582 nb_selected = gtk_tree_selection_count_selected_rows(selection);
583 if (gtk_tree_model_get_iter(model, &iter, path)) {
584 gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, RTP_COL_DATA, &selected_stream, -1);
590 if (path_currently_selected)
591 g_print("Select: He, we've got a selected path while none is selected?\n");
593 selected_stream_fwd = selected_stream;
598 if (path_currently_selected)
599 if (selected_stream == selected_stream_fwd)
600 selected_stream_fwd = NULL;
602 selected_stream_rev = NULL;
604 if (selected_stream == selected_stream_fwd)
605 g_print("Select: He, this can't be. 1 not selected but equal to fwd\n");
607 if (selected_stream_rev)
608 selected_stream_fwd = selected_stream;
610 selected_stream_rev = selected_stream;
615 if (path_currently_selected) {
616 if (selected_stream == selected_stream_fwd)
617 selected_stream_fwd = NULL;
618 else if (selected_stream == selected_stream_rev)
619 selected_stream_rev = NULL;
621 g_print("Select: He, this can't be. 2 selected but not equal to fwd or rev\n");
623 result = path_currently_selected;
628 g_print("Select: He, we're getting a too high selection count\n");
629 result = path_currently_selected;
634 if (selected_stream_fwd) {
635 g_snprintf(label_text, sizeof(label_text), "Forward: %s:%u -> %s:%u, SSRC=0x%X",
636 get_addr_name(&(selected_stream_fwd->src_addr)),
637 selected_stream_fwd->src_port,
638 get_addr_name(&(selected_stream_fwd->dest_addr)),
639 selected_stream_fwd->dest_port,
640 selected_stream_fwd->ssrc
642 gtk_label_set_text(GTK_LABEL(label_fwd), label_text);
644 if (selected_stream_rev)
645 gtk_label_set_text(GTK_LABEL(label_fwd), FWD_ONLY_LABEL_TEXT);
647 gtk_label_set_text(GTK_LABEL(label_fwd), FWD_LABEL_TEXT);
650 if (selected_stream_rev) {
651 g_snprintf(label_text, sizeof(label_text), "Reverse: %s:%u -> %s:%u, SSRC=0x%X",
652 get_addr_name(&(selected_stream_rev->src_addr)),
653 selected_stream_rev->src_port,
654 get_addr_name(&(selected_stream_rev->dest_addr)),
655 selected_stream_rev->dest_port,
656 selected_stream_rev->ssrc
658 gtk_label_set_text(GTK_LABEL(label_rev), label_text);
660 gtk_label_set_text(GTK_LABEL(label_rev), REV_LABEL_TEXT);
666 /****************************************************************************/
668 /****************************************************************************/
669 /* append a line to list */
671 add_to_list_store(rtp_stream_info_t* strinfo)
673 gchar label_text[256];
674 gchar *data[NUM_COLS];
681 /* save the current locale */
682 savelocale = setlocale(LC_NUMERIC, NULL);
683 /* switch to "C" locale to avoid problems with localized decimal separators
684 in g_snprintf("%f") functions */
685 setlocale(LC_NUMERIC, "C");
687 data[0] = g_strdup(get_addr_name(&(strinfo->src_addr)));
689 data[2] = g_strdup(get_addr_name(&(strinfo->dest_addr)));
691 data[4] = g_strdup_printf("0x%X", strinfo->ssrc);
692 if ((strinfo->pt > 95) && (strinfo->info_payload_type_str != NULL)) {
693 data[5] = g_strdup(strinfo->info_payload_type_str);
695 data[5] = g_strdup(val_to_str_ext(strinfo->pt, &rtp_payload_type_short_vals_ext,
700 expected = (strinfo->rtp_stats.stop_seq_nr + strinfo->rtp_stats.cycles*65536)
701 - strinfo->rtp_stats.start_seq_nr + 1;
702 lost = expected - strinfo->rtp_stats.total_nr;
704 perc = (double)(lost*100)/(double)expected;
708 data[7] = g_strdup_printf("%d (%.1f%%)", lost, perc);
709 data[8] = g_strdup_printf("%.2f", strinfo->rtp_stats.max_delta);
710 data[9] = g_strdup_printf("%.2f", strinfo->rtp_stats.max_jitter);
711 data[10] = g_strdup_printf("%.2f", strinfo->rtp_stats.mean_jitter);
712 if (strinfo->problem)
713 data[11] = g_strdup("X");
715 data[11] = g_strdup("");
717 /* restore previous locale setting */
718 setlocale(LC_NUMERIC, savelocale);
720 /* Acquire an iterator */
721 gtk_list_store_append(list_store, &list_iter);
723 /* Fill the new row */
724 gtk_list_store_set(list_store, &list_iter,
725 RTP_COL_SRC_ADDR, data[0],
726 RTP_COL_SRC_PORT, strinfo->src_port,
727 RTP_COL_DST_ADDR, data[2],
728 RTP_COL_DST_PORT, strinfo->dest_port,
729 RTP_COL_SSRC, data[4],
730 RTP_COL_PAYLOAD, data[5],
731 RTP_COL_PACKETS, strinfo->npackets,
732 RTP_COL_LOST, data[7],
733 RTP_COL_MAX_DELTA, data[8],
734 RTP_COL_MAX_JITTER, data[9],
735 RTP_COL_MEAN_JITTER, data[10],
736 RTP_COL_PROBLEM, data[11],
737 RTP_COL_DATA, strinfo,
740 for (i = 0; i < NUM_COLS-1; i++)
743 /* Update the top label with the number of detected streams */
744 g_snprintf(label_text, sizeof(label_text),
745 "Detected %d RTP streams. Choose one for forward and reverse direction for analysis",
747 gtk_label_set_text(GTK_LABEL(top_label), label_text);
750 /****************************************************************************/
751 /* Create list view */
753 create_list_view(void)
755 GtkTreeViewColumn *column;
756 GtkCellRenderer *renderer;
757 GtkTreeSortable *sortable;
758 GtkTreeView *list_view;
759 GtkTreeSelection *selection;
761 /* Create the store */
762 list_store = gtk_list_store_new(NUM_COLS, /* Total number of columns */
763 G_TYPE_STRING, /* Source address */
764 G_TYPE_UINT, /* Source port */
765 G_TYPE_STRING, /* Destination address */
766 G_TYPE_UINT, /* Destination port */
767 G_TYPE_STRING, /* SSRC */
768 G_TYPE_STRING, /* Payload */
769 G_TYPE_UINT, /* Packets */
770 G_TYPE_STRING, /* Lost */
771 G_TYPE_STRING, /* Max. delta */
772 G_TYPE_STRING, /* Max. jitter */
773 G_TYPE_STRING, /* Mean jitter */
774 G_TYPE_STRING, /* Problem */
775 G_TYPE_POINTER /* Data */
779 list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));
781 list_view = GTK_TREE_VIEW(list);
782 sortable = GTK_TREE_SORTABLE(list_store);
784 /* Speed up the list display */
785 gtk_tree_view_set_fixed_height_mode(list_view, TRUE);
787 /* Setup the sortable columns */
788 gtk_tree_sortable_set_sort_column_id(sortable, RTP_COL_SRC_ADDR, GTK_SORT_ASCENDING);
789 gtk_tree_view_set_headers_clickable(list_view, FALSE);
791 /* The view now holds a reference. We can get rid of our own reference */
792 g_object_unref(G_OBJECT(list_store));
795 * Create the first column packet, associating the "text" attribute of the
796 * cell_renderer to the first column of the model
798 renderer = gtk_cell_renderer_text_new();
799 column = gtk_tree_view_column_new_with_attributes("Src IP addr", renderer,
800 "text", RTP_COL_SRC_ADDR,
802 gtk_tree_view_column_set_sort_column_id(column, RTP_COL_SRC_ADDR);
803 gtk_tree_view_column_set_resizable(column, TRUE);
804 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
805 gtk_tree_view_column_set_min_width(column, 60);
806 gtk_tree_view_column_set_fixed_width(column, 100);
807 /* Add the column to the view. */
808 gtk_tree_view_append_column(list_view, column);
811 renderer = gtk_cell_renderer_text_new();
812 column = gtk_tree_view_column_new_with_attributes("Src port", renderer,
813 "text", RTP_COL_SRC_PORT,
815 gtk_tree_view_column_set_sort_column_id(column, RTP_COL_SRC_PORT);
816 gtk_tree_view_column_set_resizable(column, TRUE);
817 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
818 gtk_tree_view_column_set_min_width(column, 60);
819 gtk_tree_view_column_set_fixed_width(column, 80);
820 gtk_tree_view_append_column(list_view, column);
822 /* Destination address */
823 renderer = gtk_cell_renderer_text_new();
824 column = gtk_tree_view_column_new_with_attributes("Dst IP addr", renderer,
825 "text", RTP_COL_DST_ADDR,
827 gtk_tree_view_column_set_sort_column_id(column, RTP_COL_DST_ADDR);
828 gtk_tree_view_column_set_resizable(column, TRUE);
829 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
830 gtk_tree_view_column_set_min_width(column, 60);
831 gtk_tree_view_column_set_fixed_width(column, 100);
832 gtk_tree_view_append_column(list_view, column);
834 /* Destination port */
835 renderer = gtk_cell_renderer_text_new();
836 column = gtk_tree_view_column_new_with_attributes("Dst port", renderer,
837 "text", RTP_COL_DST_PORT,
839 gtk_tree_view_column_set_sort_column_id(column, RTP_COL_DST_PORT);
840 gtk_tree_view_column_set_resizable(column, TRUE);
841 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
842 gtk_tree_view_column_set_min_width(column, 60);
843 gtk_tree_view_column_set_fixed_width(column, 80);
844 gtk_tree_view_append_column(list_view, column);
847 renderer = gtk_cell_renderer_text_new();
848 column = gtk_tree_view_column_new_with_attributes("SSRC", renderer,
849 "text", RTP_COL_SSRC,
851 gtk_tree_view_column_set_sort_column_id(column, RTP_COL_SSRC);
852 gtk_tree_view_column_set_resizable(column, TRUE);
853 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
854 gtk_tree_view_column_set_min_width(column, 70);
855 gtk_tree_view_column_set_fixed_width(column, 90);
856 gtk_tree_view_append_column(list_view, column);
859 renderer = gtk_cell_renderer_text_new();
860 column = gtk_tree_view_column_new_with_attributes("Payload", renderer,
861 "text", RTP_COL_PAYLOAD,
863 gtk_tree_view_column_set_sort_column_id(column, RTP_COL_PAYLOAD);
864 gtk_tree_view_column_set_resizable(column, TRUE);
865 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
866 gtk_tree_view_column_set_min_width(column, 80);
867 gtk_tree_view_column_set_fixed_width(column, 100);
868 gtk_tree_view_append_column(list_view, column);
871 renderer = gtk_cell_renderer_text_new();
872 column = gtk_tree_view_column_new_with_attributes("Packets", renderer,
873 "text", RTP_COL_PACKETS,
875 gtk_tree_view_column_set_sort_column_id(column, RTP_COL_PACKETS);
876 gtk_tree_view_column_set_resizable(column, TRUE);
877 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
878 gtk_tree_view_column_set_min_width(column, 60);
879 gtk_tree_view_column_set_fixed_width(column, 70);
880 gtk_tree_view_append_column(list_view, column);
883 renderer = gtk_cell_renderer_text_new();
884 column = gtk_tree_view_column_new_with_attributes("Lost", renderer,
885 "text", RTP_COL_LOST,
887 gtk_tree_view_column_set_sort_column_id(column, RTP_COL_LOST);
888 gtk_tree_view_column_set_resizable(column, TRUE);
889 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
890 gtk_tree_view_column_set_min_width(column, 60);
891 gtk_tree_view_column_set_fixed_width(column, 90);
892 gtk_tree_view_append_column(list_view, column);
895 renderer = gtk_cell_renderer_text_new();
896 column = gtk_tree_view_column_new_with_attributes("Max Delta (ms)", renderer,
897 "text", RTP_COL_MAX_DELTA,
899 gtk_tree_view_column_set_sort_column_id(column, RTP_COL_MAX_DELTA);
900 gtk_tree_view_column_set_resizable(column, TRUE);
901 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
902 gtk_tree_view_column_set_min_width(column, 90);
903 gtk_tree_view_column_set_fixed_width(column, 130);
904 gtk_tree_view_append_column(list_view, column);
906 /* Max Jitter (ms) */
907 renderer = gtk_cell_renderer_text_new();
908 column = gtk_tree_view_column_new_with_attributes("Max Jitter (ms)", renderer,
909 "text", RTP_COL_MAX_JITTER,
911 gtk_tree_view_column_set_sort_column_id(column, RTP_COL_MAX_JITTER);
912 gtk_tree_view_column_set_resizable(column, TRUE);
913 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
914 gtk_tree_view_column_set_min_width(column, 50);
915 gtk_tree_view_column_set_fixed_width(column, 120);
916 gtk_tree_view_append_column(list_view, column);
918 /* Mean Jitter (ms) */
919 renderer = gtk_cell_renderer_text_new();
920 column = gtk_tree_view_column_new_with_attributes("Mean Jitter (ms)", renderer,
921 "text", RTP_COL_MEAN_JITTER,
923 gtk_tree_view_column_set_sort_column_id(column, RTP_COL_MEAN_JITTER);
924 gtk_tree_view_column_set_resizable(column, TRUE);
925 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
926 gtk_tree_view_column_set_min_width(column, 50);
927 gtk_tree_view_column_set_fixed_width(column, 130);
928 gtk_tree_view_append_column(list_view, column);
931 renderer = gtk_cell_renderer_text_new();
932 column = gtk_tree_view_column_new_with_attributes("Pb?", renderer,
933 "text", RTP_COL_PROBLEM,
935 gtk_tree_view_column_set_sort_column_id(column, RTP_COL_PROBLEM);
936 gtk_tree_view_column_set_resizable(column, TRUE);
937 gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
938 gtk_tree_view_column_set_min_width(column, 30);
939 gtk_tree_view_column_set_fixed_width(column, 50);
940 gtk_tree_view_append_column(list_view, column);
942 /* Now enable the sorting of each column */
943 gtk_tree_view_set_rules_hint(list_view, TRUE);
944 gtk_tree_view_set_headers_clickable(list_view, TRUE);
946 /* Setup the selection handler */
947 selection = gtk_tree_view_get_selection(list_view);
949 gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
950 gtk_tree_selection_set_select_function(selection, rtpstream_view_selection_func, NULL, NULL);
954 /****************************************************************************/
957 rtpstream_dlg_create (void)
959 GtkWidget *rtpstream_dlg_w;
961 GtkWidget *scrolledwindow;
962 GtkWidget *hbuttonbox;
963 /* GtkWidget *bt_goto;*/
964 GtkWidget *bt_unselect;
965 GtkWidget *bt_findrev;
968 GtkWidget *bt_filter;
969 GtkWidget *bt_analyze;
973 rtpstream_dlg_w = dlg_window_new("Wireshark: RTP Streams");
974 gtk_window_set_default_size(GTK_WINDOW(rtpstream_dlg_w), 620, 400);
976 main_vb = gtk_vbox_new (FALSE, 0);
977 gtk_container_add(GTK_CONTAINER(rtpstream_dlg_w), main_vb);
978 gtk_container_set_border_width (GTK_CONTAINER (main_vb), 12);
980 top_label = gtk_label_new ("Detected 0 RTP streams. Choose one for forward and reverse direction for analysis");
981 gtk_box_pack_start (GTK_BOX (main_vb), top_label, FALSE, FALSE, 8);
983 scrolledwindow = scrolled_window_new (NULL, NULL);
984 gtk_box_pack_start (GTK_BOX (main_vb), scrolledwindow, TRUE, TRUE, 0);
987 gtk_container_add(GTK_CONTAINER(scrolledwindow), list);
989 gtk_widget_show(rtpstream_dlg_w);
991 label_fwd = gtk_label_new (FWD_LABEL_TEXT);
992 gtk_box_pack_start (GTK_BOX (main_vb), label_fwd, FALSE, FALSE, 0);
994 label_rev = gtk_label_new (REV_LABEL_TEXT);
995 gtk_box_pack_start (GTK_BOX (main_vb), label_rev, FALSE, FALSE, 0);
998 hbuttonbox = gtk_hbutton_box_new ();
999 gtk_box_pack_start (GTK_BOX (main_vb), hbuttonbox, FALSE, FALSE, 0);
1000 gtk_button_box_set_layout (GTK_BUTTON_BOX (hbuttonbox), GTK_BUTTONBOX_END);
1001 gtk_box_set_spacing (GTK_BOX (hbuttonbox), 0);
1003 bt_unselect = gtk_button_new_with_label ("Unselect");
1004 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_unselect);
1005 gtk_widget_set_tooltip_text (bt_unselect, "Undo stream selection");
1007 bt_findrev = gtk_button_new_with_label ("Find Reverse");
1008 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_findrev);
1009 gtk_widget_set_tooltip_text (bt_findrev, "Find the reverse stream matching the selected forward stream");
1011 bt_goto = gtk_button_new_from_stock(GTK_STOCK_JUMP_TO);
1012 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_goto);
1014 bt_save = gtk_button_new_from_stock(GTK_STOCK_SAVE_AS);
1015 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_save);
1016 gtk_widget_set_tooltip_text (bt_save, "Save stream payload in rtpdump format");
1018 bt_mark = gtk_button_new_with_label ("Mark Packets");
1019 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_mark);
1020 gtk_widget_set_tooltip_text (bt_mark, "Mark packets of the selected stream(s)");
1022 bt_filter = gtk_button_new_from_stock(WIRESHARK_STOCK_PREPARE_FILTER);
1023 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_filter);
1024 gtk_widget_set_tooltip_text (bt_filter, "Prepare a display filter of the selected stream(s)");
1026 /* XXX - maybe we want to have a "Copy as CSV" stock button here? */
1027 /*bt_copy = gtk_button_new_with_label ("Copy content to clipboard as CSV");*/
1028 bt_copy = gtk_button_new_from_stock(GTK_STOCK_COPY);
1029 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_copy);
1030 gtk_widget_set_tooltip_text(bt_copy,
1031 "Copy all statistical values of this page to the clipboard in CSV (Comma Separated Values) format.");
1033 bt_analyze = gtk_button_new_from_stock(WIRESHARK_STOCK_ANALYZE);
1034 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_analyze);
1035 gtk_widget_set_tooltip_text (bt_analyze, "Open an analyze window of the selected stream(s)");
1037 bt_close = gtk_button_new_from_stock(GTK_STOCK_CLOSE);
1038 gtk_container_add (GTK_CONTAINER (hbuttonbox), bt_close);
1039 gtk_widget_set_tooltip_text (bt_close, "Close this dialog");
1040 #if GTK_CHECK_VERSION(2,18,0)
1041 gtk_widget_set_can_default(bt_close, TRUE);
1043 GTK_WIDGET_SET_FLAGS(bt_close, GTK_CAN_DEFAULT);
1046 g_signal_connect(bt_unselect, "clicked", G_CALLBACK(rtpstream_on_unselect), NULL);
1047 g_signal_connect(bt_findrev, "clicked", G_CALLBACK(rtpstream_on_findrev), NULL);
1049 g_signal_connect(bt_goto, "clicked", G_CALLBACK(rtpstream_on_goto), NULL);
1051 g_signal_connect(bt_save, "clicked", G_CALLBACK(rtpstream_on_save), NULL);
1052 g_signal_connect(bt_mark, "clicked", G_CALLBACK(rtpstream_on_mark), NULL);
1053 g_signal_connect(bt_filter, "clicked", G_CALLBACK(rtpstream_on_filter), NULL);
1054 g_signal_connect(bt_copy, "clicked", G_CALLBACK(rtpstream_on_copy_as_csv), NULL);
1055 g_signal_connect(bt_analyze, "clicked", G_CALLBACK(rtpstream_on_analyse), NULL);
1057 window_set_cancel_button(rtpstream_dlg_w, bt_close, window_cancel_button_cb);
1059 g_signal_connect(rtpstream_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1060 g_signal_connect(rtpstream_dlg_w, "destroy", G_CALLBACK(rtpstream_on_destroy), NULL);
1062 gtk_widget_show_all(rtpstream_dlg_w);
1063 window_present(rtpstream_dlg_w);
1065 rtpstream_on_unselect(NULL, NULL);
1067 rtp_stream_dlg = rtpstream_dlg_w;
1071 /****************************************************************************/
1073 /****************************************************************************/
1075 /****************************************************************************/
1076 /* update the contents of the dialog box list_store */
1077 /* list: pointer to list of rtp_stream_info_t* */
1078 void rtpstream_dlg_update(GList *list_lcl)
1080 if (rtp_stream_dlg != NULL) {
1081 gtk_list_store_clear(list_store);
1084 list_lcl = g_list_first(list_lcl);
1087 add_to_list_store((rtp_stream_info_t*)(list_lcl->data));
1088 list_lcl = g_list_next(list_lcl);
1091 rtpstream_on_unselect(NULL, NULL);
1094 last_list = list_lcl;
1098 /****************************************************************************/
1099 /* update the contents of the dialog box list_store */
1100 /* list: pointer to list of rtp_stream_info_t* */
1101 void rtpstream_dlg_show(GList *list_lcl)
1103 if (rtp_stream_dlg != NULL) {
1104 /* There's already a dialog box; reactivate it. */
1105 reactivate_window(rtp_stream_dlg);
1106 /* Another list since last call? */
1107 if (list_lcl != last_list) {
1108 rtpstream_dlg_update(list_lcl);
1112 /* Create and show the dialog box */
1113 rtpstream_dlg_create();
1114 rtpstream_dlg_update(list_lcl);
1119 /****************************************************************************/
1120 /* entry point when called via the GTK menu */
1121 void rtpstream_launch(GtkAction *action _U_, gpointer user_data _U_)
1123 /* Register the tap listener */
1124 register_tap_listener_rtp_stream();
1126 /* Scan for RTP streams (redissect all packets) */
1129 /* Show the dialog box with the list of streams */
1130 rtpstream_dlg_show(rtpstream_get_info()->strinfo_list);
1132 /* Tap listener will be removed and cleaned up in rtpstream_on_destroy */
1135 /****************************************************************************/
1137 register_tap_listener_rtp_stream_dlg(void)