diff options
author | Gerald Combs <gerald@wireshark.org> | 2015-01-14 17:25:56 -0800 |
---|---|---|
committer | Gerald Combs <gerald@wireshark.org> | 2015-01-30 06:48:32 +0000 |
commit | 2bf7878e8a7455fe656bb07e9a7d42e6ac4d87fd (patch) | |
tree | 3a0c99831311c43017d1d9b3336856e4a956c353 /ui | |
parent | 6824cee6c4b5f7c00b9dc4e9013aaa936b18b739 (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')
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><small><i>A hint.</i></small></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 */ |