aboutsummaryrefslogtreecommitdiffstats
path: root/ui
diff options
context:
space:
mode:
authorGerald Combs <gerald@wireshark.org>2015-01-14 17:25:56 -0800
committerGerald Combs <gerald@wireshark.org>2015-01-30 06:48:32 +0000
commit2bf7878e8a7455fe656bb07e9a7d42e6ac4d87fd (patch)
tree3a0c99831311c43017d1d9b3336856e4a956c353 /ui
parent6824cee6c4b5f7c00b9dc4e9013aaa936b18b739 (diff)
Qt: Add the RTP Streams dialog.
Add keyboard shortcuts. Note that not all of the buttons made it from GTK+. Add a "Go to setup frame" option. Move rtp_streams.c from ui/gtk to ui. Add a help URL for RTP analysis (which needs to be split into streams + analysis). Fix RTP stream packet marking. Change-Id: Ifb8192ff701a933422509233d76461a46e459f4f Reviewed-on: https://code.wireshark.org/review/6852 Petri-Dish: Gerald Combs <gerald@wireshark.org> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Gerald Combs <gerald@wireshark.org>
Diffstat (limited to 'ui')
-rw-r--r--ui/CMakeLists.txt7
-rw-r--r--ui/Makefile.common1
-rw-r--r--ui/cli/tap-rtp.c4
-rw-r--r--ui/gtk/CMakeLists.txt1
-rw-r--r--ui/gtk/Makefile.common1
-rw-r--r--ui/gtk/iax2_analysis.c4
-rw-r--r--ui/gtk/rtp_analysis.c4
-rw-r--r--ui/gtk/rtp_stream_dlg.c84
-rw-r--r--ui/gtk/rtp_stream_dlg.h10
-rw-r--r--ui/help_url.c3
-rw-r--r--ui/help_url.h3
-rw-r--r--ui/qt/CMakeLists.txt5
-rw-r--r--ui/qt/Makefile.am2
-rw-r--r--ui/qt/Makefile.common4
-rw-r--r--ui/qt/Wireshark.pro3
-rw-r--r--ui/qt/main_window.h1
-rw-r--r--ui/qt/main_window.ui12
-rw-r--r--ui/qt/main_window_slots.cpp13
-rw-r--r--ui/qt/packet_list.cpp1
-rw-r--r--ui/qt/rtp_stream_dialog.cpp636
-rw-r--r--ui/qt/rtp_stream_dialog.h105
-rw-r--r--ui/qt/rtp_stream_dialog.ui246
-rw-r--r--ui/qt/syntax_line_edit.cpp6
-rw-r--r--ui/qt/traffic_table_dialog.cpp3
-rw-r--r--ui/qt/wireshark_dialog.h1
-rw-r--r--ui/rtp_stream.c (renamed from ui/gtk/rtp_stream.c)147
-rw-r--r--ui/rtp_stream.h52
-rw-r--r--ui/tap-rtp-common.c12
-rw-r--r--ui/tap-rtp-common.h1
-rw-r--r--ui/utf8_entities.h1
30 files changed, 1236 insertions, 137 deletions
diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt
index 35efca9977..214d54f8a2 100644
--- a/ui/CMakeLists.txt
+++ b/ui/CMakeLists.txt
@@ -43,6 +43,7 @@ set(COMMON_UI_SRC
profile.c
proto_hier_stats.c
recent.c
+ rtp_stream.c
software_update.c
ssl_key_export.c
tap_export_pdu.c
@@ -58,6 +59,12 @@ set(COMMON_UI_SRC
voip_calls.c
)
+# Enables visibility in IDEs
+file(GLOB EXTRA_UI_HEADERS
+ rtp_analysis.h
+ rtp_stream.h
+)
+
set(DIRTY_UI_SRC)
add_lex_files(DIRTY_UI_SRC
diff --git a/ui/Makefile.common b/ui/Makefile.common
index af77a1db0d..843ddd875f 100644
--- a/ui/Makefile.common
+++ b/ui/Makefile.common
@@ -64,6 +64,7 @@ WIRESHARK_UI_SRC = \
profile.c \
proto_hier_stats.c \
recent.c \
+ rtp_stream.c \
software_update.c \
ssl_key_export.c \
tap_export_pdu.c \
diff --git a/ui/cli/tap-rtp.c b/ui/cli/tap-rtp.c
index 76d15877ea..135750f523 100644
--- a/ui/cli/tap-rtp.c
+++ b/ui/cli/tap-rtp.c
@@ -51,13 +51,11 @@ void register_tap_listener_rtp_streams(void);
/* The one and only global rtpstream_tapinfo_t structure for tshark and wireshark.
*/
static rtpstream_tapinfo_t the_tapinfo_struct =
- {0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, 0, FALSE};
+ {NULL, NULL, NULL, 0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, 0, FALSE};
static void
rtp_streams_stat_draw(void *arg _U_)
{
-
-
GList *list;
rtp_stream_info_t *strinfo;
gchar *payload_type;
diff --git a/ui/gtk/CMakeLists.txt b/ui/gtk/CMakeLists.txt
index d3acfb8e69..ea3b6ae4dd 100644
--- a/ui/gtk/CMakeLists.txt
+++ b/ui/gtk/CMakeLists.txt
@@ -99,7 +99,6 @@ set(WIRESHARK_GTK_SRC
proto_hier_tree_model.c
proto_tree_model.c
range_utils.c
- rtp_stream.c
sctp_byte_graph_dlg.c
sctp_error_dlg.c
sctp_graph_dlg.c
diff --git a/ui/gtk/Makefile.common b/ui/gtk/Makefile.common
index 5b58c68878..5de6296d16 100644
--- a/ui/gtk/Makefile.common
+++ b/ui/gtk/Makefile.common
@@ -120,7 +120,6 @@ WIRESHARK_GTK_SRC = \
proto_tree_model.c \
range_utils.c \
rtp_player.c \
- rtp_stream.c \
sctp_byte_graph_dlg.c \
sctp_error_dlg.c \
sctp_graph_dlg.c \
diff --git a/ui/gtk/iax2_analysis.c b/ui/gtk/iax2_analysis.c
index 7c18c755d5..e78b67fafa 100644
--- a/ui/gtk/iax2_analysis.c
+++ b/ui/gtk/iax2_analysis.c
@@ -3771,10 +3771,10 @@ void iax2_analysis_cb(GtkAction *action _U_, gpointer user_data _U_)
port_dst_rev = edt.pi.srcport;
/* Scan for rtpstream */
- rtpstream_scan();
+ rtpstream_scan(rtpstream_dlg_get_tapinfo(), &cfile);
/* search for reversed direction in the global rtp streams list */
nfound = 0;
- strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
+ strinfo_list = g_list_first(rtpstream_dlg_get_tapinfo()->strinfo_list);
while (strinfo_list)
{
strinfo = (rtp_stream_info_t*)(strinfo_list->data);
diff --git a/ui/gtk/rtp_analysis.c b/ui/gtk/rtp_analysis.c
index 6feac4a2e0..f4f48dcf38 100644
--- a/ui/gtk/rtp_analysis.c
+++ b/ui/gtk/rtp_analysis.c
@@ -3999,10 +3999,10 @@ rtp_analysis_cb(GtkAction *action _U_, gpointer user_data _U_)
}
/* Scan for rtpstream */
- rtpstream_scan();
+ rtpstream_scan(rtpstream_dlg_get_tapinfo(), &cfile);
/* search for reversed direction in the global rtp streams list */
nfound = 0;
- strinfo_list = g_list_first(rtpstream_get_info()->strinfo_list);
+ strinfo_list = g_list_first(rtpstream_dlg_get_tapinfo()->strinfo_list);
while (strinfo_list)
{
strinfo = (rtp_stream_info_t*)(strinfo_list->data);
diff --git a/ui/gtk/rtp_stream_dlg.c b/ui/gtk/rtp_stream_dlg.c
index 58c8b70ab8..89a8b9dee2 100644
--- a/ui/gtk/rtp_stream_dlg.c
+++ b/ui/gtk/rtp_stream_dlg.c
@@ -44,7 +44,6 @@
#include "ui/gtk/file_dlg.h"
#include "ui/gtk/gui_utils.h"
#include "ui/gtk/gtkglobals.h"
-#include "ui/rtp_stream.h"
#include "ui/gtk/stock_icons.h"
#include "ui/gtk/gui_stat_menu.h"
@@ -52,8 +51,17 @@ static const gchar FWD_LABEL_TEXT[] = "Select a forward stream with left mouse b
static const gchar FWD_ONLY_LABEL_TEXT[] = "Select a forward stream with Ctrl + left mouse button";
static const gchar REV_LABEL_TEXT[] = "Select a reverse stream with Ctrl + left mouse button";
+static void rtpstream_dlg_update(void *ti_ptr);
+static void rtpstream_dlg_mark_packet(rtpstream_tapinfo_t *tapinfo, frame_data *fd);
void register_tap_listener_rtp_stream_dlg(void);
+/* The one and only global rtpstream_tapinfo_t structure for tshark and wireshark.
+ */
+static rtpstream_tapinfo_t the_tapinfo_struct =
+ { rtpstream_dlg_update, rtpstream_dlg_mark_packet, NULL, 0, NULL, 0,
+ TAP_ANALYSE, NULL, NULL, NULL, 0, FALSE
+ };
+
/****************************************************************************/
/* pointer to the one and only dialog window */
static GtkWidget *rtp_stream_dlg = NULL;
@@ -91,7 +99,6 @@ enum
NUM_COLS /* The number of columns */
};
-
/****************************************************************************/
static void save_stream_destroy_cb(GtkWidget *win _U_, gpointer user_data _U_)
{
@@ -128,7 +135,7 @@ static gboolean save_stream_ok_cb(GtkWidget *ok_bt _U_, gpointer fs)
/*
* Don't dismiss the dialog box if the save operation fails.
*/
- if (!rtpstream_save(selected_stream_fwd, g_dest)) {
+ if (!rtpstream_save(&the_tapinfo_struct, &cfile, selected_stream_fwd, g_dest)) {
g_free(g_dest);
return;
}
@@ -139,7 +146,7 @@ static gboolean save_stream_ok_cb(GtkWidget *ok_bt _U_, gpointer fs)
/* Dialog box needs to be always destroyed. Return TRUE */
/* so that caller will destroy the dialog box. */
/* See comment under rtpstream_on_save. */
- rtpstream_save(selected_stream_fwd, g_dest);
+ rtpstream_save(&the_tapinfo_struct, &cfile, selected_stream_fwd, g_dest);
g_free(g_dest);
return TRUE;
#endif
@@ -153,14 +160,14 @@ static void
rtpstream_on_destroy(GObject *object _U_, gpointer user_data _U_)
{
/* Remove the stream tap listener */
- remove_tap_listener_rtp_stream();
+ remove_tap_listener_rtp_stream(&the_tapinfo_struct);
/* Is there a save voice window open? */
if (rtpstream_save_dlg != NULL)
window_destroy(rtpstream_save_dlg);
/* Clean up memory used by stream tap */
- rtpstream_reset((rtpstream_tapinfo_t *)rtpstream_get_info());
+ rtpstream_reset(rtpstream_dlg_get_tapinfo());
/* Note that we no longer have a "RTP Streams" dialog box. */
rtp_stream_dlg = NULL;
@@ -180,24 +187,6 @@ rtpstream_on_unselect(GtkButton *button _U_, gpointer user_data _U_)
gtk_label_set_text(GTK_LABEL(label_rev), REV_LABEL_TEXT);
}
-
-/****************************************************************************/
-static gint rtp_stream_info_cmp_reverse(gconstpointer aa, gconstpointer bb)
-{
- const struct _rtp_stream_info* a = (const struct _rtp_stream_info *)aa;
- const struct _rtp_stream_info* b = (const struct _rtp_stream_info *)bb;
-
- if (a==NULL || b==NULL)
- return 1;
- if ((ADDRESSES_EQUAL(&(a->src_addr), &(b->dest_addr)))
- && (a->src_port == b->dest_port)
- && (ADDRESSES_EQUAL(&(a->dest_addr), &(b->src_addr)))
- && (a->dest_port == b->src_port))
- return 0;
- else
- return 1;
-}
-
/****************************************************************************/
static void
rtpstream_on_findrev(GtkButton *button _U_, gpointer user_data _U_)
@@ -254,7 +243,7 @@ rtpstream_on_findrev(GtkButton *button _U_, gpointer user_data _U_)
gtk_tree_model_get_iter(GTK_TREE_MODEL(list_store), &iter, path_fwd);
while (gtk_tree_model_iter_next(GTK_TREE_MODEL(list_store), &iter)) {
gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, RTP_COL_DATA, &stream, -1);
- if (rtp_stream_info_cmp_reverse(selected_stream_fwd, stream) == 0) {
+ if (rtp_stream_info_is_reverse(selected_stream_fwd, stream)) {
found_it = TRUE;
break;
}
@@ -265,7 +254,7 @@ rtpstream_on_findrev(GtkButton *button _U_, gpointer user_data _U_)
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store), &iter);
do {
gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, RTP_COL_DATA, &stream, -1);
- if (rtp_stream_info_cmp_reverse(selected_stream_fwd, stream) == 0) {
+ if (rtp_stream_info_is_reverse(selected_stream_fwd, stream)) {
found_it = TRUE;
break;
}
@@ -369,7 +358,7 @@ rtpstream_on_mark(GtkButton *button _U_, gpointer user_data _U_)
{
if (selected_stream_fwd==NULL && selected_stream_rev==NULL)
return;
- rtpstream_mark(selected_stream_fwd, selected_stream_rev);
+ rtpstream_mark(&the_tapinfo_struct, &cfile, selected_stream_fwd, selected_stream_rev);
}
@@ -446,7 +435,7 @@ rtpstream_on_filter(GtkButton *button _U_, gpointer user_data _U_)
/*
main_filter_packets(&cfile, filter_string, FALSE);
- rtpstream_dlg_update(rtpstream_get_info()->strinfo_list);
+ rtpstream_dlg_update(rtpstream_dlg_get_tapinfo()->strinfo_list);
*/
}
@@ -480,7 +469,6 @@ rtpstream_on_copy_as_csv(GtkWindow *win _U_, gpointer data _U_)
if (gtk_tree_model_get_iter_first(GTK_TREE_MODEL(list_store), &iter)) {
for (i=0; i<streams_nb; i++) {
for (j=0; j<NUM_COLS-1; j++) {
- /*if (j == RTP_COL_SRC_PORT || j == RTP_COL_DST_PORT || j == RTP_COL_PACKETS) { */
if (gtk_tree_model_get_column_type(GTK_TREE_MODEL(list_store), j) == G_TYPE_UINT) {
gtk_tree_model_get(GTK_TREE_MODEL(list_store), &iter, j, &table_entry_uint, -1);
g_string_append_printf(CSV_str, "\"%u\"", table_entry_uint);
@@ -1098,14 +1086,20 @@ rtpstream_dlg_create (void)
/****************************************************************************/
-/* PUBLIC */
-/****************************************************************************/
-
-/****************************************************************************/
/* update the contents of the dialog box list_store */
/* list: pointer to list of rtp_stream_info_t* */
-void rtpstream_dlg_update(GList *list_lcl)
+static void
+rtpstream_dlg_update(void *ti_ptr)
{
+ GList *list_lcl;
+ rtpstream_tapinfo_t *tapinfo = (rtpstream_tapinfo_t *)ti_ptr;
+
+ if (!tapinfo) {
+ return;
+ }
+
+ list_lcl = tapinfo->strinfo_list;
+
if (rtp_stream_dlg != NULL) {
gtk_list_store_clear(list_store);
streams_nb = 0;
@@ -1123,6 +1117,16 @@ void rtpstream_dlg_update(GList *list_lcl)
last_list = list_lcl;
}
+static void
+rtpstream_dlg_mark_packet(rtpstream_tapinfo_t *tapinfo _U_, frame_data *fd) {
+ if (!fd) return;
+
+ cf_mark_frame(&cfile, fd);
+}
+
+/****************************************************************************/
+/* PUBLIC */
+/****************************************************************************/
/****************************************************************************/
/* update the contents of the dialog box list_store */
@@ -1150,13 +1154,13 @@ void rtpstream_dlg_show(GList *list_lcl)
void rtpstream_launch(GtkAction *action _U_, gpointer user_data _U_)
{
/* Register the tap listener */
- register_tap_listener_rtp_stream();
+ register_tap_listener_rtp_stream(&the_tapinfo_struct);
/* Scan for RTP streams (redissect all packets) */
- rtpstream_scan();
+ rtpstream_scan(&the_tapinfo_struct, &cfile);
/* Show the dialog box with the list of streams */
- rtpstream_dlg_show(rtpstream_get_info()->strinfo_list);
+ rtpstream_dlg_show(the_tapinfo_struct.strinfo_list);
/* Tap listener will be removed and cleaned up in rtpstream_on_destroy */
}
@@ -1167,6 +1171,12 @@ register_tap_listener_rtp_stream_dlg(void)
{
}
+/****************************************************************************/
+/* Needed by iax2_analysis.c */
+rtpstream_tapinfo_t *rtpstream_dlg_get_tapinfo(void) {
+ return &the_tapinfo_struct;
+}
+
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*
diff --git a/ui/gtk/rtp_stream_dlg.h b/ui/gtk/rtp_stream_dlg.h
index 91fadf1a0e..8c8fc6408f 100644
--- a/ui/gtk/rtp_stream_dlg.h
+++ b/ui/gtk/rtp_stream_dlg.h
@@ -28,6 +28,8 @@
#include <gtk/gtk.h>
+#include "ui/rtp_stream.h"
+
/** @file
* "RTP Stream Analysis" dialog box.
* @ingroup dialog_group
@@ -41,10 +43,12 @@
void rtpstream_dlg_show(GList *list);
/**
- * Update the contents of the dialog box clist with that of list.
+ * Retrieves a constant reference to the unique info structure of the
+ * rtp_streams tap listener.
+ * The user should not modify the data pointed to.
*
- * @param list pointer to list of rtp_stream_info_t*
+ * @return Pointer to an rtpstream_tapinfo_t
*/
-void rtpstream_dlg_update(GList *list);
+rtpstream_tapinfo_t *rtpstream_dlg_get_tapinfo(void);
#endif /* __RTP_STREAM_DLG_H__ */
diff --git a/ui/help_url.c b/ui/help_url.c
index 5ccb3082c5..bbc724d2bb 100644
--- a/ui/help_url.c
+++ b/ui/help_url.c
@@ -333,6 +333,9 @@ topic_action_url(topic_action_e action)
case(HELP_TELEPHONY_VOIP_CALLS_DIALOG):
url = user_guide_url("ChTelVoipCalls.html");
break;
+ case(HELP_RTP_ANALYSIS_DIALOG):
+ url = user_guide_url("ChTelRTPAnalysis.html");
+ break;
case(TOPIC_ACTION_NONE):
default:
diff --git a/ui/help_url.h b/ui/help_url.h
index 135c75f560..aff98886b9 100644
--- a/ui/help_url.h
+++ b/ui/help_url.h
@@ -110,7 +110,8 @@ typedef enum {
HELP_SAVE_WIN32_DIALOG,
HELP_TIME_SHIFT_DIALOG,
HELP_FILTER_SAVE_DIALOG,
- HELP_TELEPHONY_VOIP_CALLS_DIALOG
+ HELP_TELEPHONY_VOIP_CALLS_DIALOG,
+ HELP_RTP_ANALYSIS_DIALOG
} topic_action_e;
/** Given a filename return a filesystem URL. Relative paths are prefixed with
diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt
index 1e20e8d2f1..822d81ad90 100644
--- a/ui/qt/CMakeLists.txt
+++ b/ui/qt/CMakeLists.txt
@@ -82,6 +82,7 @@ set(WIRESHARK_QT_HEADERS
proto_tree.h
qcustomplot.h
recent_file_status.h
+ rtp_stream_dialog.h
sctp_all_assocs_dialog.h
sctp_assoc_analyse_dialog.h
sctp_chunk_statistics_dialog.h
@@ -112,7 +113,7 @@ if(HAVE_PCAP_REMOTE)
)
endif()
-file(GLOB EXTA_QT_HEADERS
+file(GLOB EXTRA_QT_HEADERS
packet_list_record.h
qt_ui_utils.h
related_packet_delegate.h
@@ -181,6 +182,7 @@ set(WIRESHARK_QT_SRC
qt_ui_utils.cpp
recent_file_status.cpp
related_packet_delegate.cpp
+ rtp_stream_dialog.cpp
sctp_all_assocs_dialog.cpp
sctp_assoc_analyse_dialog.cpp
sctp_chunk_statistics_dialog.cpp
@@ -261,6 +263,7 @@ set(WIRESHARK_QT_UI
preferences_dialog.ui
print_dialog.ui
profile_dialog.ui
+ rtp_stream_dialog.ui
sctp_all_assocs_dialog.ui
sctp_assoc_analyse_dialog.ui
sctp_chunk_statistics_dialog.ui
diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am
index d7f4b4f96c..07879dc839 100644
--- a/ui/qt/Makefile.am
+++ b/ui/qt/Makefile.am
@@ -188,6 +188,8 @@ remote_capture_dialog.cpp remote_capture_dialog.h: ui_remote_capture_dialog.h
remote_settings_dialog.cpp remote_settings_dialog.h: ui_remote_settings_dialog.h
+rtp_stream_dialog.cpp rtp_stream_dialog.h: ui_rtp_stream_dialog.h
+
search_frame.cpp search_frame.h: ui_search_frame.h
sequence_dialog.cpp sequence_dialog.h: ui_sequence_dialog.h
diff --git a/ui/qt/Makefile.common b/ui/qt/Makefile.common
index 4c17bb63cb..93a9ca25a6 100644
--- a/ui/qt/Makefile.common
+++ b/ui/qt/Makefile.common
@@ -63,6 +63,7 @@ NODIST_GENERATED_HEADER_FILES = \
ui_profile_dialog.h \
ui_remote_capture_dialog.h \
ui_remote_settings_dialog.h \
+ ui_rtp_stream_dialog.h \
ui_sctp_all_assocs_dialog.h \
ui_sctp_assoc_analyse_dialog.h \
ui_sctp_chunk_statistics_dialog.h \
@@ -177,6 +178,7 @@ MOC_HDRS = \
remote_capture_dialog.h \
remote_settings_dialog.h \
search_frame.h \
+ rtp_stream_dialog.h \
sctp_all_assocs_dialog.h \
sctp_assoc_analyse_dialog.h \
sctp_chunk_statistics_dialog.h \
@@ -237,6 +239,7 @@ UI_FILES = \
profile_dialog.ui \
remote_capture_dialog.ui \
remote_settings_dialog.ui \
+ rtp_stream_dialog.ui \
sctp_all_assocs_dialog.ui \
sctp_assoc_analyse_dialog.ui \
sctp_chunk_statistics_dialog.ui \
@@ -376,6 +379,7 @@ WIRESHARK_QT_SRC = \
related_packet_delegate.cpp \
remote_capture_dialog.cpp \
remote_settings_dialog.cpp \
+ rtp_stream_dialog.cpp \
sctp_all_assocs_dialog.cpp \
sctp_assoc_analyse_dialog.cpp \
sctp_chunk_statistics_dialog.cpp \
diff --git a/ui/qt/Wireshark.pro b/ui/qt/Wireshark.pro
index b169212c29..aa49c0481a 100644
--- a/ui/qt/Wireshark.pro
+++ b/ui/qt/Wireshark.pro
@@ -239,6 +239,7 @@ FORMS += \
profile_dialog.ui \
remote_capture_dialog.ui \
remote_settings_dialog.ui \
+ rtp_stream_dialog.ui \
sctp_all_assocs_dialog.ui \
sctp_assoc_analyse_dialog.ui \
sctp_chunk_statistics_dialog.ui \
@@ -292,6 +293,7 @@ HEADERS += $$HEADERS_WS_C \
profile_dialog.h \
remote_capture_dialog.h \
remote_settings_dialog.h \
+ rtp_stream_dialog.h \
sctp_all_assocs_dialog.h \
sctp_assoc_analyse_dialog.h \
sctp_chunk_statistics_dialog.h \
@@ -654,6 +656,7 @@ SOURCES += \
related_packet_delegate.cpp \
remote_capture_dialog.cpp \
remote_settings_dialog.cpp \
+ rtp_stream_dialog.cpp \
sctp_all_assocs_dialog.cpp \
sctp_assoc_analyse_dialog.cpp \
sctp_chunk_statistics_dialog.cpp \
diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h
index d1fedbeaa1..28dd1af301 100644
--- a/ui/qt/main_window.h
+++ b/ui/qt/main_window.h
@@ -428,6 +428,7 @@ private slots:
void openVoipCallsDialog(bool all_flows = false);
void on_actionTelephonyVoipCalls_triggered();
void on_actionTelephonyISUPMessages_triggered();
+ void on_actionTelephonyRTPStreams_triggered();
void on_actionTelephonyRTSPPacketCounter_triggered();
void on_actionTelephonySMPPOperations_triggered();
void on_actionTelephonyUCPMessages_triggered();
diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui
index 0e1888faed..b03092f5df 100644
--- a/ui/qt/main_window.ui
+++ b/ui/qt/main_window.ui
@@ -442,8 +442,15 @@
</property>
<addaction name="actionTelephonyRTSPPacketCounter"/>
</widget>
+ <widget class="QMenu" name="menuRTP">
+ <property name="title">
+ <string>RTP</string>
+ </property>
+ <addaction name="actionTelephonyRTPStreams"/>
+ </widget>
<addaction name="actionTelephonyVoipCalls"/>
<addaction name="actionTelephonyISUPMessages"/>
+ <addaction name="menuRTP"/>
<addaction name="menuRTSP"/>
<addaction name="actionTelephonySMPPOperations"/>
<addaction name="actionTelephonyUCPMessages"/>
@@ -2213,6 +2220,11 @@
<string>SIP Flows</string>
</property>
</action>
+ <action name="actionTelephonyRTPStreams">
+ <property name="text">
+ <string>RTP Streams</string>
+ </property>
+ </action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp
index dad5df9943..1df80ab575 100644
--- a/ui/qt/main_window_slots.cpp
+++ b/ui/qt/main_window_slots.cpp
@@ -88,6 +88,7 @@
#include "print_dialog.h"
#include "profile_dialog.h"
#include "qt_ui_utils.h"
+#include "rtp_stream_dialog.h"
#include "sctp_all_assocs_dialog.h"
#include "sctp_assoc_analyse_dialog.h"
#include "sctp_graph_dialog.h"
@@ -2468,6 +2469,18 @@ void MainWindow::on_actionTelephonyISUPMessages_triggered()
openStatisticsTreeDialog("isup_msg");
}
+void MainWindow::on_actionTelephonyRTPStreams_triggered()
+{
+ RtpStreamDialog *rtp_stream_dialog = new RtpStreamDialog(*this, capture_file_);
+ connect(rtp_stream_dialog, SIGNAL(packetsMarked()),
+ packet_list_, SLOT(redrawVisiblePackets()));
+ connect(rtp_stream_dialog, SIGNAL(goToPacket(int)),
+ packet_list_, SLOT(goToPacket(int)));
+ connect(rtp_stream_dialog, SIGNAL(updateFilter(QString&, bool)),
+ this, SLOT(filterPackets(QString&, bool)));
+ rtp_stream_dialog->show();
+}
+
void MainWindow::on_actionTelephonyRTSPPacketCounter_triggered()
{
openStatisticsTreeDialog("rtsp");
diff --git a/ui/qt/packet_list.cpp b/ui/qt/packet_list.cpp
index 57e2acc76a..6b465186a1 100644
--- a/ui/qt/packet_list.cpp
+++ b/ui/qt/packet_list.cpp
@@ -982,7 +982,6 @@ void PacketList::addRelatedFrame(int related_frame)
related_packet_delegate_.addRelatedFrame(related_frame);
}
-#include <QDebug>
void PacketList::showHeaderMenu(QPoint pos)
{
header_ctx_column_ = header()->logicalIndexAt(pos);
diff --git a/ui/qt/rtp_stream_dialog.cpp b/ui/qt/rtp_stream_dialog.cpp
new file mode 100644
index 0000000000..7a502d5585
--- /dev/null
+++ b/ui/qt/rtp_stream_dialog.cpp
@@ -0,0 +1,636 @@
+/* rtp_stream_dialog.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "rtp_stream_dialog.h"
+#include "ui_rtp_stream_dialog.h"
+
+#include "file.h"
+
+#include "epan/addr_resolv.h"
+#include <epan/rtp_pt.h>
+
+#include "ui/utf8_entities.h"
+
+#include "qt_ui_utils.h"
+#include "wireshark_application.h"
+
+#include <QAction>
+#include <QClipboard>
+#include <QFileDialog>
+#include <QKeyEvent>
+#include <QPushButton>
+#include <QTextStream>
+#include <QTreeWidgetItem>
+#include <QTreeWidgetItemIterator>
+
+#include "tango_colors.h"
+
+/*
+ * @file RTP stream dialog
+ *
+ * Displays a list of RTP streams with the following information:
+ * - UDP 4-tuple
+ * - SSRC
+ * - Payload type
+ * - Stats: Packets, lost, max delta, max jitter, mean jitter
+ * - Problems
+ *
+ * Finds reverse streams
+ * "Save As" rtpdump
+ * Mark packets
+ * Go to the setup frame
+ * Prepare filter
+ * Copy As CSV and YAML
+ * Analyze
+ */
+
+// To do:
+// - Add more statistics to the hint text (e.g. lost packets).
+// - Add more statistics to the main list (e.g. stream duration)
+
+const int src_addr_col_ = 0;
+const int src_port_col_ = 1;
+const int dst_addr_col_ = 2;
+const int dst_port_col_ = 3;
+const int ssrc_col_ = 4;
+const int payload_col_ = 5;
+const int packets_col_ = 6;
+const int lost_col_ = 7;
+const int max_delta_col_ = 8;
+const int max_jitter_col_ = 9;
+const int mean_jitter_col_ = 10;
+const int status_col_ = 11;
+
+Q_DECLARE_METATYPE(rtp_stream_info_t*)
+
+class RtpStreamTreeWidgetItem : public QTreeWidgetItem
+{
+public:
+ RtpStreamTreeWidgetItem(QTreeWidget *tree, rtp_stream_info_t *stream_info) : QTreeWidgetItem(tree) {
+ setData(0, Qt::UserRole, qVariantFromValue(stream_info));
+ drawData();
+ }
+
+ void drawData() {
+ rtp_stream_info_t *stream_info = data(0, Qt::UserRole).value<rtp_stream_info_t*>();
+ if (!stream_info) {
+ return;
+ }
+ setText(src_addr_col_, address_to_display_qstring(&stream_info->src_addr));
+ setText(src_port_col_, QString::number(stream_info->src_port));
+ setText(dst_addr_col_, address_to_display_qstring(&stream_info->dest_addr));
+ setText(dst_port_col_, QString::number(stream_info->dest_port));
+ setText(ssrc_col_, QString("0x%1").arg(stream_info->ssrc, 0, 16));
+
+ if (stream_info->payload_type_name != NULL) {
+ setText(payload_col_, stream_info->payload_type_name);
+ } else {
+ setText(payload_col_, val_to_str_ext(stream_info->payload_type,
+ &rtp_payload_type_short_vals_ext,
+ "Unknown (%u)"));
+ }
+
+ setText(packets_col_, QString::number(stream_info->packet_count));
+
+ guint32 expected;
+ double pct_loss;
+ expected = (stream_info->rtp_stats.stop_seq_nr + stream_info->rtp_stats.cycles*65536)
+ - stream_info->rtp_stats.start_seq_nr + 1;
+ lost_ = expected - stream_info->rtp_stats.total_nr;
+ if (expected) {
+ pct_loss = (double)(lost_*100.0)/(double)expected;
+ } else {
+ pct_loss = 0;
+ }
+
+ setText(lost_col_, QObject::tr("%1 (%L2%)").arg(lost_).arg(QString::number(pct_loss, 'f', 1)));
+ setText(max_delta_col_, QString::number(stream_info->rtp_stats.max_delta, 'f', 3)); // This is RTP. Do we need nanoseconds?
+ setText(max_jitter_col_, QString::number(stream_info->rtp_stats.max_jitter, 'f', 3));
+ setText(mean_jitter_col_, QString::number(stream_info->rtp_stats.mean_jitter, 'f', 3));
+
+ if (stream_info->problem) {
+ setText(status_col_, UTF8_BULLET);
+ setTextAlignment(status_col_, Qt::AlignCenter);
+ for (int i = 0; i < columnCount(); i++) {
+ setBackgroundColor(i, ws_css_warn_background);
+ setTextColor(i, ws_css_warn_text);
+ }
+ }
+ }
+ // Return a QString, int, double, or invalid QVariant representing the raw column data.
+ QVariant colData(int col) const {
+ rtp_stream_info_t *stream_info = data(0, Qt::UserRole).value<rtp_stream_info_t*>();
+ if (!stream_info) {
+ return QVariant();
+ }
+
+ switch(col) {
+ case src_addr_col_:
+ case dst_addr_col_:
+ case payload_col_: // XXX Return numeric value?
+ return text(col);
+ case src_port_col_:
+ return stream_info->src_port;
+ case dst_port_col_:
+ return stream_info->dest_port;
+ case ssrc_col_:
+ return stream_info->ssrc;
+ case packets_col_:
+ return stream_info->packet_count;
+ case lost_col_:
+ return lost_;
+ case max_delta_col_:
+ return stream_info->rtp_stats.max_delta;
+ case max_jitter_col_:
+ return stream_info->rtp_stats.max_jitter;
+ case mean_jitter_col_:
+ return stream_info->rtp_stats.mean_jitter;
+ case status_col_:
+ return stream_info->problem ? "Problem" : "";
+ default:
+ break;
+ }
+ return QVariant();
+ }
+
+ bool operator< (const QTreeWidgetItem &other) const
+ {
+ rtp_stream_info_t *this_stream_info = data(0, Qt::UserRole).value<rtp_stream_info_t*>();
+ rtp_stream_info_t *other_stream_info = other.data(0, Qt::UserRole).value<rtp_stream_info_t*>();
+ if (!this_stream_info || !other_stream_info) {
+ return false;
+ }
+ const RtpStreamTreeWidgetItem &other_rstwi = dynamic_cast<const RtpStreamTreeWidgetItem&>(other);
+
+ switch (treeWidget()->sortColumn()) {
+ case src_addr_col_:
+ return cmp_address(&(this_stream_info->src_addr), &(other_stream_info->src_addr)) < 0;
+ case src_port_col_:
+ return this_stream_info->src_port < other_stream_info->src_port;
+ case dst_addr_col_:
+ return cmp_address(&(this_stream_info->dest_addr), &(other_stream_info->dest_addr)) < 0;
+ case dst_port_col_:
+ return this_stream_info->dest_port < other_stream_info->dest_port;
+ case ssrc_col_:
+ return this_stream_info->ssrc < other_stream_info->ssrc;
+ case payload_col_:
+ return this_stream_info->payload_type < other_stream_info->payload_type; // XXX Compare payload_type_name instead?
+ case packets_col_:
+ return this_stream_info->packet_count < other_stream_info->packet_count;
+ case lost_col_:
+ return lost_ < other_rstwi.lost_;
+ case max_delta_col_:
+ return this_stream_info->rtp_stats.max_delta < other_stream_info->rtp_stats.max_delta;
+ case max_jitter_col_:
+ return this_stream_info->rtp_stats.max_jitter < other_stream_info->rtp_stats.max_jitter;
+ case mean_jitter_col_:
+ return this_stream_info->rtp_stats.mean_jitter < other_stream_info->rtp_stats.mean_jitter;
+ default:
+ break;
+ }
+
+ // Fall back to string comparison
+ return QTreeWidgetItem::operator <(other);
+ }
+
+private:
+ guint32 lost_;
+};
+
+RtpStreamDialog::RtpStreamDialog(QWidget &parent, CaptureFile &cf) :
+ WiresharkDialog(parent, cf),
+ ui(new Ui::RtpStreamDialog),
+ need_redraw_(false)
+{
+ ui->setupUi(this);
+ setWindowSubtitle(tr("RTP Streams"));
+ ui->streamTreeWidget->installEventFilter(this);
+
+ // XXX Use recent settings instead
+ resize(parent.width() * 4 / 5, parent.height() * 2 / 3);
+
+ ctx_menu_.addAction(ui->actionSelectNone);
+ ctx_menu_.addAction(ui->actionFindReverse);
+ ctx_menu_.addAction(ui->actionGoToSetup);
+ ctx_menu_.addAction(ui->actionMarkPackets);
+ ctx_menu_.addAction(ui->actionPrepareFilter);
+ ctx_menu_.addAction(ui->actionExportAsRtpDump);
+ ctx_menu_.addAction(ui->actionCopyAsCsv);
+ ctx_menu_.addAction(ui->actionCopyAsYaml);
+ ctx_menu_.addAction(ui->actionAnalyze);
+ ui->streamTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ connect(ui->streamTreeWidget, SIGNAL(customContextMenuRequested(QPoint)),
+ SLOT(showStreamMenu(QPoint)));
+
+ // Some GTK+ buttons have been left out intentionally in order to
+ // reduce clutter. Do you have a strong and informed opinion about
+ // this? Perhaps you should volunteer to maintain this code!
+ find_reverse_button_ = ui->buttonBox->addButton(ui->actionFindReverse->text(), QDialogButtonBox::ApplyRole);
+ find_reverse_button_->setToolTip(ui->actionFindReverse->toolTip());
+ prepare_button_ = ui->buttonBox->addButton(ui->actionPrepareFilter->text(), QDialogButtonBox::ApplyRole);
+ prepare_button_->setToolTip(ui->actionPrepareFilter->toolTip());
+ export_button_ = ui->buttonBox->addButton(tr("Export..."), QDialogButtonBox::ApplyRole);
+ export_button_->setToolTip(ui->actionExportAsRtpDump->toolTip());
+ copy_button_ = ui->buttonBox->addButton(tr("Copy"), QDialogButtonBox::ApplyRole);
+ analyze_button_ = ui->buttonBox->addButton(ui->actionAnalyze->text(), QDialogButtonBox::ApplyRole);
+ analyze_button_->setToolTip(ui->actionAnalyze->toolTip());
+
+ QMenu *copy_menu = new QMenu();
+ QAction *ca;
+ ca = copy_menu->addAction(tr("as CSV"));
+ ca->setToolTip(ui->actionCopyAsCsv->toolTip());
+ connect(ca, SIGNAL(triggered()), this, SLOT(on_actionCopyAsCsv_triggered()));
+ ca = copy_menu->addAction(tr("as YAML"));
+ ca->setToolTip(ui->actionCopyAsYaml->toolTip());
+ connect(ca, SIGNAL(triggered()), this, SLOT(on_actionCopyAsYaml_triggered()));
+ copy_button_->setMenu(copy_menu);
+
+ /* Register the tap listener */
+ memset(&tapinfo_, 0, sizeof(rtpstream_tapinfo_t));
+ tapinfo_.tap_draw = tapDraw;
+ tapinfo_.tap_mark_packet = tapMarkPacket;
+ tapinfo_.tap_data = this;
+ tapinfo_.mode = TAP_ANALYSE;
+
+ register_tap_listener_rtp_stream(&tapinfo_);
+ /* Scan for RTP streams (redissect all packets) */
+ rtpstream_scan(&tapinfo_, cf.capFile());
+
+ updateWidgets();
+}
+
+RtpStreamDialog::~RtpStreamDialog()
+{
+ delete ui;
+ remove_tap_listener_rtp_stream(&tapinfo_);
+}
+
+bool RtpStreamDialog::eventFilter(QObject *obj, QEvent *event)
+{
+ Q_UNUSED(obj)
+ if (ui->streamTreeWidget->hasFocus() && event->type() == QEvent::KeyPress) {
+ QKeyEvent &keyEvent = static_cast<QKeyEvent&>(*event);
+ switch(keyEvent.key()) {
+ case Qt::Key_G:
+ on_actionGoToSetup_triggered();
+ return true;
+ case Qt::Key_M:
+ on_actionMarkPackets_triggered();
+ return true;
+ case Qt::Key_P:
+ on_actionPrepareFilter_triggered();
+ return true;
+ case Qt::Key_R:
+ on_actionFindReverse_triggered();
+ return true;
+ case Qt::Key_A:
+ // XXX "Shift+Ctrl+A" is a fairly standard shortcut for "select none".
+ // However, the main window uses this for displaying the profile dialog.
+// if (keyEvent.modifiers() == (Qt::ControlModifier | Qt::ShiftModifier))
+// on_actionSelectNone_triggered();
+// return true;
+ break;
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+void RtpStreamDialog::tapDraw(void *tapinfo_ptr)
+{
+ rtpstream_tapinfo_t *tapinfo = (rtpstream_tapinfo_t *) tapinfo_ptr;
+
+ RtpStreamDialog *rtp_stream_dialog = static_cast<RtpStreamDialog *>(tapinfo->tap_data);
+ if (rtp_stream_dialog) {
+ rtp_stream_dialog->updateStreams();
+ }
+}
+
+void RtpStreamDialog::tapMarkPacket(rtpstream_tapinfo_t *tapinfo, frame_data *fd)
+{
+ if (!tapinfo) return;
+
+ RtpStreamDialog *rtp_stream_dialog = static_cast<RtpStreamDialog *>(tapinfo->tap_data);
+ if (rtp_stream_dialog) {
+ rtp_stream_dialog->need_redraw_ = true;
+ cf_mark_frame(rtp_stream_dialog->cap_file_.capFile(), fd);
+ rtp_stream_dialog->need_redraw_ = true;
+ }
+}
+
+void RtpStreamDialog::updateStreams()
+{
+ GList *cur_stream = g_list_nth(tapinfo_.strinfo_list, ui->streamTreeWidget->topLevelItemCount());
+
+ // Add any missing items
+ while (cur_stream && cur_stream->data) {
+ rtp_stream_info_t *stream_info = (rtp_stream_info_t*) cur_stream->data;
+ new RtpStreamTreeWidgetItem(ui->streamTreeWidget, stream_info);
+ cur_stream = g_list_next(cur_stream);
+ }
+
+ // Recalculate values
+ QTreeWidgetItemIterator iter(ui->streamTreeWidget);
+ while (*iter) {
+ RtpStreamTreeWidgetItem *rsti = static_cast<RtpStreamTreeWidgetItem*>(*iter);
+ rsti->drawData();
+ ++iter;
+ }
+
+ // Resize columns
+ for (int i = 0; i < ui->streamTreeWidget->columnCount(); i++) {
+ ui->streamTreeWidget->resizeColumnToContents(i);
+ }
+
+ ui->streamTreeWidget->setSortingEnabled(true);
+
+ updateWidgets();
+
+ if (need_redraw_) {
+ emit packetsMarked();
+ need_redraw_ = false;
+ }
+}
+
+void RtpStreamDialog::updateWidgets()
+{
+ bool selected = ui->streamTreeWidget->selectedItems().count() > 0;
+
+ QString hint = "<small><i>";
+ hint += tr("%1 streams").arg(ui->streamTreeWidget->topLevelItemCount());
+
+ if (selected) {
+ int tot_packets = 0;
+ foreach(QTreeWidgetItem *ti, ui->streamTreeWidget->selectedItems()) {
+ rtp_stream_info_t *stream_info = ti->data(0, Qt::UserRole).value<rtp_stream_info_t *>();
+ if (stream_info) {
+ tot_packets += stream_info->packet_count;
+ }
+ }
+ hint += tr(", %1 selected, %2 total packets")
+ .arg(ui->streamTreeWidget->selectedItems().count())
+ .arg(tot_packets);
+ }
+
+ hint += ". Right-click for more options.";
+ hint += "</i></small>";
+ ui->hintLabel->setText(hint);
+
+ bool enable = selected && !file_closed_;
+ bool has_data = ui->streamTreeWidget->topLevelItemCount() > 0;
+
+ find_reverse_button_->setEnabled(enable);
+ prepare_button_->setEnabled(enable);
+ export_button_->setEnabled(enable);
+ copy_button_->setEnabled(has_data);
+ analyze_button_->setEnabled(false); // XXX No dialog
+
+ ui->actionFindReverse->setEnabled(enable);
+ ui->actionGoToSetup->setEnabled(enable);
+ ui->actionMarkPackets->setEnabled(enable);
+ ui->actionPrepareFilter->setEnabled(enable);
+ ui->actionExportAsRtpDump->setEnabled(enable);
+ ui->actionCopyAsCsv->setEnabled(has_data);
+ ui->actionCopyAsYaml->setEnabled(has_data);
+ ui->actionAnalyze->setEnabled(false); // XXX No dialog
+}
+
+QList<QVariant> RtpStreamDialog::streamRowData(int row) const
+{
+ QList<QVariant> row_data;
+
+ if (row >= ui->streamTreeWidget->topLevelItemCount()) {
+ return row_data;
+ }
+
+ for (int col = 0; col < ui->streamTreeWidget->columnCount(); col++) {
+ if (row < 0) {
+ row_data << ui->streamTreeWidget->headerItem()->text(col);
+ } else {
+ RtpStreamTreeWidgetItem *rsti = static_cast<RtpStreamTreeWidgetItem*>(ui->streamTreeWidget->topLevelItem(row));
+ if (rsti) {
+ row_data << rsti->colData(col);
+ }
+ }
+ }
+ return row_data;
+}
+
+void RtpStreamDialog::captureFileClosing()
+{
+ remove_tap_listener_rtp_stream(&tapinfo_);
+ WiresharkDialog::captureFileClosing();
+}
+
+void RtpStreamDialog::showStreamMenu(QPoint pos)
+{
+ ctx_menu_.popup(ui->streamTreeWidget->viewport()->mapToGlobal(pos));
+}
+
+void RtpStreamDialog::on_actionAnalyze_triggered()
+{
+
+}
+
+void RtpStreamDialog::on_actionCopyAsCsv_triggered()
+{
+ QString csv;
+ QTextStream stream(&csv, QIODevice::Text);
+ for (int row = -1; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
+ QStringList rdsl;
+ foreach (QVariant v, streamRowData(row)) {
+ if (!v.isValid()) {
+ rdsl << "\"\"";
+ } else if ((int) v.type() == (int) QMetaType::QString) {
+ rdsl << QString("\"%1\"").arg(v.toString());
+ } else {
+ rdsl << v.toString();
+ }
+ }
+ stream << rdsl.join(",") << endl;
+ }
+ wsApp->clipboard()->setText(stream.readAll());
+}
+
+void RtpStreamDialog::on_actionCopyAsYaml_triggered()
+{
+ QString yaml;
+ QTextStream stream(&yaml, QIODevice::Text);
+ stream << "---" << endl;
+ for (int row = -1; row < ui->streamTreeWidget->topLevelItemCount(); row ++) {
+ stream << "-" << endl;
+ foreach (QVariant v, streamRowData(row)) {
+ stream << " - " << v.toString() << endl;
+ }
+ }
+ wsApp->clipboard()->setText(stream.readAll());
+}
+
+void RtpStreamDialog::on_actionExportAsRtpDump_triggered()
+{
+ if (file_closed_ || ui->streamTreeWidget->selectedItems().count() < 1) return;
+
+ // XXX If the user selected multiple frames is this the one we actually want?
+ QTreeWidgetItem *ti = ui->streamTreeWidget->selectedItems()[0];
+ rtp_stream_info_t *stream_info = ti->data(0, Qt::UserRole).value<rtp_stream_info_t *>();
+ if (stream_info) {
+ QString file_name;
+ QDir path(wsApp->lastOpenDir());
+ QString save_file = path.canonicalPath() + "/" + cap_file_.fileTitle();
+ QString extension;
+ file_name = QFileDialog::getSaveFileName(this, wsApp->windowTitleString(tr("Save RTPDump As" UTF8_HORIZONTAL_ELLIPSIS)),
+ save_file, "RTPDump Format (*.rtpdump)", &extension);
+
+ if (file_name.length() > 0) {
+ gchar *dest_file = qstring_strdup(file_name);
+ gboolean save_ok = rtpstream_save(&tapinfo_, cap_file_.capFile(), stream_info, dest_file);
+ g_free(dest_file);
+ // else error dialog?
+ if (save_ok) {
+ path = QDir(file_name);
+ wsApp->setLastOpenDir(path.canonicalPath().toUtf8().constData());
+ }
+ }
+
+ }
+}
+
+void RtpStreamDialog::on_actionFindReverse_triggered()
+{
+ if (ui->streamTreeWidget->selectedItems().count() < 1) return;
+
+ // Gather up our selected streams...
+ QList<rtp_stream_info_t *> selected_streams;
+ foreach(QTreeWidgetItem *ti, ui->streamTreeWidget->selectedItems()) {
+ rtp_stream_info_t *stream_info = ti->data(0, Qt::UserRole).value<rtp_stream_info_t *>();
+ if (stream_info) {
+ selected_streams << stream_info;
+ }
+ }
+
+ // ...and compare them to our unselected streams.
+ QTreeWidgetItemIterator iter(ui->streamTreeWidget, QTreeWidgetItemIterator::Unselected);
+ while (*iter) {
+ rtp_stream_info_t *stream = (*iter)->data(0, Qt::UserRole).value<rtp_stream_info_t*>();
+ if (stream) {
+ foreach (rtp_stream_info_t *fwd_stream, selected_streams) {
+ if (rtp_stream_info_is_reverse(fwd_stream, stream)) {
+ (*iter)->setSelected(true);
+ }
+ }
+ }
+ ++iter;
+ }
+}
+
+void RtpStreamDialog::on_actionGoToSetup_triggered()
+{
+ if (ui->streamTreeWidget->selectedItems().count() < 1) return;
+ // XXX If the user selected multiple frames is this the one we actually want?
+ QTreeWidgetItem *ti = ui->streamTreeWidget->selectedItems()[0];
+ rtp_stream_info_t *stream_info = ti->data(0, Qt::UserRole).value<rtp_stream_info_t *>();
+ if (stream_info) {
+ emit goToPacket(stream_info->setup_frame_number);
+ }
+}
+
+void RtpStreamDialog::on_actionMarkPackets_triggered()
+{
+ if (ui->streamTreeWidget->selectedItems().count() < 1) return;
+ rtp_stream_info_t *stream_a, *stream_b = NULL;
+
+ stream_a = ui->streamTreeWidget->selectedItems()[0]->data(0, Qt::UserRole).value<rtp_stream_info_t*>();
+ if (ui->streamTreeWidget->selectedItems().count() > 1)
+ stream_b = ui->streamTreeWidget->selectedItems()[1]->data(0, Qt::UserRole).value<rtp_stream_info_t*>();
+
+ if (stream_a == NULL && stream_b == NULL) return;
+
+ // XXX Mark the setup frame as well?
+ need_redraw_ = false;
+ rtpstream_mark(&tapinfo_, cap_file_.capFile(), stream_a, stream_b);
+ updateWidgets();
+}
+
+void RtpStreamDialog::on_actionPrepareFilter_triggered()
+{
+ if (ui->streamTreeWidget->selectedItems().count() < 1) return;
+
+ // Gather up our selected streams...
+ QStringList stream_filters;
+ foreach(QTreeWidgetItem *ti, ui->streamTreeWidget->selectedItems()) {
+ rtp_stream_info_t *stream_info = ti->data(0, Qt::UserRole).value<rtp_stream_info_t *>();
+ if (stream_info) {
+ QString ip_proto = stream_info->src_addr.type == AT_IPv6 ? "ipv6" : "ip";
+ stream_filters << QString("(%1.src==%2 && udp.srcport==%3 && %1.dst==%4 && udp.dstport==%5 && rtp.ssrc==0x%6)")
+ .arg(ip_proto) // %1
+ .arg(address_to_qstring(&stream_info->src_addr)) // %2
+ .arg(stream_info->src_port) // %3
+ .arg(address_to_qstring(&stream_info->dest_addr)) // %4
+ .arg(stream_info->dest_port) // %5
+ .arg(stream_info->ssrc, 0, 16);
+ }
+ }
+ if (stream_filters.length() > 0) {
+ QString filter = stream_filters.join(" || ");
+ remove_tap_listener_rtp_stream(&tapinfo_);
+ emit updateFilter(filter);
+ }
+}
+
+void RtpStreamDialog::on_actionSelectNone_triggered()
+{
+ ui->streamTreeWidget->clearSelection();
+}
+
+void RtpStreamDialog::on_streamTreeWidget_itemSelectionChanged()
+{
+ updateWidgets();
+}
+
+void RtpStreamDialog::on_buttonBox_clicked(QAbstractButton *button)
+{
+ if (button == prepare_button_) {
+ on_actionPrepareFilter_triggered();
+ } else if (button == export_button_) {
+ on_actionExportAsRtpDump_triggered();
+ } else if (button == analyze_button_) {
+
+ }
+}
+
+void RtpStreamDialog::on_buttonBox_helpRequested()
+{
+ wsApp->helpTopicAction(HELP_RTP_ANALYSIS_DIALOG);
+}
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/rtp_stream_dialog.h b/ui/qt/rtp_stream_dialog.h
new file mode 100644
index 0000000000..1615d5453e
--- /dev/null
+++ b/ui/qt/rtp_stream_dialog.h
@@ -0,0 +1,105 @@
+/* rtp_stream_dialog.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef RTP_STREAM_DIALOG_H
+#define RTP_STREAM_DIALOG_H
+
+#include "wireshark_dialog.h"
+
+#include "ui/rtp_stream.h"
+
+#include <QAbstractButton>
+#include <QMenu>
+
+namespace Ui {
+class RtpStreamDialog;
+}
+
+class RtpStreamDialog : public WiresharkDialog
+{
+ Q_OBJECT
+
+public:
+ explicit RtpStreamDialog(QWidget &parent, CaptureFile &cf);
+ ~RtpStreamDialog();
+
+signals:
+ // Tells the packet list to redraw. An alternative might be to add a
+ // cf_packet_marked callback to file.[ch] but that's synchronous and
+ // might incur too much overhead.
+ void packetsMarked();
+ void updateFilter(QString &filter, bool force = false);
+ void goToPacket(int packet_num);
+
+protected:
+ bool eventFilter(QObject *obj, QEvent *event);
+
+private:
+ Ui::RtpStreamDialog *ui;
+ rtpstream_tapinfo_t tapinfo_;
+ QPushButton *find_reverse_button_;
+ QPushButton *prepare_button_;
+ QPushButton *export_button_;
+ QPushButton *copy_button_;
+ QPushButton *analyze_button_;
+ QMenu ctx_menu_;
+ bool need_redraw_;
+
+ static void tapDraw(void *tapinfo_ptr);
+ static void tapMarkPacket(rtpstream_tapinfo_t *tapinfo, frame_data *fd);
+
+ void updateStreams();
+ void updateWidgets();
+
+ QList<QVariant> streamRowData(int row) const;
+
+
+private slots:
+ void captureFileClosing();
+ void showStreamMenu(QPoint pos);
+ void on_actionCopyAsCsv_triggered();
+ void on_actionCopyAsYaml_triggered();
+ void on_actionFindReverse_triggered();
+ void on_actionGoToSetup_triggered();
+ void on_actionMarkPackets_triggered();
+ void on_actionPrepareFilter_triggered();
+ void on_actionSelectNone_triggered();
+ void on_streamTreeWidget_itemSelectionChanged();
+ void on_buttonBox_helpRequested();
+ void on_buttonBox_clicked(QAbstractButton *button);
+ void on_actionExportAsRtpDump_triggered();
+ void on_actionAnalyze_triggered();
+};
+
+#endif // RTP_STREAM_DIALOG_H
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/rtp_stream_dialog.ui b/ui/qt/rtp_stream_dialog.ui
new file mode 100644
index 0000000000..03573aa0a0
--- /dev/null
+++ b/ui/qt/rtp_stream_dialog.ui
@@ -0,0 +1,246 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>RtpStreamDialog</class>
+ <widget class="QDialog" name="RtpStreamDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>600</width>
+ <height>460</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QTreeWidget" name="streamTreeWidget">
+ <property name="selectionMode">
+ <enum>QAbstractItemView::MultiSelection</enum>
+ </property>
+ <property name="textElideMode">
+ <enum>Qt::ElideMiddle</enum>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>false</bool>
+ </property>
+ <property name="uniformRowHeights">
+ <bool>true</bool>
+ </property>
+ <property name="itemsExpandable">
+ <bool>false</bool>
+ </property>
+ <property name="sortingEnabled">
+ <bool>true</bool>
+ </property>
+ <property name="expandsOnDoubleClick">
+ <bool>false</bool>
+ </property>
+ <attribute name="headerDefaultSectionSize">
+ <number>50</number>
+ </attribute>
+ <column>
+ <property name="text">
+ <string>Source Address</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Source Port</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Destination Address</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Destination Port</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>SSRC</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Payload</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Packets</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Lost</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Max Delta (ms)</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Max Jitter</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Mean Jitter</string>
+ </property>
+ </column>
+ <column>
+ <property name="text">
+ <string>Status</string>
+ </property>
+ </column>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="hintLabel">
+ <property name="text">
+ <string>&lt;small&gt;&lt;i&gt;A hint.&lt;/i&gt;&lt;/small&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Close|QDialogButtonBox::Help</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ <action name="actionFindReverse">
+ <property name="text">
+ <string>Find Reverse</string>
+ </property>
+ <property name="toolTip">
+ <string>Find the reverse stream matching the selected forward stream.</string>
+ </property>
+ <property name="shortcut">
+ <string>R</string>
+ </property>
+ </action>
+ <action name="actionMarkPackets">
+ <property name="text">
+ <string>Mark Packets</string>
+ </property>
+ <property name="toolTip">
+ <string>Mark the packets of the selected stream(s).</string>
+ </property>
+ <property name="shortcut">
+ <string>M</string>
+ </property>
+ </action>
+ <action name="actionSelectNone">
+ <property name="text">
+ <string>Select None</string>
+ </property>
+ <property name="toolTip">
+ <string>Undo stream selection.</string>
+ </property>
+ </action>
+ <action name="actionGoToSetup">
+ <property name="text">
+ <string>Go To Setup</string>
+ </property>
+ <property name="toolTip">
+ <string>Go to the setup packet for this stream.</string>
+ </property>
+ <property name="shortcut">
+ <string>G</string>
+ </property>
+ </action>
+ <action name="actionPrepareFilter">
+ <property name="text">
+ <string>Prepare Filter</string>
+ </property>
+ <property name="toolTip">
+ <string>Prepare a filter matching the selected stream(s).</string>
+ </property>
+ <property name="shortcut">
+ <string>P</string>
+ </property>
+ </action>
+ <action name="actionExportAsRtpDump">
+ <property name="text">
+ <string>Export As RTPDump</string>
+ </property>
+ <property name="toolTip">
+ <string>Export the stream payload as rtpdump</string>
+ </property>
+ <property name="shortcut">
+ <string>E</string>
+ </property>
+ </action>
+ <action name="actionAnalyze">
+ <property name="text">
+ <string>Analyze</string>
+ </property>
+ <property name="toolTip">
+ <string>Open the analysis window for the selected stream(s)</string>
+ </property>
+ </action>
+ <action name="actionCopyAsCsv">
+ <property name="text">
+ <string>Copy as CSV</string>
+ </property>
+ <property name="toolTip">
+ <string>Copy stream list as CSV.</string>
+ </property>
+ </action>
+ <action name="actionCopyAsYaml">
+ <property name="text">
+ <string>Copy as YAML</string>
+ </property>
+ <property name="toolTip">
+ <string>Copy stream list as YAML.</string>
+ </property>
+ </action>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>RtpStreamDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>RtpStreamDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/ui/qt/syntax_line_edit.cpp b/ui/qt/syntax_line_edit.cpp
index 483bdcb7ce..867c01d38a 100644
--- a/ui/qt/syntax_line_edit.cpp
+++ b/ui/qt/syntax_line_edit.cpp
@@ -59,9 +59,9 @@ void SyntaxLineEdit::setSyntaxState(SyntaxState state) {
.arg(Invalid)
.arg(Deprecated)
.arg("palette(text)") // Foreground
- .arg(ColorUtils::fromColorT(&prefs.gui_text_valid).name()) // Invalid
- .arg(ColorUtils::fromColorT(&prefs.gui_text_invalid).name()) // Deprecated
- .arg(ColorUtils::fromColorT(&prefs.gui_text_deprecated).name()) // Valid
+ .arg(ColorUtils::fromColorT(&prefs.gui_text_valid).name()) // Valid
+ .arg(ColorUtils::fromColorT(&prefs.gui_text_invalid).name()) // Invalid
+ .arg(ColorUtils::fromColorT(&prefs.gui_text_deprecated).name()) // VDeprecated
;
setStyleSheet(style_sheet_);
}
diff --git a/ui/qt/traffic_table_dialog.cpp b/ui/qt/traffic_table_dialog.cpp
index e4b1034c64..4e3865f215 100644
--- a/ui/qt/traffic_table_dialog.cpp
+++ b/ui/qt/traffic_table_dialog.cpp
@@ -44,6 +44,9 @@
#include <QTextStream>
#include <QToolButton>
+// To do:
+// - Add "copy" items to the menu.
+
// Bugs:
// - Name resolution doesn't do anything if its preference is disabled.
// - Columns don't resize correctly.
diff --git a/ui/qt/wireshark_dialog.h b/ui/qt/wireshark_dialog.h
index 95be7f05c6..36072a79ea 100644
--- a/ui/qt/wireshark_dialog.h
+++ b/ui/qt/wireshark_dialog.h
@@ -39,6 +39,7 @@ signals:
public slots:
protected:
+ virtual void keyPressEvent(QKeyEvent *event) { QDialog::keyPressEvent(event); }
void setWindowSubtitle(const QString &subtitle);
virtual void updateWidgets();
diff --git a/ui/gtk/rtp_stream.c b/ui/rtp_stream.c
index c0b7606b01..f188d0bd5b 100644
--- a/ui/gtk/rtp_stream.c
+++ b/ui/rtp_stream.c
@@ -32,123 +32,147 @@
#include <fcntl.h>
#endif
+#include "file.h"
+
#include <epan/epan.h>
#include <epan/packet.h>
#include <epan/tap.h>
#include <epan/dissectors/packet-rtp.h>
#include <epan/addr_resolv.h>
-#include "../globals.h"
#include "ui/alert_box.h"
#include "ui/simple_dialog.h"
#include "ui/rtp_stream.h"
#include "ui/tap-rtp-common.h"
#include <wsutil/file_util.h>
-#include "ui/gtk/rtp_stream_dlg.h"
-#include "ui/gtk/main.h"
-
-/* The one and only global rtpstream_tapinfo_t structure for tshark and wireshark.
- */
-static rtpstream_tapinfo_t the_tapinfo_struct =
- {0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, 0, FALSE};
-
/****************************************************************************/
/* redraw the output */
-static void rtpstream_draw(void *arg _U_)
+static void rtpstream_draw(void *ti_ptr)
{
+ rtpstream_tapinfo_t *tapinfo = (rtpstream_tapinfo_t *)ti_ptr;
/* XXX: see rtpstream_on_update in rtp_streams_dlg.c for comments
g_signal_emit_by_name(top_level, "signal_rtpstream_update");
*/
- rtpstream_dlg_update(the_tapinfo_struct.strinfo_list);
+ if (tapinfo && tapinfo->tap_draw) {
+ tapinfo->tap_draw(ti_ptr);
+ }
return;
}
/****************************************************************************/
/* scan for RTP streams */
-void rtpstream_scan(void)
+void rtpstream_scan(rtpstream_tapinfo_t *tapinfo, capture_file *cap_file)
{
- gboolean was_registered = the_tapinfo_struct.is_registered;
- if (!the_tapinfo_struct.is_registered)
- register_tap_listener_rtp_stream();
+ gboolean was_registered;
+
+ if (!tapinfo || !cap_file) {
+ return;
+ }
+
+ was_registered = tapinfo->is_registered;
+ if (!tapinfo->is_registered)
+ register_tap_listener_rtp_stream(tapinfo);
- the_tapinfo_struct.mode = TAP_ANALYSE;
- cf_retap_packets(&cfile);
+ tapinfo->mode = TAP_ANALYSE;
+ cf_retap_packets(cap_file);
if (!was_registered)
- remove_tap_listener_rtp_stream();
+ remove_tap_listener_rtp_stream(tapinfo);
}
/****************************************************************************/
/* save rtp dump of stream_fwd */
-gboolean rtpstream_save(rtp_stream_info_t* stream, const gchar *filename)
+gboolean rtpstream_save(rtpstream_tapinfo_t *tapinfo, capture_file *cap_file, rtp_stream_info_t* stream, const gchar *filename)
{
- gboolean was_registered = the_tapinfo_struct.is_registered;
+ gboolean was_registered;
+
+ if (!tapinfo) {
+ return FALSE;
+ }
+
+ was_registered = tapinfo->is_registered;
+
/* open file for saving */
- the_tapinfo_struct.save_file = ws_fopen(filename, "wb");
- if (the_tapinfo_struct.save_file==NULL) {
+ tapinfo->save_file = ws_fopen(filename, "wb");
+ if (tapinfo->save_file==NULL) {
open_failure_alert_box(filename, errno, TRUE);
return FALSE;
}
- rtp_write_header(stream, the_tapinfo_struct.save_file);
- if (ferror(the_tapinfo_struct.save_file)) {
+ rtp_write_header(stream, tapinfo->save_file);
+ if (ferror(tapinfo->save_file)) {
write_failure_alert_box(filename, errno);
- fclose(the_tapinfo_struct.save_file);
+ fclose(tapinfo->save_file);
return FALSE;
}
- if (!the_tapinfo_struct.is_registered)
- register_tap_listener_rtp_stream();
+ if (!tapinfo->is_registered)
+ register_tap_listener_rtp_stream(tapinfo);
- the_tapinfo_struct.mode = TAP_SAVE;
- the_tapinfo_struct.filter_stream_fwd = stream;
- cf_retap_packets(&cfile);
- the_tapinfo_struct.mode = TAP_ANALYSE;
+ tapinfo->mode = TAP_SAVE;
+ tapinfo->filter_stream_fwd = stream;
+ cf_retap_packets(cap_file);
+ tapinfo->mode = TAP_ANALYSE;
if (!was_registered)
- remove_tap_listener_rtp_stream();
+ remove_tap_listener_rtp_stream(tapinfo);
- if (ferror(the_tapinfo_struct.save_file)) {
+ if (ferror(tapinfo->save_file)) {
write_failure_alert_box(filename, errno);
- fclose(the_tapinfo_struct.save_file);
+ fclose(tapinfo->save_file);
return FALSE;
}
- if (fclose(the_tapinfo_struct.save_file) == EOF) {
+ if (fclose(tapinfo->save_file) == EOF) {
write_failure_alert_box(filename, errno);
return FALSE;
}
return TRUE;
}
+/****************************************************************************/
+/* compare the endpoints of two RTP streams */
+gboolean rtp_stream_info_is_reverse(const rtp_stream_info_t *stream_a, rtp_stream_info_t *stream_b)
+{
+ if (stream_a == NULL || stream_b == NULL)
+ return FALSE;
+
+ if ((ADDRESSES_EQUAL(&(stream_a->src_addr), &(stream_b->dest_addr)))
+ && (stream_a->src_port == stream_b->dest_port)
+ && (ADDRESSES_EQUAL(&(stream_a->dest_addr), &(stream_b->src_addr)))
+ && (stream_a->dest_port == stream_b->src_port))
+ return TRUE;
+ else
+ return FALSE;
+}
/****************************************************************************/
/* mark packets in stream_fwd or stream_rev */
-void rtpstream_mark(rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev)
+void rtpstream_mark(rtpstream_tapinfo_t *tapinfo, capture_file *cap_file, rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev)
{
- gboolean was_registered = the_tapinfo_struct.is_registered;
- if (!the_tapinfo_struct.is_registered)
- register_tap_listener_rtp_stream();
+ gboolean was_registered;
- the_tapinfo_struct.mode = TAP_MARK;
- the_tapinfo_struct.filter_stream_fwd = stream_fwd;
- the_tapinfo_struct.filter_stream_rev = stream_rev;
- cf_retap_packets(&cfile);
- the_tapinfo_struct.mode = TAP_ANALYSE;
+ if (!tapinfo) {
+ return;
+ }
- if (!was_registered)
- remove_tap_listener_rtp_stream();
-}
+ was_registered = tapinfo->is_registered;
+ if (!tapinfo->is_registered)
+ register_tap_listener_rtp_stream(tapinfo);
-/****************************************************************************/
-const rtpstream_tapinfo_t* rtpstream_get_info(void)
-{
- return &the_tapinfo_struct;
+ tapinfo->mode = TAP_MARK;
+ tapinfo->filter_stream_fwd = stream_fwd;
+ tapinfo->filter_stream_rev = stream_rev;
+ cf_retap_packets(cap_file);
+ tapinfo->mode = TAP_ANALYSE;
+
+ if (!was_registered)
+ remove_tap_listener_rtp_stream(tapinfo);
}
@@ -158,24 +182,27 @@ const rtpstream_tapinfo_t* rtpstream_get_info(void)
/****************************************************************************/
void
-remove_tap_listener_rtp_stream(void)
+remove_tap_listener_rtp_stream(rtpstream_tapinfo_t *tapinfo)
{
- if (the_tapinfo_struct.is_registered) {
- remove_tap_listener(&the_tapinfo_struct);
-
- the_tapinfo_struct.is_registered = FALSE;
+ if (tapinfo && tapinfo->is_registered) {
+ remove_tap_listener(tapinfo);
+ tapinfo->is_registered = FALSE;
}
}
/****************************************************************************/
void
-register_tap_listener_rtp_stream(void)
+register_tap_listener_rtp_stream(rtpstream_tapinfo_t *tapinfo)
{
GString *error_string;
- if (!the_tapinfo_struct.is_registered) {
- error_string = register_tap_listener("rtp", &the_tapinfo_struct,
+ if (!tapinfo) {
+ return;
+ }
+
+ if (!tapinfo->is_registered) {
+ error_string = register_tap_listener("rtp", tapinfo,
NULL, 0, rtpstream_reset_cb, rtpstream_packet,
rtpstream_draw);
@@ -186,7 +213,7 @@ register_tap_listener_rtp_stream(void)
exit(1);
}
- the_tapinfo_struct.is_registered = TRUE;
+ tapinfo->is_registered = TRUE;
}
}
diff --git a/ui/rtp_stream.h b/ui/rtp_stream.h
index c173c1a2a0..d8ceaaf1e6 100644
--- a/ui/rtp_stream.h
+++ b/ui/rtp_stream.h
@@ -26,10 +26,23 @@
#ifndef __RTP_STREAM_H__
#define __RTP_STREAM_H__
+/** @file
+ * "RTP Streams" dialog box common routines.
+ * @ingroup main_ui_group
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
#include "rtp_analysis.h"
#include <glib.h>
#include <stdio.h>
+
+#include "cfile.h"
+
#include <epan/address.h>
+#include <epan/tap.h>
/****************************************************************************/
@@ -91,12 +104,18 @@ typedef enum
TAP_MARK
} tap_mode_t;
+typedef struct _rtpstream_tapinfo rtpstream_tapinfo_t;
+
+typedef void (*tap_mark_packet_cb)(rtpstream_tapinfo_t *tapinfo, frame_data *fd);
/* structure that holds the information about all detected streams */
/** struct holding all information of the tap */
-typedef struct _rtpstream_tapinfo {
+struct _rtpstream_tapinfo {
+ tap_draw_cb tap_draw; /**< tap draw callback */
+ tap_mark_packet_cb tap_mark_packet; /**< packet marking callback */
+ void *tap_data; /**< data for tap callbacks */
int nstreams; /**< number of streams in the list */
- GList *strinfo_list; /**< list with all streams */
+ GList *strinfo_list; /**< list of rtp_stream_info_t* */
int npackets; /**< total number of rtp packets of all streams */
/* used while tapping. user shouldn't modify these */
tap_mode_t mode;
@@ -105,7 +124,7 @@ typedef struct _rtpstream_tapinfo {
FILE *save_file;
guint32 launch_count; /**< number of times the tap has been run */
gboolean is_registered; /**< if the tap listener is currently registered or not */
-} rtpstream_tapinfo_t;
+};
/****************************************************************************/
/* INTERFACE */
@@ -117,19 +136,13 @@ typedef struct _rtpstream_tapinfo {
* So whenever rtp_stream.c is added to the list of WIRESHARK_TAP_SRCs, the tap will be registered on startup.
* If not, it will be registered on demand by the rtp_streams and rtp_analysis functions that need it.
*/
-void register_tap_listener_rtp_stream(void);
+void register_tap_listener_rtp_stream(rtpstream_tapinfo_t *tapinfo);
/**
* Removes the rtp_streams tap listener (if not already done)
* From that point on, the RTP streams list won't be updated any more.
*/
-void remove_tap_listener_rtp_stream(void);
-
-/**
-* Retrieves a constant reference to the unique info structure of the rtp_streams tap listener.
-* The user should not modify the data pointed to.
-*/
-const rtpstream_tapinfo_t* rtpstream_get_info(void);
+void remove_tap_listener_rtp_stream(rtpstream_tapinfo_t *tapinfo);
/**
* Cleans up memory of rtp streams tap.
@@ -140,20 +153,31 @@ void rtpstream_reset(rtpstream_tapinfo_t *tapinfo);
* Scans all packets for RTP streams and updates the RTP streams list.
* (redissects all packets)
*/
-void rtpstream_scan(void);
+void rtpstream_scan(rtpstream_tapinfo_t *tapinfo, capture_file *cap_file);
/**
* Saves an RTP stream as raw data stream with timestamp information for later RTP playback.
* (redissects all packets)
*/
-gboolean rtpstream_save(rtp_stream_info_t* stream, const gchar *filename);
+gboolean rtpstream_save(rtpstream_tapinfo_t *tapinfo, capture_file *cap_file, rtp_stream_info_t* stream, const gchar *filename);
+
+/**
+* Compares the endpoints of two RTP streams.
+*
+* @return TRUE if the
+*/
+gboolean rtp_stream_info_is_reverse(const rtp_stream_info_t *stream_a, rtp_stream_info_t *stream_b);
/**
* Marks all packets belonging to either of stream_fwd or stream_rev.
* (both can be NULL)
* (redissects all packets)
*/
-void rtpstream_mark(rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev);
+void rtpstream_mark(rtpstream_tapinfo_t *tapinfo, capture_file *cap_file, rtp_stream_info_t* stream_fwd, rtp_stream_info_t* stream_rev);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
#endif /* __RTP_STREAM_H__ */
diff --git a/ui/tap-rtp-common.c b/ui/tap-rtp-common.c
index 8e5f2c92b0..5b14f8e308 100644
--- a/ui/tap-rtp-common.c
+++ b/ui/tap-rtp-common.c
@@ -46,7 +46,7 @@
/****************************************************************************/
/* GCompareFunc style comparison function for _rtp_stream_info */
-gint rtp_stream_info_cmp(gconstpointer aa, gconstpointer bb)
+static gint rtp_stream_info_cmp(gconstpointer aa, gconstpointer bb)
{
const struct _rtp_stream_info* a = (const struct _rtp_stream_info*)aa;
const struct _rtp_stream_info* b = (const struct _rtp_stream_info*)bb;
@@ -264,15 +264,13 @@ int rtpstream_packet(void *arg, packet_info *pinfo, epan_dissect_t *edt _U_, con
rtp_write_sample(&sample, tapinfo->save_file);
}
}
-#ifdef __GTK_H__
- else if (tapinfo->mode == TAP_MARK) {
- if (rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_fwd)==0
- || rtp_stream_info_cmp(&tmp_strinfo, tapinfo->filter_stream_rev)==0)
+ else if (tapinfo->mode == TAP_MARK && tapinfo->tap_mark_packet) {
+ if (rtp_stream_info_cmp(&new_stream_info, tapinfo->filter_stream_fwd)==0
+ || rtp_stream_info_cmp(&new_stream_info, tapinfo->filter_stream_rev)==0)
{
- cf_mark_frame(&cfile, pinfo->fd);
+ tapinfo->tap_mark_packet(tapinfo, pinfo->fd);
}
}
-#endif
return 0;
}
diff --git a/ui/tap-rtp-common.h b/ui/tap-rtp-common.h
index e71d93d838..7fb27c7ed8 100644
--- a/ui/tap-rtp-common.h
+++ b/ui/tap-rtp-common.h
@@ -30,7 +30,6 @@
#ifndef TAP_RTP_COMMON_H_INCLUDED
#define TAP_RTP_COMMON_H_INCLUDED
-gint rtp_stream_info_cmp(gconstpointer, gconstpointer);
void rtpstream_reset_cb(void*);
void rtp_write_header(rtp_stream_info_t*, FILE*);
void rtp_write_sample(rtp_sample_t*, FILE*);
diff --git a/ui/utf8_entities.h b/ui/utf8_entities.h
index 18b0fc1a59..d04f24d1d6 100644
--- a/ui/utf8_entities.h
+++ b/ui/utf8_entities.h
@@ -33,6 +33,7 @@
#define UTF8_MIDDLE_DOT "\xc2\xb7" /* 183 / 0xb7 */
+#define UTF8_BULLET "\xe2\x80\xa2" /* 8226 / 0x2024 */
#define UTF8_HORIZONTAL_ELLIPSIS "\xe2\x80\xa6" /* 8230 / 0x2026 */
#define UTF8_LEFTWARDS_ARROW "\xe2\x86\x90" /* 8592 / 0x2190 */
#define UTF8_RIGHTWARDS_ARROW "\xe2\x86\x92" /* 8594 / 0x2192 */