diff options
-rw-r--r-- | epan/proto.c | 5 | ||||
-rw-r--r-- | epan/proto.h | 3 | ||||
-rw-r--r-- | ui/gtk/main_menubar.c | 2 | ||||
-rw-r--r-- | ui/gtk/rtp_analysis.c | 27 | ||||
-rw-r--r-- | ui/qt/CMakeLists.txt | 3 | ||||
-rw-r--r-- | ui/qt/Makefile.am | 4 | ||||
-rw-r--r-- | ui/qt/Makefile.common | 4 | ||||
-rw-r--r-- | ui/qt/Wireshark.pro | 3 | ||||
-rw-r--r-- | ui/qt/color_utils.cpp | 20 | ||||
-rw-r--r-- | ui/qt/color_utils.h | 1 | ||||
-rw-r--r-- | ui/qt/follow_stream_dialog.cpp | 2 | ||||
-rw-r--r-- | ui/qt/io_graph_dialog.cpp | 18 | ||||
-rw-r--r-- | ui/qt/main_window.cpp | 2 | ||||
-rw-r--r-- | ui/qt/main_window.h | 1 | ||||
-rw-r--r-- | ui/qt/main_window.ui | 9 | ||||
-rw-r--r-- | ui/qt/main_window_slots.cpp | 14 | ||||
-rw-r--r-- | ui/qt/packet_list.cpp | 2 | ||||
-rw-r--r-- | ui/qt/qt_ui_utils.cpp | 6 | ||||
-rw-r--r-- | ui/qt/qt_ui_utils.h | 3 | ||||
-rw-r--r-- | ui/qt/rtp_analysis_dialog.cpp | 1538 | ||||
-rw-r--r-- | ui/qt/rtp_analysis_dialog.h | 156 | ||||
-rw-r--r-- | ui/qt/rtp_analysis_dialog.ui | 346 | ||||
-rw-r--r-- | ui/qt/rtp_stream_dialog.cpp | 5 | ||||
-rw-r--r-- | ui/qt/syntax_line_edit.cpp | 2 | ||||
-rw-r--r-- | ui/rtp_analysis.h | 10 | ||||
-rw-r--r-- | ui/tap-rtp-common.c | 2 | ||||
-rw-r--r-- | ui/tap-rtp-common.h | 36 | ||||
-rw-r--r-- | ui/utf8_entities.h | 19 |
28 files changed, 2183 insertions, 60 deletions
diff --git a/epan/proto.c b/epan/proto.c index 449e1c5995..447b8c2950 100644 --- a/epan/proto.c +++ b/epan/proto.c @@ -5417,7 +5417,8 @@ proto_get_protocol_filter_name(const int proto_id) void proto_get_frame_protocols(const wmem_list_t *layers, gboolean *is_ip, gboolean *is_tcp, gboolean *is_udp, - gboolean *is_sctp, gboolean *is_ssl) + gboolean *is_sctp, gboolean *is_ssl, + gboolean *is_rtp) { wmem_list_frame_t *protos = wmem_list_head(layers); int proto_id; @@ -5442,6 +5443,8 @@ proto_get_frame_protocols(const wmem_list_t *layers, gboolean *is_ip, *is_sctp = TRUE; } else if (is_ssl && !strcmp(proto_name, "ssl")) { *is_ssl = TRUE; + } else if (is_rtp && !strcmp(proto_name, "rtp")) { + *is_rtp = TRUE; } protos = wmem_list_frame_next(protos); diff --git a/epan/proto.h b/epan/proto.h index fd9b295073..102ed0fdb8 100644 --- a/epan/proto.h +++ b/epan/proto.h @@ -2170,7 +2170,8 @@ WS_DLL_PUBLIC const char *proto_get_protocol_filter_name(const int proto_id); * unchanged. May be NULL. */ WS_DLL_PUBLIC void proto_get_frame_protocols(const wmem_list_t *layers, - gboolean *is_ip, gboolean *is_tcp, gboolean *is_udp, gboolean *is_sctp, gboolean *is_ssl); + gboolean *is_ip, gboolean *is_tcp, gboolean *is_udp, gboolean *is_sctp, + gboolean *is_ssl, gboolean *is_rtp); /** Find a protocol by name in a layer list. * @param layers Protocol layer list diff --git a/ui/gtk/main_menubar.c b/ui/gtk/main_menubar.c index 81c3cfb59a..8b61697328 100644 --- a/ui/gtk/main_menubar.c +++ b/ui/gtk/main_menubar.c @@ -4573,7 +4573,7 @@ set_menus_for_selected_packet(capture_file *cf) than one time reference frame or the current frame isn't a time reference frame). (XXX - why check frame_selected?) */ if (cf->edt) - proto_get_frame_protocols(cf->edt->pi.layers, &is_ip, &is_tcp, &is_udp, &is_sctp, &is_ssl); + proto_get_frame_protocols(cf->edt->pi.layers, &is_ip, &is_tcp, &is_udp, &is_sctp, &is_ssl, NULL); if (cf->edt && cf->edt->tree) { GPtrArray *ga; diff --git a/ui/gtk/rtp_analysis.c b/ui/gtk/rtp_analysis.c index 02d3014029..e33e25932f 100644 --- a/ui/gtk/rtp_analysis.c +++ b/ui/gtk/rtp_analysis.c @@ -484,7 +484,7 @@ static int rtp_packet_save_payload(tap_rtp_save_info_t *saveinfo, /****************************************************************************/ /* whenever a RTP packet is seen by the tap listener */ -static int +static gboolean rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, const void *rtpinfo_arg) { user_data_t *user_data = (user_data_t *)user_data_arg; @@ -493,10 +493,10 @@ rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, con /* we ignore packets that are not displayed */ if (pinfo->fd->flags.passed_dfilter == 0) - return 0; + return FALSE; /* also ignore RTP Version != 2 */ else if (rtpinfo->info_version != 2) - return 0; + return FALSE; /* is it the forward direction? */ else if (user_data->ssrc_fwd == rtpinfo->info_sync_src && (CMP_ADDRESS(&(user_data->src_fwd), &(pinfo->src)) == 0) @@ -548,7 +548,7 @@ rtp_packet(void *user_data_arg, packet_info *pinfo, epan_dissect_t *edt _U_, con #endif } - return 0; + return FALSE; } /* @@ -606,7 +606,7 @@ rtp_packet_add_info(GtkWidget *list, user_data_t * user_data, g_snprintf(color_str, sizeof(color_str), "#ffffbfffbfff"); } else if (statinfo->flags & STAT_FLAG_DUP_PKT) { - g_snprintf(status, sizeof(status), "Suspected duplicate(MAC address) only delta time calculated"); + g_snprintf(status, sizeof(status), "Suspected duplicate (MAC address) only delta time calculated"); /* color = Yellow; */ g_snprintf(color_str, sizeof(color_str), "#ffffffff0000"); } @@ -3212,7 +3212,7 @@ draw_stat(user_data_t *user_data) user_data->forward.statinfo.max_delta, user_data->forward.statinfo.max_nr, user_data->forward.statinfo.max_jitter, user_data->forward.statinfo.mean_jitter, user_data->forward.statinfo.max_skew, - f_expected, f_expected, f_lost, f_perc, + f_total_nr, f_expected, f_lost, f_perc, user_data->forward.statinfo.sequence, f_duration / 1000, f_duration * (f_clock_drift - 1.0), @@ -3231,7 +3231,7 @@ draw_stat(user_data_t *user_data) user_data->reversed.statinfo.max_delta, user_data->reversed.statinfo.max_nr, user_data->reversed.statinfo.max_jitter, user_data->reversed.statinfo.mean_jitter, user_data->reversed.statinfo.max_skew, - r_expected, r_expected, r_lost, r_perc, + r_total_nr, r_expected, r_lost, r_perc, user_data->reversed.statinfo.sequence, r_duration / 1000, r_duration * (r_clock_drift - 1.0), @@ -3543,9 +3543,9 @@ create_rtp_dialog(user_data_t* user_data) GtkWidget *player_bt; #endif /* HAVE_LIBPORTAUDIO */ GtkWidget *graph_bt; - gchar label_forward[150]; - gchar label_forward_tree[150]; - gchar label_reverse[150]; + gchar label_forward[200]; + gchar label_forward_tree[200]; + gchar label_reverse[200]; char *src_addr, *dst_addr; window = dlg_window_new("Wireshark: RTP Stream Analysis"); /* transient_for top_level */ @@ -3566,7 +3566,7 @@ create_rtp_dialog(user_data_t* user_data) g_snprintf(label_forward_tree, sizeof(label_forward_tree), "Analysing stream from %s port %u to %s port %u SSRC = 0x%X \n" - "Note many things affects the accurasy of the analysis, use with caution", + "Note many things affects the accuracy of the analysis, use with caution", src_addr, user_data->port_src_fwd, dst_addr, user_data->port_dst_fwd, user_data->ssrc_fwd); wmem_free(NULL, src_addr); wmem_free(NULL, dst_addr); @@ -3575,7 +3575,7 @@ create_rtp_dialog(user_data_t* user_data) dst_addr = (char*)address_to_display(NULL, &(user_data->dst_rev)); g_snprintf(label_reverse, sizeof(label_reverse), "Analysing stream from %s port %u to %s port %u SSRC = 0x%X \n" - "Note many things affects the accurasy of the analysis, use with caution", + "Note many things affects the accuracy of the analysis, use with caution", src_addr, user_data->port_src_rev, dst_addr, user_data->port_dst_rev, user_data->ssrc_rev); wmem_free(NULL, src_addr); wmem_free(NULL, dst_addr); @@ -3931,7 +3931,7 @@ rtp_analysis_cb(GtkAction *action _U_, gpointer user_data _U_) guint32 ssrc_rev = 0; unsigned int version_fwd; - gchar filter_text[256]; + const gchar *filter_text = "rtp && rtp.version && rtp.ssrc && (ip || ipv6)"; dfilter_t *sfcode; gchar *err_msg; capture_file *cf; @@ -3943,7 +3943,6 @@ rtp_analysis_cb(GtkAction *action _U_, gpointer user_data _U_) rtp_stream_info_t *strinfo; /* Try to compile the filter. */ - g_strlcpy(filter_text, "rtp && rtp.version && rtp.ssrc && (ip || ipv6)", sizeof(filter_text)); if (!dfilter_compile(filter_text, &sfcode, &err_msg)) { simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg); g_free(err_msg); diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index 9ef86b7ba1..35b2d2e2f4 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -99,6 +99,7 @@ set(WIRESHARK_QT_HEADERS related_packet_delegate.h resolved_addresses_dialog.h response_time_delay_dialog.h + rtp_analysis_dialog.h rtp_stream_dialog.h sctp_all_assocs_dialog.h sctp_assoc_analyse_dialog.h @@ -225,6 +226,7 @@ set(WIRESHARK_QT_SRC related_packet_delegate.cpp resolved_addresses_dialog.cpp response_time_delay_dialog.cpp + rtp_analysis_dialog.cpp rtp_stream_dialog.cpp sctp_all_assocs_dialog.cpp sctp_assoc_analyse_dialog.cpp @@ -329,6 +331,7 @@ set(WIRESHARK_QT_UI profile_dialog.ui protocol_hierarchy_dialog.ui resolved_addresses_dialog.ui + rtp_analysis_dialog.ui rtp_stream_dialog.ui sctp_all_assocs_dialog.ui sctp_assoc_analyse_dialog.ui diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am index befd4669ba..0f045e182d 100644 --- a/ui/qt/Makefile.am +++ b/ui/qt/Makefile.am @@ -216,6 +216,10 @@ preferences_dialog.$(OBJEXT): ui_preferences_dialog.h print_dialog.$(OBJEXT): ui_print_dialog.h +rtp_analysis_dialog.$(OBJEXT): ui_rtp_analysis_dialog.h + +rtp_stream_dialog.$(OBJEXT): ui_rtp_stream_dialog.h + profile_dialog.$(OBJEXT): ui_profile_dialog.h protocol_hierarchy_dialog.$(OBJEXT): ui_protocol_hierarchy_dialog.h diff --git a/ui/qt/Makefile.common b/ui/qt/Makefile.common index 6116ad444e..c1a7479e54 100644 --- a/ui/qt/Makefile.common +++ b/ui/qt/Makefile.common @@ -77,6 +77,7 @@ NODIST_GENERATED_HEADER_FILES = \ ui_remote_capture_dialog.h \ ui_remote_settings_dialog.h \ ui_resolved_addresses_dialog.h \ + ui_rtp_analysis_dialog.h \ ui_rtp_stream_dialog.h \ ui_sctp_all_assocs_dialog.h \ ui_sctp_assoc_analyse_dialog.h \ @@ -210,6 +211,7 @@ MOC_HDRS = \ resolved_addresses_dialog.h \ response_time_delay_dialog.h \ search_frame.h \ + rtp_analysis_dialog.h \ rtp_stream_dialog.h \ sctp_all_assocs_dialog.h \ sctp_assoc_analyse_dialog.h \ @@ -289,6 +291,7 @@ UI_FILES = \ remote_capture_dialog.ui \ remote_settings_dialog.ui \ resolved_addresses_dialog.ui \ + rtp_analysis_dialog.ui \ rtp_stream_dialog.ui \ sctp_all_assocs_dialog.ui \ sctp_assoc_analyse_dialog.ui \ @@ -440,6 +443,7 @@ WIRESHARK_QT_SRC = \ remote_settings_dialog.cpp \ resolved_addresses_dialog.cpp \ response_time_delay_dialog.cpp \ + rtp_analysis_dialog.cpp \ rtp_stream_dialog.cpp \ sctp_all_assocs_dialog.cpp \ sctp_assoc_analyse_dialog.cpp \ diff --git a/ui/qt/Wireshark.pro b/ui/qt/Wireshark.pro index 3d6c373eaa..3aadf34e63 100644 --- a/ui/qt/Wireshark.pro +++ b/ui/qt/Wireshark.pro @@ -253,6 +253,7 @@ FORMS += \ remote_capture_dialog.ui \ remote_settings_dialog.ui \ resolved_addresses_dialog.ui \ + rtp_analysis_dialog.ui \ rtp_stream_dialog.ui \ sctp_all_assocs_dialog.ui \ sctp_assoc_analyse_dialog.ui \ @@ -325,6 +326,7 @@ HEADERS += $$HEADERS_WS_C \ remote_capture_dialog.h \ remote_settings_dialog.h \ resolved_addresses_dialog.h \ + rtp_analysis_dialog.h \ rtp_stream_dialog.h \ sctp_all_assocs_dialog.h \ sctp_assoc_analyse_dialog.h \ @@ -712,6 +714,7 @@ SOURCES += \ remote_settings_dialog.cpp \ response_time_delay_dialog.cpp \ resolved_addresses_dialog.cpp \ + rtp_analysis_dialog.cpp \ rtp_stream_dialog.cpp \ sctp_all_assocs_dialog.cpp \ sctp_assoc_analyse_dialog.cpp \ diff --git a/ui/qt/color_utils.cpp b/ui/qt/color_utils.cpp index 70babce6e8..f63b2a0378 100644 --- a/ui/qt/color_utils.cpp +++ b/ui/qt/color_utils.cpp @@ -19,9 +19,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ - #include "color_utils.h" +#include "tango_colors.h" /* * Initialize a color with R, G, and B values, including any toolkit-dependent @@ -53,6 +53,24 @@ const QColor ColorUtils::expert_color_error = QColor ( 0xff, 0x5c, 0x5c ); const QColor ColorUtils::expert_color_foreground = QColor ( 0x00, 0x00, 0x00 ); /* Black */ const QColor ColorUtils::hidden_proto_item = QColor ( 0x44, 0x44, 0x44 ); /* Gray */ +// Available colors +// XXX - Add custom +const QList<QRgb> ColorUtils::graph_colors_ = QList<QRgb>() + << tango_aluminium_6 // Bar outline (use black instead)? + << tango_sky_blue_5 + << tango_butter_6 + << tango_chameleon_5 + << tango_scarlet_red_5 + << tango_plum_5 + << tango_orange_6 + << tango_aluminium_3 + << tango_sky_blue_3 + << tango_butter_3 + << tango_chameleon_3 + << tango_scarlet_red_3 + << tango_plum_3 + << tango_orange_3; + ColorUtils::ColorUtils(QObject *parent) : QObject(parent) { diff --git a/ui/qt/color_utils.h b/ui/qt/color_utils.h index 5626f1c5a5..a263f76d23 100644 --- a/ui/qt/color_utils.h +++ b/ui/qt/color_utils.h @@ -53,6 +53,7 @@ public: static const QColor expert_color_foreground; /* black */ static const QColor hidden_proto_item; /* gray */ + static const QList<QRgb> graph_colors_; signals: public slots: diff --git a/ui/qt/follow_stream_dialog.cpp b/ui/qt/follow_stream_dialog.cpp index 84a8e1983a..d75a0b2f91 100644 --- a/ui/qt/follow_stream_dialog.cpp +++ b/ui/qt/follow_stream_dialog.cpp @@ -855,7 +855,7 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index) return false; } - proto_get_frame_protocols(cap_file_.capFile()->edt->pi.layers, NULL, &is_tcp, &is_udp, NULL, NULL); + proto_get_frame_protocols(cap_file_.capFile()->edt->pi.layers, NULL, &is_tcp, &is_udp, NULL, NULL, NULL); switch (follow_type_) { diff --git a/ui/qt/io_graph_dialog.cpp b/ui/qt/io_graph_dialog.cpp index 222163fd88..e5da5255e4 100644 --- a/ui/qt/io_graph_dialog.cpp +++ b/ui/qt/io_graph_dialog.cpp @@ -31,8 +31,8 @@ #include "ui/utf8_entities.h" #include "qt_ui_utils.h" -#include "tango_colors.h" +#include "color_utils.h" #include "qcustomplot.h" #include "stock_icon.h" #include "syntax_line_edit.h" @@ -78,21 +78,7 @@ const int num_cols_ = 7; // Available colors // XXX - Add custom -QList<QRgb> colors_ = QList<QRgb>() - << tango_aluminium_6 // Bar outline (use black instead)? - << tango_sky_blue_5 - << tango_butter_6 - << tango_chameleon_5 - << tango_scarlet_red_5 - << tango_plum_5 - << tango_orange_6 - << tango_aluminium_3 - << tango_sky_blue_3 - << tango_butter_3 - << tango_chameleon_3 - << tango_scarlet_red_3 - << tango_plum_3 - << tango_orange_3; +QList<QRgb> colors_ = ColorUtils::graph_colors_; const qreal graph_line_width_ = 1.0; diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp index bd60aef7d7..7042e93d85 100644 --- a/ui/qt/main_window.cpp +++ b/ui/qt/main_window.cpp @@ -1798,7 +1798,7 @@ void MainWindow::setMenusForFollowStream() main_ui_->actionAnalyzeFollowUDPStream->setEnabled(false); main_ui_->actionAnalyzeFollowSSLStream->setEnabled(false); - proto_get_frame_protocols(capture_file_.capFile()->edt->pi.layers, NULL, &is_tcp, &is_udp, NULL, NULL); + proto_get_frame_protocols(capture_file_.capFile()->edt->pi.layers, NULL, &is_tcp, &is_udp, NULL, NULL, NULL); if (is_tcp) { diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index 9cd6dd5a63..6f74cda8f6 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -511,6 +511,7 @@ private slots: void on_actionTelephonyVoipCalls_triggered(); void on_actionTelephonyISUPMessages_triggered(); void on_actionTelephonyRTPStreams_triggered(); + void on_actionTelephonyRTPStreamAnalysis_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 292df80960..6a334ef049 100644 --- a/ui/qt/main_window.ui +++ b/ui/qt/main_window.ui @@ -500,6 +500,7 @@ <string>RTP</string> </property> <addaction name="actionTelephonyRTPStreams"/> + <addaction name="actionTelephonyRTPStreamAnalysis"/> </widget> <widget class="QMenu" name="menuANSI"> <property name="title"> @@ -2535,6 +2536,14 @@ <string>Ctrl+Space</string> </property> </action> + <action name="actionTelephonyRTPStreamAnalysis"> + <property name="text"> + <string>Analyze RTP Stream</string> + </property> + <property name="toolTip"> + <string>RTP Stream Analysis</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 c4b73e77bf..d62d91ccad 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -107,6 +107,7 @@ #include "qt_ui_utils.h" #include "resolved_addresses_dialog.h" #include "rtp_stream_dialog.h" +#include "rtp_analysis_dialog.h" #include "sctp_all_assocs_dialog.h" #include "sctp_assoc_analyse_dialog.h" #include "sctp_graph_dialog.h" @@ -956,7 +957,7 @@ void MainWindow::recentActionTriggered() { void MainWindow::setMenusForSelectedPacket() { // gboolean is_ip = FALSE, is_tcp = FALSE, is_udp = FALSE, is_sctp = FALSE, is_ssl = FALSE; - gboolean is_tcp = FALSE, is_sctp = FALSE; + gboolean is_tcp = FALSE, is_sctp = FALSE, is_rtp = FALSE; // /* Making the menu context-sensitive allows for easier selection of the // desired item and has the added benefit, with large captures, of @@ -999,7 +1000,7 @@ void MainWindow::setMenusForSelectedPacket() if (capture_file_.capFile()->edt) { - proto_get_frame_protocols(capture_file_.capFile()->edt->pi.layers, NULL, &is_tcp, NULL, &is_sctp, NULL); + proto_get_frame_protocols(capture_file_.capFile()->edt->pi.layers, NULL, &is_tcp, NULL, &is_sctp, NULL, &is_rtp); } } // if (cfile.edt && cfile.edt->tree) { @@ -1160,6 +1161,7 @@ void MainWindow::setMenusForSelectedPacket() main_ui_->actionSCTPAnalyseThisAssociation->setEnabled(is_sctp); main_ui_->actionSCTPShowAllAssociations->setEnabled(is_sctp); main_ui_->actionSCTPFilterThisAssociation->setEnabled(is_sctp); + main_ui_->actionTelephonyRTPStreamAnalysis->setEnabled(is_rtp); // while (list_entry != NULL) { // dissector_filter_t *filter_entry; @@ -2907,6 +2909,14 @@ void MainWindow::on_actionTelephonyRTPStreams_triggered() rtp_stream_dialog->show(); } +void MainWindow::on_actionTelephonyRTPStreamAnalysis_triggered() +{ + RtpAnalysisDialog *rtp_analysis_dialog = new RtpAnalysisDialog(*this, capture_file_); + connect(rtp_analysis_dialog, SIGNAL(goToPacket(int)), + packet_list_, SLOT(goToPacket(int))); + rtp_analysis_dialog->show(); +} + void MainWindow::on_actionTelephonyRTSPPacketCounter_triggered() { openStatisticsTreeDialog("rtsp"); diff --git a/ui/qt/packet_list.cpp b/ui/qt/packet_list.cpp index 0a368a1b3e..14452438c7 100644 --- a/ui/qt/packet_list.cpp +++ b/ui/qt/packet_list.cpp @@ -506,7 +506,7 @@ void PacketList::contextMenuEvent(QContextMenuEvent *event) /* walk the list of a available protocols in the packet to see what we have */ if (cap_file_ != NULL && cap_file_->edt != NULL) - proto_get_frame_protocols(cap_file_->edt->pi.layers, NULL, &is_tcp, &is_udp, &is_sctp, NULL); + proto_get_frame_protocols(cap_file_->edt->pi.layers, NULL, &is_tcp, &is_udp, &is_sctp, NULL, NULL); QMenu *main_conv_menu = window()->findChild<QMenu *>("menuConversationFilter"); conv_menu_.clear(); diff --git a/ui/qt/qt_ui_utils.cpp b/ui/qt/qt_ui_utils.cpp index c1841c9f80..4d131d302e 100644 --- a/ui/qt/qt_ui_utils.cpp +++ b/ui/qt/qt_ui_utils.cpp @@ -66,13 +66,15 @@ QByteArray gstring_free_to_qbytearray(GString *glib_gstring) return qt_ba; } -const QString address_to_qstring(const _address *address) +const QString address_to_qstring(const _address *address, bool enclose) { QString address_qstr = QString(); if (address) { + if (enclose && address->type == AT_IPv6) address_qstr += "["; gchar *address_gchar_p = address_to_str(NULL, address); - address_qstr = address_gchar_p; + address_qstr += address_gchar_p; wmem_free(NULL, address_gchar_p); + if (enclose && address->type == AT_IPv6) address_qstr += "]"; } return address_qstr; } diff --git a/ui/qt/qt_ui_utils.h b/ui/qt/qt_ui_utils.h index 88057922f8..a46f2d1c84 100644 --- a/ui/qt/qt_ui_utils.h +++ b/ui/qt/qt_ui_utils.h @@ -82,10 +82,11 @@ QByteArray gstring_free_to_qbytearray(GString *glib_gstring); /** Convert an address to a QString using address_to_str(). * * @param address A pointer to an address. + * @param enclose Enclose IPv6 addresses in square brackets. * * @return A QString representation of the address. May be the null string (QString()) */ -const QString address_to_qstring(const struct _address *address); +const QString address_to_qstring(const struct _address *address, bool enclose = false); /** Convert an address to a QString using address_to_display(). * diff --git a/ui/qt/rtp_analysis_dialog.cpp b/ui/qt/rtp_analysis_dialog.cpp new file mode 100644 index 0000000000..e1246cd97e --- /dev/null +++ b/ui/qt/rtp_analysis_dialog.cpp @@ -0,0 +1,1538 @@ +/* rtp_analysis_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_analysis_dialog.h" +#include "ui_rtp_analysis_dialog.h" + +#include "file.h" +#include "frame_tvbuff.h" + +#include "epan/epan_dissect.h" +#include "epan/rtp_pt.h" + +#include "epan/dfilter/dfilter.h" + +#include "epan/dissectors/packet-rtp.h" + +#include "ui/help_url.h" +#include "ui/utf8_entities.h" + +#include <wsutil/g711.h> +#include <wsutil/pint.h> + +#include <QFileDialog> +#include <QMessageBox> +#include <QPushButton> +#include <QTemporaryFile> + +#include "color_utils.h" +#include "qt_ui_utils.h" +#include "stock_icon.h" +#include "wireshark_application.h" + +/* + * @file RTP stream analysis dialog + * + * Displays forward and reverse RTP streams and graphs each stream + */ + +// To do: +// - Progress bar for tapping and saving. +// - Add a refresh button and/or action. +// - Fixup output file names. +// - Add a graph title and legend when saving? + +enum { + packet_col_, + sequence_col_, + delta_col_, + jitter_col_, + skew_col_, + bandwidth_col_, + marker_col_, + status_col_ +}; + +const QRgb color_cn_ = 0xbfbfff; +const QRgb color_rtp_warn_ = 0xffdbbf; +const QRgb color_pt_event_ = 0xefffff; + +enum { rtp_analysis_type_ = 1000 }; +class RtpAnalysisTreeWidgetItem : public QTreeWidgetItem +{ +public: + RtpAnalysisTreeWidgetItem(QTreeWidget *tree, tap_rtp_stat_t *statinfo, packet_info *pinfo, const struct _rtp_info *rtpinfo) : + QTreeWidgetItem(tree, rtp_analysis_type_) + { + frame_num_ = pinfo->fd->num; + sequence_num_ = rtpinfo->info_seq_num; + pkt_len_ = pinfo->fd->pkt_len; + flags_ = statinfo->flags; + if (flags_ & STAT_FLAG_FIRST) { + delta_ = 0.0; + jitter_ = 0.0; + skew_ = 0.0; + } else { + delta_ = statinfo->delta; + jitter_ = statinfo->jitter; + skew_ = statinfo->skew; + } + bandwidth_ = statinfo->bandwidth; + marker_ = rtpinfo->info_marker_set ? true : false; + ok_ = false; + + QColor bg_color = QColor(); + QString status; + + if (statinfo->pt == PT_CN) { + status = "Comfort noise (PT=13, RFC 3389)"; + bg_color = color_cn_; + } else if (statinfo->pt == PT_CN_OLD) { + status = "Comfort noise (PT=19, reserved)"; + bg_color = color_cn_; + } else if (statinfo->flags & STAT_FLAG_WRONG_SEQ) { + status = "Wrong sequence number"; + bg_color = ColorUtils::expert_color_error; + } else if (statinfo->flags & STAT_FLAG_DUP_PKT) { + status = "Suspected duplicate (MAC address) only delta time calculated"; + bg_color = color_rtp_warn_; + } else if (statinfo->flags & STAT_FLAG_REG_PT_CHANGE) { + status = QString("Payload changed to PT=%u").arg(statinfo->pt); + if (statinfo->flags & STAT_FLAG_PT_T_EVENT) { + status.append(" telephone/event"); + } + bg_color = color_rtp_warn_; + } else if (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) { + status = "Incorrect timestamp"; + /* color = COLOR_WARNING; */ + bg_color = color_rtp_warn_; + } else if ((statinfo->flags & STAT_FLAG_PT_CHANGE) + && !(statinfo->flags & STAT_FLAG_FIRST) + && !(statinfo->flags & STAT_FLAG_PT_CN) + && (statinfo->flags & STAT_FLAG_FOLLOW_PT_CN) + && !(statinfo->flags & STAT_FLAG_MARKER)) { + status = "Marker missing?"; + bg_color = color_rtp_warn_; + } else if (statinfo->flags & STAT_FLAG_PT_T_EVENT) { + status = QString("PT=%u telephone/event").arg(statinfo->pt); + /* XXX add color? */ + bg_color = color_pt_event_; + } else { + if (statinfo->flags & STAT_FLAG_MARKER) { + bg_color = color_rtp_warn_; + } + } + + if (status.isEmpty()) { + ok_ = true; + status = UTF8_CHECK_MARK; + } + + setText(packet_col_, QString::number(frame_num_)); + setText(sequence_col_, QString::number(sequence_num_)); + setText(delta_col_, QString::number(delta_, 'f', 2)); + setText(jitter_col_, QString::number(jitter_, 'f', 2)); + setText(skew_col_, QString::number(skew_, 'f', 2)); + setText(bandwidth_col_, QString::number(bandwidth_, 'f', 2)); + if (marker_) { + setText(marker_col_, UTF8_BULLET); + } + setText(status_col_, status); + + setTextAlignment(packet_col_, Qt::AlignRight); + setTextAlignment(sequence_col_, Qt::AlignRight); + setTextAlignment(delta_col_, Qt::AlignRight); + setTextAlignment(jitter_col_, Qt::AlignRight); + setTextAlignment(skew_col_, Qt::AlignRight); + setTextAlignment(bandwidth_col_, Qt::AlignRight); + setTextAlignment(marker_col_, Qt::AlignCenter); + + if (bg_color.isValid()) { + for (int col = 0; col < columnCount(); col++) { + setBackground(col, bg_color); + setForeground(col, ColorUtils::expert_color_foreground); + } + } + } + + guint32 frameNum() { return frame_num_; } + bool frameStatus() { return ok_; } + + QList<QVariant> rowData() { + QString marker_str; + QString status_str = ok_ ? "OK" : text(status_col_); + + if (marker_) marker_str = "SET"; + + return QList<QVariant>() + << frame_num_ << sequence_num_ << delta_ << jitter_ << skew_ << bandwidth_ + << marker_str << status_str; + } + + bool operator< (const QTreeWidgetItem &other) const + { + if (other.type() != rtp_analysis_type_) return QTreeWidgetItem::operator< (other); + const RtpAnalysisTreeWidgetItem *other_row = static_cast<const RtpAnalysisTreeWidgetItem *>(&other); + + switch (treeWidget()->sortColumn()) { + case (packet_col_): + return frame_num_ < other_row->frame_num_; + break; + case (sequence_col_): + return sequence_num_ < other_row->sequence_num_; + break; + case (delta_col_): + return delta_ < other_row->delta_; + break; + case (jitter_col_): + return jitter_ < other_row->jitter_; + break; + case (skew_col_): + return skew_ < other_row->skew_; + break; + case (bandwidth_col_): + return bandwidth_ < other_row->bandwidth_; + break; + default: + break; + } + + // Fall back to string comparison + return QTreeWidgetItem::operator <(other); + } +private: + guint32 frame_num_; + guint32 sequence_num_; + guint32 pkt_len_; + guint32 flags_; + double delta_; + double jitter_; + double skew_; + double bandwidth_; + bool marker_; + bool ok_; +}; + +enum { + fwd_jitter_graph_, + fwd_diff_graph_, + fwd_delta_graph_, + rev_jitter_graph_, + rev_diff_graph_, + rev_delta_graph_, + num_graphs_ +}; + +RtpAnalysisDialog::RtpAnalysisDialog(QWidget &parent, CaptureFile &cf) : + WiresharkDialog(parent, cf), + ui(new Ui::RtpAnalysisDialog), + port_src_fwd_(0), + port_dst_fwd_(0), + ssrc_fwd_(0), + port_src_rev_(0), + port_dst_rev_(0), + ssrc_rev_(0) +{ + ui->setupUi(this); + setWindowSubtitle(tr("RTP Stream Analysis")); + + // XXX Use recent settings instead + resize(parent.width() * 4 / 5, parent.height() * 4 / 5); + + stream_ctx_menu_.addAction(ui->actionGoToPacket); + stream_ctx_menu_.addAction(ui->actionNextProblem); + stream_ctx_menu_.addSeparator(); + stream_ctx_menu_.addAction(ui->actionSaveAudio); + stream_ctx_menu_.addAction(ui->actionSaveForwardAudio); + stream_ctx_menu_.addAction(ui->actionSaveReverseAudio); + stream_ctx_menu_.addSeparator(); + stream_ctx_menu_.addAction(ui->actionSaveCsv); + stream_ctx_menu_.addAction(ui->actionSaveForwardCsv); + stream_ctx_menu_.addAction(ui->actionSaveReverseCsv); + stream_ctx_menu_.addSeparator(); + stream_ctx_menu_.addAction(ui->actionSaveGraph); + ui->forwardTreeWidget->installEventFilter(this); + ui->forwardTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->forwardTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), + SLOT(showStreamMenu(QPoint))); + ui->reverseTreeWidget->installEventFilter(this); + ui->reverseTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->reverseTreeWidget, SIGNAL(customContextMenuRequested(QPoint)), + SLOT(showStreamMenu(QPoint))); + connect(ui->streamGraph, SIGNAL(mousePress(QMouseEvent*)), + this, SLOT(graphClicked(QMouseEvent*))); + + graph_ctx_menu_.addAction(ui->actionSaveGraph); + + QStringList header_labels; + for (int i = 0; i < ui->forwardTreeWidget->columnCount(); i++) { + header_labels << ui->forwardTreeWidget->headerItem()->text(i); + } + ui->reverseTreeWidget->setHeaderLabels(header_labels); + + memset(&src_fwd_, 0, sizeof(address)); + memset(&dst_fwd_, 0, sizeof(address)); + memset(&src_rev_, 0, sizeof(address)); + memset(&dst_rev_, 0, sizeof(address)); + + QList<QCheckBox *> graph_cbs = QList<QCheckBox *>() + << ui->fJitterCheckBox << ui->fDiffCheckBox << ui->fDeltaCheckBox + << ui->rJitterCheckBox << ui->rDiffCheckBox << ui->rDeltaCheckBox; + + for (int i = 0; i < num_graphs_; i++) { + QCPGraph *graph = ui->streamGraph->addGraph(); + graph->setPen(QPen(ColorUtils::graph_colors_[i])); + graph->setName(graph_cbs[i]->text()); + graphs_ << graph; + graph_cbs[i]->setChecked(true); + graph_cbs[i]->setIcon(StockIcon::colorIcon(ColorUtils::graph_colors_[i], QPalette::Text)); + } + ui->streamGraph->xAxis->setLabel("Arrival Time"); + ui->streamGraph->yAxis->setLabel("Value (ms)"); + + // We keep our temp files open for the lifetime of the dialog. The GTK+ + // UI opens and closes at various points. + QString tempname = QString("%1/wireshark_rtp_f").arg(QDir::tempPath()); + fwd_tempfile_ = new QTemporaryFile(tempname, this); + fwd_tempfile_->open(); + tempname = QString("%1/wireshark_rtp_r").arg(QDir::tempPath()); + rev_tempfile_ = new QTemporaryFile(tempname, this); + rev_tempfile_->open(); + + if (fwd_tempfile_->error() != QFile::NoError || rev_tempfile_->error() != QFile::NoError) { + err_str_ = tr("Unable to save RTP data."); + ui->actionSaveAudio->setEnabled(false); + ui->actionSaveForwardAudio->setEnabled(false); + ui->actionSaveReverseAudio->setEnabled(false); + } + + QMenu *save_menu = new QMenu(); + save_menu->addAction(ui->actionSaveAudio); + save_menu->addAction(ui->actionSaveForwardAudio); + save_menu->addAction(ui->actionSaveReverseAudio); + save_menu->addSeparator(); + save_menu->addAction(ui->actionSaveCsv); + save_menu->addAction(ui->actionSaveForwardCsv); + save_menu->addAction(ui->actionSaveReverseCsv); + save_menu->addSeparator(); + save_menu->addAction(ui->actionSaveGraph); + ui->buttonBox->button(QDialogButtonBox::Save)->setMenu(save_menu); + + const gchar *filter_text = "rtp && rtp.version && rtp.ssrc && (ip || ipv6)"; + dfilter_t *sfcode; + gchar *err_msg; + + if (!dfilter_compile(filter_text, &sfcode, &err_msg)) { + QMessageBox::warning(this, tr("No RTP packets found"), QString("%1").arg(err_msg)); + g_free(err_msg); + close(); + } + + if (!cap_file_.capFile() || !cap_file_.capFile()->current_frame) close(); + + frame_data *fdata = cap_file_.capFile()->current_frame; + + if (!cf_read_record(cap_file_.capFile(), fdata)) close(); + + epan_dissect_t edt; + + epan_dissect_init(&edt, cap_file_.capFile()->epan, TRUE, FALSE); + epan_dissect_prime_dfilter(&edt, sfcode); + epan_dissect_run(&edt, cap_file_.capFile()->cd_t, &cap_file_.capFile()->phdr, + frame_tvbuff_new_buffer(fdata, &cap_file_.capFile()->buf), fdata, NULL); + + // This shouldn't happen (the menu item should be disabled) but check anyway + if (!dfilter_apply_edt(sfcode, &edt)) { + epan_dissect_cleanup(&edt); + dfilter_free(sfcode); + err_str_ = tr("Please select an RTP packet"); + updateWidgets(); + return; + } + + dfilter_free(sfcode); + + /* OK, it is an RTP frame. Let's get the IP and port values */ + COPY_ADDRESS(&(src_fwd_), &(edt.pi.src)); + COPY_ADDRESS(&(dst_fwd_), &(edt.pi.dst)); + port_src_fwd_ = edt.pi.srcport; + port_dst_fwd_ = edt.pi.destport; + + /* assume the inverse ip/port combination for the reverse direction */ + COPY_ADDRESS(&(src_rev_), &(edt.pi.dst)); + COPY_ADDRESS(&(dst_rev_), &(edt.pi.src)); + port_src_rev_ = edt.pi.destport; + port_dst_rev_ = edt.pi.srcport; + + /* Check if it is RTP Version 2 */ + unsigned int version_fwd; + bool ok; + version_fwd = getIntFromProtoTree(edt.tree, "rtp", "rtp.version", &ok); + if (!ok || version_fwd != 2) { + err_str_ = tr("RTP version %1 found. Only version 2 is supported.").arg(version_fwd); + updateWidgets(); + return; + } + + /* now we need the SSRC value of the current frame */ + ssrc_fwd_ = getIntFromProtoTree(edt.tree, "rtp", "rtp.ssrc", &ok); + if (!ok) { + err_str_ = tr("SSRC value not found."); + updateWidgets(); + return; + } + + /* Register the tap listener */ + memset(&tapinfo_, 0, sizeof(rtpstream_tapinfo_t)); + tapinfo_.tap_data = this; + tapinfo_.mode = TAP_ANALYSE; + +// register_tap_listener_rtp_stream(&tapinfo_, NULL); + /* Scan for RTP streams (redissect all packets) */ + rtpstream_scan(&tapinfo_, cap_file_.capFile(), NULL); + + num_streams_ = 0; + GList *filtered_list = NULL; + for (GList *strinfo_list = g_list_first(tapinfo_.strinfo_list); strinfo_list; strinfo_list = g_list_next(strinfo_list)) { + rtp_stream_info_t * strinfo = (rtp_stream_info_t*)(strinfo_list->data); + if (ADDRESSES_EQUAL(&(strinfo->src_addr), &(src_fwd_)) + && (strinfo->src_port == port_src_fwd_) + && (ADDRESSES_EQUAL(&(strinfo->dest_addr), &(dst_fwd_))) + && (strinfo->dest_port == port_dst_fwd_)) + { + ++num_streams_; + filtered_list = g_list_prepend(filtered_list, strinfo); + } + + if (ADDRESSES_EQUAL(&(strinfo->src_addr), &(src_rev_)) + && (strinfo->src_port == port_src_rev_) + && (ADDRESSES_EQUAL(&(strinfo->dest_addr), &(dst_rev_))) + && (strinfo->dest_port == port_dst_rev_)) + { + ++num_streams_; + filtered_list = g_list_append(filtered_list, strinfo); + if (ssrc_rev_ == 0) + ssrc_rev_ = strinfo->ssrc; + } + } + + if (num_streams_ < 1) { + err_str_ = tr("No streams found."); + } + + connect(ui->tabWidget, SIGNAL(currentChanged(int)), + this, SLOT(updateWidgets())); + connect(ui->forwardTreeWidget, SIGNAL(itemSelectionChanged()), + this, SLOT(updateWidgets())); + connect(ui->reverseTreeWidget, SIGNAL(itemSelectionChanged()), + this, SLOT(updateWidgets())); + connect(&cap_file_, SIGNAL(captureFileClosing()), + this, SLOT(updateWidgets())); + updateWidgets(); + + register_tap_listener("rtp", this, NULL, 0, tapReset, tapPacket, tapDraw); + + cap_file_.retapPackets(); + remove_tap_listener(this); + + updateStatistics(); +} + +RtpAnalysisDialog::~RtpAnalysisDialog() +{ + delete ui; +// remove_tap_listener_rtp_stream(&tapinfo_); + delete fwd_tempfile_; + delete rev_tempfile_; +} + +// XXX Should we do this in WiresharkDialog? +void RtpAnalysisDialog::reject() +{ + // We need to make sure our temporary files are closed. + deleteLater(); + WiresharkDialog::reject(); +} + +void RtpAnalysisDialog::updateWidgets() +{ + bool enable_tab = false; + QString hint = err_str_; + + if (hint.isEmpty()) { + enable_tab = true; + hint = tr("%1 streams found.").arg(num_streams_); + } + + bool enable_nav = false; + if (!file_closed_ + && ((ui->tabWidget->currentWidget() == ui->forwardTreeWidget + && ui->forwardTreeWidget->selectedItems().length() > 0) + || (ui->tabWidget->currentWidget() == ui->reverseTreeWidget + && ui->reverseTreeWidget->selectedItems().length() > 0))) { + enable_nav = true; + } + ui->actionGoToPacket->setEnabled(enable_nav); + ui->actionNextProblem->setEnabled(enable_nav); + + if (enable_nav) { + hint.append(tr(" G: Go to packet, N: Next problem packet")); + } + + bool enable_save_fwd_audio = fwd_tempfile_->isOpen(); + bool enable_save_rev_audio = rev_tempfile_->isOpen(); + ui->actionSaveAudio->setEnabled(enable_save_fwd_audio && enable_save_rev_audio); + ui->actionSaveForwardAudio->setEnabled(enable_save_fwd_audio); + ui->actionSaveReverseAudio->setEnabled(enable_save_rev_audio); + + bool enable_save_fwd_csv = ui->forwardTreeWidget->topLevelItemCount() > 0; + bool enable_save_rev_csv = ui->reverseTreeWidget->topLevelItemCount() > 0; + ui->actionSaveCsv->setEnabled(enable_save_fwd_csv && enable_save_rev_csv); + ui->actionSaveForwardCsv->setEnabled(enable_save_fwd_csv); + ui->actionSaveReverseCsv->setEnabled(enable_save_rev_csv); + + ui->tabWidget->setEnabled(enable_tab); + hint.prepend("<small><i>"); + hint.append("</i></small>"); + ui->hintLabel->setText(hint); +} + +void RtpAnalysisDialog::on_actionGoToPacket_triggered() +{ + if (file_closed_) return; + QTreeWidget *cur_tree = qobject_cast<QTreeWidget *>(ui->tabWidget->currentWidget()); + if (!cur_tree || cur_tree->selectedItems().length() < 1) return; + + QTreeWidgetItem *ti = cur_tree->selectedItems()[0]; + if (ti->type() != rtp_analysis_type_) return; + + RtpAnalysisTreeWidgetItem *ra_ti = dynamic_cast<RtpAnalysisTreeWidgetItem *>((RtpAnalysisTreeWidgetItem *)ti); + emit goToPacket(ra_ti->frameNum()); +} + +void RtpAnalysisDialog::on_actionNextProblem_triggered() +{ + QTreeWidget *cur_tree = qobject_cast<QTreeWidget *>(ui->tabWidget->currentWidget()); + if (!cur_tree || cur_tree->topLevelItemCount() < 2) return; + + // Choose convenience over correctness. + if (cur_tree->selectedItems().length() < 1) { + cur_tree->setCurrentItem(cur_tree->topLevelItem(0)); + } + + QTreeWidgetItem *sel_ti = cur_tree->selectedItems()[0]; + if (sel_ti->type() != rtp_analysis_type_) return; + QTreeWidgetItem *test_ti = cur_tree->itemBelow(sel_ti); + while (test_ti != sel_ti) { + if (!test_ti) test_ti = cur_tree->topLevelItem(0); + RtpAnalysisTreeWidgetItem *ra_ti = dynamic_cast<RtpAnalysisTreeWidgetItem *>((RtpAnalysisTreeWidgetItem *)test_ti); + if (!ra_ti->frameStatus()) { + cur_tree->setCurrentItem(ra_ti); + break; + } + + test_ti = cur_tree->itemBelow(test_ti); + } +} + +void RtpAnalysisDialog::on_fJitterCheckBox_toggled(bool checked) +{ + ui->streamGraph->graph(fwd_jitter_graph_)->setVisible(checked); + updateGraph(); +} + +void RtpAnalysisDialog::on_fDiffCheckBox_toggled(bool checked) +{ + ui->streamGraph->graph(fwd_diff_graph_)->setVisible(checked); + updateGraph(); +} + +void RtpAnalysisDialog::on_fDeltaCheckBox_toggled(bool checked) +{ + ui->streamGraph->graph(fwd_delta_graph_)->setVisible(checked); + updateGraph(); +} + +void RtpAnalysisDialog::on_rJitterCheckBox_toggled(bool checked) +{ + ui->streamGraph->graph(rev_jitter_graph_)->setVisible(checked); + updateGraph(); +} + +void RtpAnalysisDialog::on_rDiffCheckBox_toggled(bool checked) +{ + ui->streamGraph->graph(rev_diff_graph_)->setVisible(checked); + updateGraph(); +} + +void RtpAnalysisDialog::on_rDeltaCheckBox_toggled(bool checked) +{ + ui->streamGraph->graph(rev_delta_graph_)->setVisible(checked); + updateGraph(); +} + +void RtpAnalysisDialog::on_actionSaveAudio_triggered() +{ + saveAudio(dir_both_); +} + +void RtpAnalysisDialog::on_actionSaveForwardAudio_triggered() +{ + saveAudio(dir_forward_); +} + +void RtpAnalysisDialog::on_actionSaveReverseAudio_triggered() +{ + saveAudio(dir_reverse_); +} + +void RtpAnalysisDialog::on_actionSaveCsv_triggered() +{ + saveCsv(dir_both_); +} + +void RtpAnalysisDialog::on_actionSaveForwardCsv_triggered() +{ + saveCsv(dir_forward_); +} + +void RtpAnalysisDialog::on_actionSaveReverseCsv_triggered() +{ + saveCsv(dir_reverse_); +} + +void RtpAnalysisDialog::on_actionSaveGraph_triggered() +{ + ui->tabWidget->setCurrentWidget(ui->graphTab); + + QString file_name, extension; + QDir path(wsApp->lastOpenDir()); + QString pdf_filter = tr("Portable Document Format (*.pdf)"); + QString png_filter = tr("Portable Network Graphics (*.png)"); + QString bmp_filter = tr("Windows Bitmap (*.bmp)"); + // Gaze upon my beautiful graph with lossy artifacts! + QString jpeg_filter = tr("JPEG File Interchange Format (*.jpeg *.jpg)"); + QString filter = QString("%1;;%2;;%3;;%4") + .arg(pdf_filter) + .arg(png_filter) + .arg(bmp_filter) + .arg(jpeg_filter); + + QString save_file = path.canonicalPath(); + if (!file_closed_) { + save_file += QString("/%1").arg(cap_file_.fileTitle()); + } + file_name = QFileDialog::getSaveFileName(this, wsApp->windowTitleString(tr("Save Graph As" UTF8_HORIZONTAL_ELLIPSIS)), + save_file, filter, &extension); + + if (!file_name.isEmpty()) { + bool save_ok = false; + // http://www.qcustomplot.com/index.php/support/forum/63 +// ui->streamGraph->legend->setVisible(true); + if (extension.compare(pdf_filter) == 0) { + save_ok = ui->streamGraph->savePdf(file_name); + } else if (extension.compare(png_filter) == 0) { + save_ok = ui->streamGraph->savePng(file_name); + } else if (extension.compare(bmp_filter) == 0) { + save_ok = ui->streamGraph->saveBmp(file_name); + } else if (extension.compare(jpeg_filter) == 0) { + save_ok = ui->streamGraph->saveJpg(file_name); + } +// ui->streamGraph->legend->setVisible(false); + // else error dialog? + if (save_ok) { + path = QDir(file_name); + wsApp->setLastOpenDir(path.canonicalPath().toUtf8().constData()); + } + } +} + +void RtpAnalysisDialog::on_buttonBox_helpRequested() +{ + wsApp->helpTopicAction(HELP_RTP_ANALYSIS_DIALOG); +} + +void RtpAnalysisDialog::tapReset(void *tapinfo_ptr) +{ + RtpAnalysisDialog *rtp_analysis_dialog = dynamic_cast<RtpAnalysisDialog *>((RtpAnalysisDialog*)tapinfo_ptr); + if (!rtp_analysis_dialog) return; + + rtp_analysis_dialog->resetStatistics(); +} + +gboolean RtpAnalysisDialog::tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *rtpinfo_ptr) +{ + RtpAnalysisDialog *rtp_analysis_dialog = dynamic_cast<RtpAnalysisDialog *>((RtpAnalysisDialog*)tapinfo_ptr); + if (!rtp_analysis_dialog) return FALSE; + + const struct _rtp_info *rtpinfo = (const struct _rtp_info *)rtpinfo_ptr; + if (!rtpinfo) return FALSE; + + /* we ignore packets that are not displayed */ + if (pinfo->fd->flags.passed_dfilter == 0) + return FALSE; + /* also ignore RTP Version != 2 */ + else if (rtpinfo->info_version != 2) + return FALSE; + /* is it the forward direction? */ + else if (rtp_analysis_dialog->ssrc_fwd_ == rtpinfo->info_sync_src + && (CMP_ADDRESS(&(rtp_analysis_dialog->src_fwd_), &(pinfo->src)) == 0) + && (rtp_analysis_dialog->port_src_fwd_ == pinfo->srcport) + && (CMP_ADDRESS(&(rtp_analysis_dialog->dst_fwd_), &(pinfo->dst)) == 0) + && (rtp_analysis_dialog->port_dst_fwd_ == pinfo->destport)) { + + rtp_analysis_dialog->addPacket(true, pinfo, rtpinfo); + } + /* is it the reversed direction? */ + else if (rtp_analysis_dialog->ssrc_rev_ == rtpinfo->info_sync_src + && (CMP_ADDRESS(&(rtp_analysis_dialog->src_rev_), &(pinfo->src)) == 0) + && (rtp_analysis_dialog->port_src_rev_ == pinfo->srcport) + && (CMP_ADDRESS(&(rtp_analysis_dialog->dst_rev_), &(pinfo->dst)) == 0) + && (rtp_analysis_dialog->port_dst_rev_ == pinfo->destport)) { + + rtp_analysis_dialog->addPacket(false, pinfo, rtpinfo); + } + return FALSE; +} + +void RtpAnalysisDialog::tapDraw(void *tapinfo_ptr) +{ + RtpAnalysisDialog *rtp_analysis_dialog = dynamic_cast<RtpAnalysisDialog *>((RtpAnalysisDialog*)tapinfo_ptr); + if (!rtp_analysis_dialog) return; + rtp_analysis_dialog->updateStatistics(); +} + +void RtpAnalysisDialog::resetStatistics() +{ + memset(&fwd_statinfo_, 0, sizeof(tap_rtp_stat_t)); + memset(&rev_statinfo_, 0, sizeof(tap_rtp_stat_t)); + + fwd_statinfo_.first_packet = TRUE; + rev_statinfo_.first_packet = TRUE; + fwd_statinfo_.reg_pt = PT_UNDEFINED; + rev_statinfo_.reg_pt = PT_UNDEFINED; + + ui->forwardTreeWidget->clear(); + ui->reverseTreeWidget->clear(); + + for (int i = 0; i < ui->streamGraph->graphCount(); i++) { + ui->streamGraph->graph(i)->clearData(); + } + + fwd_time_vals_.clear(); + fwd_jitter_vals_.clear(); + fwd_diff_vals_.clear(); + fwd_delta_vals_.clear(); + rev_time_vals_.clear(); + rev_jitter_vals_.clear(); + rev_diff_vals_.clear(); + rev_delta_vals_.clear(); + + fwd_tempfile_->resize(0); + rev_tempfile_->resize(0); +} + +void RtpAnalysisDialog::addPacket(bool forward, packet_info *pinfo, const _rtp_info *rtpinfo) +{ + /* add this RTP for future listening using the RTP Player*/ +// add_rtp_packet(rtpinfo, pinfo); + + if (forward) { + rtp_packet_analyse(&fwd_statinfo_, pinfo, rtpinfo); + new RtpAnalysisTreeWidgetItem(ui->forwardTreeWidget, &fwd_statinfo_, pinfo, rtpinfo); + + fwd_time_vals_.append((fwd_statinfo_.time - fwd_statinfo_.start_time) / 1000); + fwd_jitter_vals_.append(fwd_statinfo_.jitter * 1000); + fwd_diff_vals_.append(fwd_statinfo_.diff * 1000); + fwd_delta_vals_.append(fwd_statinfo_.delta * 1000); + + savePayload(fwd_tempfile_, &fwd_statinfo_, pinfo, rtpinfo); + } else { + rtp_packet_analyse(&rev_statinfo_, pinfo, rtpinfo); + new RtpAnalysisTreeWidgetItem(ui->reverseTreeWidget, &rev_statinfo_, pinfo, rtpinfo); + + rev_time_vals_.append((rev_statinfo_.time - rev_statinfo_.start_time) / 1000); + rev_jitter_vals_.append(rev_statinfo_.jitter * 1000); + rev_diff_vals_.append(rev_statinfo_.diff * 1000); + rev_delta_vals_.append(rev_statinfo_.delta * 1000); + + savePayload(rev_tempfile_, &rev_statinfo_, pinfo, rtpinfo); + } + +} + +// rtp_analysis.c:rtp_packet_save_payload +const unsigned int max_silence_ticks_ = 1000000; +const guint8 silence_pcmu_ = 0xff; +const guint8 silence_pcma_ = 0x55; +void RtpAnalysisDialog::savePayload(QTemporaryFile *tmpfile, tap_rtp_stat_t *statinfo, packet_info *pinfo, const _rtp_info *rtpinfo) +{ + /* Is this the first packet we got in this direction? */ +// if (statinfo->flags & STAT_FLAG_FIRST) { +// if (saveinfo->fp == NULL) { +// saveinfo->saved = FALSE; +// saveinfo->error_type = TAP_RTP_FILE_OPEN_ERROR; +// } else { +// saveinfo->saved = TRUE; +// } +// } + + /* Save the voice information */ + + /* If there was already an error, we quit */ + if (!tmpfile->isOpen() || tmpfile->error() != QFile::NoError) return; + + /* Quit if the captured length and packet length aren't equal or + * if the RTP dissector thinks there is some information missing + */ + if ((pinfo->fd->pkt_len != pinfo->fd->cap_len) + && (!rtpinfo->info_all_data_present)) { + tmpfile->close(); + err_str_ = tr("Can't save in a file: Wrong length of captured packets."); + return; + } + + /* If padding bit is set but the padding count is bigger + * then the whole RTP data - error with padding count + */ + if ( (rtpinfo->info_padding_set != FALSE) + && (rtpinfo->info_padding_count > rtpinfo->info_payload_len) ) { + tmpfile->close(); + err_str_ = tr("Can't save in a file: RTP data with padding."); + return; + } + + /* Do we need to insert some silence? */ + if ((rtpinfo->info_marker_set) + && ! (statinfo->flags & STAT_FLAG_FIRST) + && ! (statinfo->flags & STAT_FLAG_WRONG_TIMESTAMP) + && (statinfo->delta_timestamp > (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) ) { + /* the amount of silence should be the difference between + * the last timestamp and the current one minus x + * x should equal the amount of information in the last frame + * XXX not done yet */ + for (unsigned int i = 0; + (i < (statinfo->delta_timestamp - rtpinfo->info_payload_len - rtpinfo->info_padding_count)) + && (i < max_silence_ticks_); + i++) { + guint8 tmp; + size_t nchars; + + switch (statinfo->reg_pt) { + case PT_PCMU: + tmp = silence_pcmu_; + break; + case PT_PCMA: + tmp = silence_pcma_; + break; + default: + tmp = 0; + break; + } + nchars = tmpfile->write((char *)&tmp, 1); + if (nchars != 1) { + /* Write error or short write */ + tmpfile->close(); + err_str_ = tr("Can't save in a file: File I/O problem."); + return; + } + } + } + + + if ((rtpinfo->info_payload_type == PT_CN) + || (rtpinfo->info_payload_type == PT_CN_OLD)) { + } else { /* All other payloads */ + const char *data; + size_t nchars; + + if (!rtpinfo->info_all_data_present) { + /* Not all the data was captured. */ + tmpfile->close(); + err_str_ = tr("Can't save in a file: Not all data in all packets was captured."); + return; + } + + /* We put the pointer at the beginning of the RTP + * payload, that is, at the beginning of the RTP data + * plus the offset of the payload from the beginning + * of the RTP data */ + data = (const char *) rtpinfo->info_data + rtpinfo->info_payload_offset; + nchars = tmpfile->write(data, rtpinfo->info_payload_len - rtpinfo->info_padding_count); + if (nchars != (rtpinfo->info_payload_len - rtpinfo->info_padding_count)) { + /* Write error or short write */ + err_str_ = tr("Can't save in a file: File I/O problem."); + tmpfile->close(); + return; + } + return; + } + return; +} + +void RtpAnalysisDialog::updateStatistics() +{ + unsigned int f_clock_rate = fwd_statinfo_.clock_rate; + unsigned int r_clock_rate = rev_statinfo_.clock_rate; + unsigned int f_expected = (fwd_statinfo_.stop_seq_nr + fwd_statinfo_.cycles*65536) + - fwd_statinfo_.start_seq_nr + 1; + unsigned int r_expected = (rev_statinfo_.stop_seq_nr + rev_statinfo_.cycles*65536) + - rev_statinfo_.start_seq_nr + 1; + unsigned int f_total_nr = fwd_statinfo_.total_nr; + unsigned int r_total_nr = rev_statinfo_.total_nr; + int f_lost = f_expected - f_total_nr; + int r_lost = r_expected - r_total_nr; + double f_sumt = fwd_statinfo_.sumt; + double f_sumTS = fwd_statinfo_.sumTS; + double f_sumt2 = fwd_statinfo_.sumt2; + double f_sumtTS = fwd_statinfo_.sumtTS; + double r_sumt = rev_statinfo_.sumt; + double r_sumTS = rev_statinfo_.sumTS; + double r_sumt2 = rev_statinfo_.sumt2; + double r_sumtTS = rev_statinfo_.sumtTS; + double f_perc, r_perc; + double f_clock_drift = 1.0; + double r_clock_drift = 1.0; + double f_duration = fwd_statinfo_.time - fwd_statinfo_.start_time; + double r_duration = rev_statinfo_.time - rev_statinfo_.start_time; + + if (f_clock_rate == 0) { + f_clock_rate = 1; + } + + if (r_clock_rate == 0) { + r_clock_rate = 1; + } + + if (f_expected) { + f_perc = (double)(f_lost*100)/(double)f_expected; + } else { + f_perc = 0; + } + if (r_expected) { + r_perc = (double)(r_lost*100)/(double)r_expected; + } else { + r_perc = 0; + } + + if ((f_total_nr >0) && (f_sumt2 > 0)) { + f_clock_drift = (f_total_nr * f_sumtTS - f_sumt * f_sumTS) / (f_total_nr * f_sumt2 - f_sumt * f_sumt); + } + if ((r_total_nr >0) && (r_sumt2 > 0)) { + r_clock_drift = (r_total_nr * r_sumtTS - r_sumt * r_sumTS) / (r_total_nr * r_sumt2 - r_sumt * r_sumt); + } + + QString stats_tables = "<html><head></head><body>\n"; + stats_tables += QString("<p>%1:%2 " UTF8_LEFT_RIGHT_ARROW) + .arg(address_to_qstring(&src_fwd_, true)) + .arg(port_src_fwd_); + stats_tables += QString("<br>%1:%2</p>\n") + .arg(address_to_qstring(&dst_fwd_, true)) + .arg(port_dst_fwd_); + stats_tables += "<h4>Forward</h4>\n"; + stats_tables += "<p><table>\n"; + stats_tables += QString("<tr><th align=\"left\">SSRC</th><td>0x%1</tr>") + .arg(ssrc_fwd_, 8, 16, QChar('0')); + stats_tables += QString("<tr><th align=\"left\">Max Delta</th><td>%1 ms @ %2</td></tr>") + .arg(fwd_statinfo_.max_delta, 0, 'f', 2) + .arg(fwd_statinfo_.max_nr); + stats_tables += QString("<tr><th align=\"left\">Max Jitter</th><td>%1 ms</tr>") + .arg(fwd_statinfo_.max_jitter, 0, 'f', 2); + stats_tables += QString("<tr><th align=\"left\">Mean Jitter</th><td>%1 ms</tr>") + .arg(fwd_statinfo_.mean_jitter, 0, 'f', 2); + stats_tables += QString("<tr><th align=\"left\">Max Skew</th><td>%1 ms</tr>") + .arg(fwd_statinfo_.max_skew, 0, 'f', 2); + stats_tables += QString("<tr><th align=\"left\">RTP Packets</th><td>%1</tr>") + .arg(f_total_nr); + stats_tables += QString("<tr><th align=\"left\">Expected</th><td>%1</tr>") + .arg(f_expected); + stats_tables += QString("<tr><th align=\"left\">Lost</th><td>%1 (%2 %)</tr>") + .arg(f_lost).arg(f_perc, 0, 'f', 2); + stats_tables += QString("<tr><th align=\"left\">Seq Errs</th><td>%1</tr>") + .arg(fwd_statinfo_.sequence); + stats_tables += QString("<tr><th align=\"left\">Duration</th><td>%1 s</tr>") + .arg(f_duration / 1000.0, 0, 'f', 2); + stats_tables += QString("<tr><th align=\"left\">Clock Drift</th><td>%1 ms</tr>") + .arg(f_duration * (f_clock_drift - 1.0), 0, 'f', 0); + stats_tables += QString("<tr><th align=\"left\">Freq Drift</th><td>%1 Hz (%2 %)</tr>") // XXX Terminology? + .arg(f_clock_drift * f_clock_rate, 0, 'f', 0).arg(100.0 * (f_clock_drift - 1.0), 0, 'f', 2); + stats_tables += "</table></p>\n"; + + stats_tables += "<h4>Reverse</h4>\n"; + stats_tables += "<p><table>\n"; + stats_tables += QString("<tr><th align=\"left\">SSRC</th><td>0x%1</tr>") + .arg(ssrc_fwd_, 8, 16, QChar('0')); + stats_tables += QString("<tr><th align=\"left\">Max Delta</th><td>%1 ms @ %2</td></tr>") + .arg(rev_statinfo_.max_delta, 0, 'f', 2) + .arg(rev_statinfo_.max_nr); + stats_tables += QString("<tr><th align=\"left\">Max Jitter</th><td>%1 ms</tr>") + .arg(rev_statinfo_.max_jitter, 0, 'f', 2); + stats_tables += QString("<tr><th align=\"left\">Mean Jitter</th><td>%1 ms</tr>") + .arg(rev_statinfo_.mean_jitter, 0, 'f', 2); + stats_tables += QString("<tr><th align=\"left\">Max Skew</th><td>%1 ms</tr>") + .arg(rev_statinfo_.max_skew, 0, 'f', 2); + stats_tables += QString("<tr><th align=\"left\">RTP Packets</th><td>%1</tr>") + .arg(r_total_nr); + stats_tables += QString("<tr><th align=\"left\">Expected</th><td>%1</tr>") + .arg(r_expected); + stats_tables += QString("<tr><th align=\"left\">Lost</th><td>%1 (%2 %)</tr>") + .arg(r_lost).arg(r_perc, 0, 'f', 2); + stats_tables += QString("<tr><th align=\"left\">Seq Errs</th><td>%1</tr>") + .arg(rev_statinfo_.sequence); + stats_tables += QString("<tr><th align=\"left\">Duration</th><td>%1 s</tr>") + .arg(r_duration / 1000.0, 0, 'f', 2); + stats_tables += QString("<tr><th align=\"left\">Clock Drift</th><td>%1 ms</tr>") + .arg(r_duration * (r_clock_drift - 1.0), 0, 'f', 0); + stats_tables += QString("<tr><th align=\"left\">Freq Drift</th><td>%1 Hz (%2 %)</tr>") // XXX Terminology? + .arg(r_clock_drift * r_clock_rate, 0, 'f', 0).arg(100.0 * (r_clock_drift - 1.0), 0, 'f', 2); + stats_tables += "</table></p></body>\n"; + + ui->statisticsLabel->setText(stats_tables); + + for (int col = 0; col < ui->forwardTreeWidget->columnCount() - 1; col++) { + ui->forwardTreeWidget->resizeColumnToContents(col); + ui->reverseTreeWidget->resizeColumnToContents(col); + } + + graphs_[fwd_jitter_graph_]->setData(fwd_time_vals_, fwd_jitter_vals_); + graphs_[fwd_diff_graph_]->setData(fwd_time_vals_, fwd_diff_vals_); + graphs_[fwd_delta_graph_]->setData(fwd_time_vals_, fwd_delta_vals_); + graphs_[rev_jitter_graph_]->setData(rev_time_vals_, rev_jitter_vals_); + graphs_[rev_diff_graph_]->setData(rev_time_vals_, rev_diff_vals_); + graphs_[rev_delta_graph_]->setData(rev_time_vals_, rev_delta_vals_); + + updateGraph(); + + updateWidgets(); +} + +void RtpAnalysisDialog::updateGraph() +{ + for (int i = 0; i < ui->streamGraph->graphCount(); i++) { + if (ui->streamGraph->graph(i)->visible()) { + ui->streamGraph->graph(i)->rescaleAxes(i > 0); + } + } + ui->streamGraph->replot(); +} + +// rtp_analysis.c:copy_file +enum { save_audio_none_, save_audio_au_, save_audio_raw_ }; +void RtpAnalysisDialog::saveAudio(RtpAnalysisDialog::StreamDirection direction) +{ + if (!fwd_tempfile_->isOpen() || !rev_tempfile_->isOpen()) return; + + QString caption; + + switch (direction) { + case dir_forward_: + caption = tr("Save forward stream audio"); + break; + case dir_reverse_: + caption = tr("Save reverse stream audio"); + break; + case dir_both_: + default: + caption = tr("Save audio"); + break; + } + + QString ext_filter = tr("Sun Audio (*.au)"); + if (direction != dir_both_) { + ext_filter.append(tr(";;Raw (*.raw)")); + } + QString sel_filter; + QString file_path = QFileDialog::getSaveFileName( + this, caption, wsApp->lastOpenDir().absoluteFilePath("Saved RTP Audio.au"), + ext_filter, &sel_filter); + + if (file_path.isEmpty()) return; + + int save_format = save_audio_none_; + if (file_path.endsWith(".au")) { + save_format = save_audio_au_; + } else if (file_path.endsWith(".raw")) { + save_format = save_audio_raw_; + } + + if (save_format == save_audio_none_) { + QMessageBox::warning(this, tr("Warning"), tr("Unable to save in that format")); + return; + } + + QFile save_file(file_path); + gint16 sample; + gchar pd[4]; +// progdlg_t *progbar; +// guint32 progbar_count, progbar_quantum; +// guint32 progbar_nextstep = 0; + guint32 count = 0; + gboolean stop_flag = FALSE; + size_t nchars; + + save_file.open(QIODevice::WriteOnly); + fwd_tempfile_->seek(0); + rev_tempfile_->seek(0); + + if (save_file.error() != QFile::NoError) { + QMessageBox::warning(this, tr("Warning"), tr("Unable to save %1").arg(save_file.fileName())); + return; + } + +// progbar = create_progress_dlg(top_level, "Saving voice in a file", dest, TRUE, &stop_flag); + + if (save_format == save_audio_au_) { /* au format */ + /* First we write the .au header. XXX Hope this is endian independent */ + /* the magic word 0x2e736e64 == .snd */ + phton32(pd, 0x2e736e64); + nchars = save_file.write((const char *)pd, 4); + if (nchars != 4) + goto copy_file_err; + /* header offset == 24 bytes */ + phton32(pd, 24); + nchars = save_file.write((const char *)pd, 4); + if (nchars != 4) + goto copy_file_err; + /* total length; it is permitted to set this to 0xffffffff */ + phton32(pd, 0xffffffff); + nchars = save_file.write((const char *)pd, 4); + if (nchars != 4) + goto copy_file_err; + /* encoding format == 16-bit linear PCM */ + phton32(pd, 3); + nchars = save_file.write((const char *)pd, 4); + if (nchars != 4) + goto copy_file_err; + /* sample rate == 8000 Hz */ + phton32(pd, 8000); + nchars = save_file.write((const char *)pd, 4); + if (nchars != 4) + goto copy_file_err; + /* channels == 1 */ + phton32(pd, 1); + nchars = save_file.write((const char *)pd, 4); + if (nchars != 4) + goto copy_file_err; + + switch (direction) { + /* Only forward direction */ + case dir_forward_: + { + char f_rawvalue; +// progbar_count = user_data->forward.saveinfo.count; +// progbar_quantum = user_data->forward.saveinfo.count/100; + while (fwd_tempfile_->getChar(&f_rawvalue)) { + if (stop_flag) + break; +// if ((count > progbar_nextstep) && (count <= progbar_count)) { +// update_progress_dlg(progbar, +// (gfloat) count/progbar_count, "Saving"); +// progbar_nextstep = progbar_nextstep + progbar_quantum; +// } + count++; + + if (fwd_statinfo_.pt == PT_PCMU) { + sample = ulaw2linear((unsigned char)f_rawvalue); + phton16(pd, sample); + } else if (fwd_statinfo_.pt == PT_PCMA) { + sample = alaw2linear((unsigned char)f_rawvalue); + phton16(pd, sample); + } else { + goto copy_file_err; + } + + nchars = save_file.write((const char *)pd, 2); + if (nchars < 2) { + goto copy_file_err; + } + } + break; + } + /* Only reverse direction */ + case dir_reverse_: + { + char r_rawvalue; +// progbar_count = user_data->reversed.saveinfo.count; +// progbar_quantum = user_data->reversed.saveinfo.count/100; + while (rev_tempfile_->getChar(&r_rawvalue)) { + if (stop_flag) + break; +// if ((count > progbar_nextstep) && (count <= progbar_count)) { +// update_progress_dlg(progbar, +// (gfloat) count/progbar_count, "Saving"); +// progbar_nextstep = progbar_nextstep + progbar_quantum; +// } + count++; + + if (rev_statinfo_.pt == PT_PCMU) { + sample = ulaw2linear((unsigned char)r_rawvalue); + phton16(pd, sample); + } else if (rev_statinfo_.pt == PT_PCMA) { + sample = alaw2linear((unsigned char)r_rawvalue); + phton16(pd, sample); + } else { + goto copy_file_err; + } + + nchars = save_file.write((const char *)pd, 2); + if (nchars < 2) { + goto copy_file_err; + } + } + break; + } + /* Both directions */ + case dir_both_: + { + char f_rawvalue, r_rawvalue; + guint32 f_write_silence = 0; + guint32 r_write_silence = 0; +// if (user_data->forward.saveinfo.count > user_data->reversed.saveinfo.count) { +// progbar_count = user_data->forward.saveinfo.count; +// } else { +// progbar_count = user_data->reversed.saveinfo.count; +// } +// progbar_quantum = progbar_count/100; + /* since conversation in one way can start later than in the other one, + * we have to write some silence information for one channel */ + if (fwd_statinfo_.start_time > rev_statinfo_.start_time) { + f_write_silence = (guint32) + ((fwd_statinfo_.start_time - rev_statinfo_.start_time) + * (8000/1000)); + } else if (fwd_statinfo_.start_time < rev_statinfo_.start_time) { + r_write_silence = (guint32) + ((rev_statinfo_.start_time - fwd_statinfo_.start_time) + * (8000/1000)); + } + for (;;) { + if (stop_flag) + break; +// if ((count > progbar_nextstep) && (count <= progbar_count)) { +// update_progress_dlg(progbar, +// (gfloat) count/progbar_count, "Saving"); +// progbar_nextstep = progbar_nextstep + progbar_quantum; +// } + count++; + if (f_write_silence > 0) { + rev_tempfile_->getChar(&r_rawvalue); + switch (fwd_statinfo_.reg_pt) { + case PT_PCMU: + f_rawvalue = silence_pcmu_; + break; + case PT_PCMA: + f_rawvalue = silence_pcma_; + break; + default: + f_rawvalue = 0; + break; + } + f_write_silence--; + } else if (r_write_silence > 0) { + fwd_tempfile_->getChar(&f_rawvalue); + switch (rev_statinfo_.reg_pt) { + case PT_PCMU: + r_rawvalue = silence_pcmu_; + break; + case PT_PCMA: + r_rawvalue = silence_pcma_; + break; + default: + r_rawvalue = 0; + break; + } + r_write_silence--; + } else { + fwd_tempfile_->getChar(&f_rawvalue); + rev_tempfile_->getChar(&r_rawvalue); + } + if (fwd_tempfile_->atEnd() && rev_tempfile_->atEnd()) + break; + if ((fwd_statinfo_.pt == PT_PCMU) + && (rev_statinfo_.pt == PT_PCMU)) { + sample = (ulaw2linear((unsigned char)r_rawvalue) + + ulaw2linear((unsigned char)f_rawvalue)) / 2; + phton16(pd, sample); + } + else if ((fwd_statinfo_.pt == PT_PCMA) + && (rev_statinfo_.pt == PT_PCMA)) { + sample = (alaw2linear((unsigned char)r_rawvalue) + + alaw2linear((unsigned char)f_rawvalue)) / 2; + phton16(pd, sample); + } else { + goto copy_file_err; + } + + nchars = save_file.write((const char *)pd, 2); + if (nchars < 2) { + goto copy_file_err; + } + } + } + } + } else if (save_format == save_audio_raw_) { /* raw format */ + QFile *tempfile; + switch (direction) { + /* Only forward direction */ + case dir_forward_: { +// progbar_count = user_data->forward.saveinfo.count; +// progbar_quantum = user_data->forward.saveinfo.count/100; + tempfile = fwd_tempfile_; + break; + } + /* only reversed direction */ + case dir_reverse_: { +// progbar_count = user_data->reversed.saveinfo.count; +// progbar_quantum = user_data->reversed.saveinfo.count/100; + tempfile = rev_tempfile_; + break; + } + default: { + goto copy_file_err; + } + } + + int chunk_size = 65536; + /* XXX how do you just copy the file? */ + while (chunk_size > 0) { + if (stop_flag) + break; + QByteArray bytes = tempfile->read(chunk_size); +// if ((count > progbar_nextstep) && (count <= progbar_count)) { +// update_progress_dlg(progbar, +// (gfloat) count/progbar_count, "Saving"); +// progbar_nextstep = progbar_nextstep + progbar_quantum; +// } +// count++; + + if (!save_file.write(bytes)) { + goto copy_file_err; + } + chunk_size = bytes.length(); + } + } + +// ret_val = TRUE; +// goto copy_file_xit; + +copy_file_err: + return; +// ret_val = FALSE; +// goto copy_file_xit; + +//copy_file_xit: +// destroy_progress_dlg(progbar); +// fclose(forw_stream); +// fclose(rev_stream); +// fclose(to_stream); + // return ret_val; +} + +// XXX The GTK+ UI saves the length and timestamp. +void RtpAnalysisDialog::saveCsv(RtpAnalysisDialog::StreamDirection direction) +{ + QString caption; + + switch (direction) { + case dir_forward_: + caption = tr("Save forward stream CSV"); + break; + case dir_reverse_: + caption = tr("Save reverse stream CSV"); + break; + case dir_both_: + default: + caption = tr("Save CSV"); + break; + } + + QString file_path = QFileDialog::getSaveFileName( + this, caption, wsApp->lastOpenDir().absoluteFilePath("RTP Packet Data.csv"), + tr("Comma-separated values (*.csv)")); + + if (file_path.isEmpty()) return; + + QFile save_file(file_path); + save_file.open(QFile::WriteOnly); + + if (direction == dir_forward_ || direction == dir_both_) { + save_file.write("Forward\n"); + + for (int row = 0; row < ui->forwardTreeWidget->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->forwardTreeWidget->topLevelItem(row); + if (ti->type() != rtp_analysis_type_) continue; + RtpAnalysisTreeWidgetItem *ra_ti = dynamic_cast<RtpAnalysisTreeWidgetItem *>((RtpAnalysisTreeWidgetItem *)ti); + QStringList values; + foreach (QVariant v, ra_ti->rowData()) { + if (!v.isValid()) { + values << "\"\""; + } else if ((int) v.type() == (int) QMetaType::QString) { + values << QString("\"%1\"").arg(v.toString()); + } else { + values << v.toString(); + } + } + save_file.write(values.join(",").toUtf8()); + save_file.write("\n"); + } + } + if (direction == dir_both_) { + save_file.write("\n"); + } + if (direction == dir_reverse_ || direction == dir_both_) { + save_file.write("Reverse\n"); + + for (int row = 0; row < ui->forwardTreeWidget->topLevelItemCount(); row++) { + QTreeWidgetItem *ti = ui->forwardTreeWidget->topLevelItem(row); + if (ti->type() != rtp_analysis_type_) continue; + RtpAnalysisTreeWidgetItem *ra_ti = dynamic_cast<RtpAnalysisTreeWidgetItem *>((RtpAnalysisTreeWidgetItem *)ti); + QStringList values; + foreach (QVariant v, ra_ti->rowData()) { + if (!v.isValid()) { + values << "\"\""; + } else if ((int) v.type() == (int) QMetaType::QString) { + values << QString("\"%1\"").arg(v.toString()); + } else { + values << v.toString(); + } + } + save_file.write(values.join(",").toUtf8()); + save_file.write("\n"); + } + } +} + +// Adapted from rtp_analysis.c:process_node +guint32 RtpAnalysisDialog::processNode(proto_node *ptree_node, header_field_info *hfinformation, const gchar *proto_field, bool *ok) +{ + field_info *finfo; + proto_node *proto_sibling_node; + header_field_info *hfssrc; + ipv4_addr *ipv4; + + finfo = PNODE_FINFO(ptree_node); + + /* Caller passed top of the protocol tree. Expected child node */ + g_assert(finfo); + + if (hfinformation == (finfo->hfinfo)) { + hfssrc = proto_registrar_get_byname(proto_field); + if (hfssrc == NULL) { + return 0; + } + for (ptree_node = ptree_node->first_child; + ptree_node != NULL; + ptree_node = ptree_node->next) { + finfo = PNODE_FINFO(ptree_node); + if (hfssrc == finfo->hfinfo) { + guint32 result; + if (hfinformation->type == FT_IPv4) { + ipv4 = (ipv4_addr *)fvalue_get(&finfo->value); + result = ipv4_get_net_order_addr(ipv4); + } else { + result = fvalue_get_uinteger(&finfo->value); + } + if (ok) *ok = true; + return result; + } + } + if (!ptree_node) { + return 0; + } + } + + proto_sibling_node = ptree_node->next; + + if (proto_sibling_node) { + return processNode(proto_sibling_node, hfinformation, proto_field, ok); + } else { + return 0; + } +} + +// Adapted from rtp_analysis.c:get_int_value_from_proto_tree +guint32 RtpAnalysisDialog::getIntFromProtoTree(proto_tree *protocol_tree, const gchar *proto_name, const gchar *proto_field, bool *ok) +{ + proto_node *ptree_node; + header_field_info *hfinformation; + + if (ok) *ok = false; + + hfinformation = proto_registrar_get_byname(proto_name); + if (hfinformation == NULL) { + return 0; + } + + ptree_node = ((proto_node *)protocol_tree)->first_child; + if (!ptree_node) { + return 0; + } + + return processNode(ptree_node, hfinformation, proto_field, ok); +} + +bool RtpAnalysisDialog::eventFilter(QObject *, QEvent *event) +{ + if (event->type() != QEvent::KeyPress) return false; + + QKeyEvent *kevt = static_cast<QKeyEvent *>(event); + + switch(kevt->key()) { + case Qt::Key_G: + on_actionGoToPacket_triggered(); + return true; + case Qt::Key_N: + on_actionNextProblem_triggered(); + return true; + default: + break; + } + return false; +} + +void RtpAnalysisDialog::graphClicked(QMouseEvent *event) +{ + updateWidgets(); + if (event->button() == Qt::RightButton) { + graph_ctx_menu_.exec(event->globalPos()); + } +} + +void RtpAnalysisDialog::showStreamMenu(QPoint pos) +{ + QTreeWidget *cur_tree = qobject_cast<QTreeWidget *>(ui->tabWidget->currentWidget()); + if (!cur_tree) return; + + updateWidgets(); + stream_ctx_menu_.popup(cur_tree->viewport()->mapToGlobal(pos)); +} + +/* + * 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_analysis_dialog.h b/ui/qt/rtp_analysis_dialog.h new file mode 100644 index 0000000000..99f9bd2f64 --- /dev/null +++ b/ui/qt/rtp_analysis_dialog.h @@ -0,0 +1,156 @@ +/* rtp_analysis_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_ANALYSIS_DIALOG_H +#define RTP_ANALYSIS_DIALOG_H + +#include <config.h> + +#include <glib.h> + +#include "epan/address.h" + +#include "ui/rtp_analysis.h" +#include "ui/rtp_stream.h" + +#include <QAbstractButton> +#include <QMenu> + +#include "wireshark_dialog.h" + +namespace Ui { +class RtpAnalysisDialog; +} + +class QCPGraph; +class QTemporaryFile; + +class RtpAnalysisDialog : public WiresharkDialog +{ + Q_OBJECT + +public: + explicit RtpAnalysisDialog(QWidget &parent, CaptureFile &cf); + ~RtpAnalysisDialog(); + +signals: + void goToPacket(int packet_num); + +protected: + virtual void reject(); + +protected slots: + virtual void updateWidgets(); + +private slots: + void on_actionGoToPacket_triggered(); + void on_actionNextProblem_triggered(); + void on_fJitterCheckBox_toggled(bool checked); + void on_fDiffCheckBox_toggled(bool checked); + void on_fDeltaCheckBox_toggled(bool checked); + void on_rJitterCheckBox_toggled(bool checked); + void on_rDiffCheckBox_toggled(bool checked); + void on_rDeltaCheckBox_toggled(bool checked); + void on_actionSaveAudio_triggered(); + void on_actionSaveForwardAudio_triggered(); + void on_actionSaveReverseAudio_triggered(); + void on_actionSaveCsv_triggered(); + void on_actionSaveForwardCsv_triggered(); + void on_actionSaveReverseCsv_triggered(); + void on_actionSaveGraph_triggered(); + void on_buttonBox_helpRequested(); + void showStreamMenu(QPoint pos); + void graphClicked(QMouseEvent *event); + +private: + Ui::RtpAnalysisDialog *ui; + enum StreamDirection { dir_both_, dir_forward_, dir_reverse_ }; + + address src_fwd_; + guint32 port_src_fwd_; + address dst_fwd_; + guint32 port_dst_fwd_; + guint32 ssrc_fwd_; + address src_rev_; + guint32 port_src_rev_; + address dst_rev_; + guint32 port_dst_rev_; + guint32 ssrc_rev_; + int num_streams_; + + tap_rtp_stat_t fwd_statinfo_; + tap_rtp_stat_t rev_statinfo_; + + QTemporaryFile *fwd_tempfile_; + QTemporaryFile *rev_tempfile_; + + // Graph data for QCustomPlot + QList<QCPGraph *>graphs_; + QVector<double> fwd_time_vals_; + QVector<double> fwd_jitter_vals_; + QVector<double> fwd_diff_vals_; + QVector<double> fwd_delta_vals_; + + QVector<double> rev_time_vals_; + QVector<double> rev_jitter_vals_; + QVector<double> rev_diff_vals_; + QVector<double> rev_delta_vals_; + + rtpstream_tapinfo_t tapinfo_; + QString err_str_; + + QMenu stream_ctx_menu_; + QMenu graph_ctx_menu_; + + // Tap callbacks + static void tapReset(void *tapinfo_ptr); + static gboolean tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *rtpinfo_ptr); + static void tapDraw(void *tapinfo_ptr); + + void resetStatistics(); + void addPacket(bool forward, packet_info *pinfo, const struct _rtp_info *rtpinfo); + void savePayload(QTemporaryFile *tmpfile, tap_rtp_stat_t *statinfo, packet_info *pinfo, const struct _rtp_info *rtpinfo); + void updateStatistics(); + void updateGraph(); + + void saveAudio(StreamDirection direction); + void saveCsv(StreamDirection direction); + + guint32 processNode(proto_node *ptree_node, header_field_info *hfinformation, const gchar* proto_field, bool *ok); + guint32 getIntFromProtoTree(proto_tree *protocol_tree, const gchar *proto_name, const gchar *proto_field, bool *ok); + + bool eventFilter(QObject*, QEvent* event); +}; + +#endif // RTP_ANALYSIS_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_analysis_dialog.ui b/ui/qt/rtp_analysis_dialog.ui new file mode 100644 index 0000000000..a593e37693 --- /dev/null +++ b/ui/qt/rtp_analysis_dialog.ui @@ -0,0 +1,346 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>RtpAnalysisDialog</class> + <widget class="QDialog" name="RtpAnalysisDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>650</width> + <height>475</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="statisticsLabel"> + <property name="text"> + <string><html><head/><body><p><span style=" font-size:medium; font-weight:600;">Forward</span></p><p><span style=" font-size:medium; font-weight:600;">Reverse</span></p></body></html></string> + </property> + <property name="textFormat"> + <enum>Qt::RichText</enum> + </property> + <property name="textInteractionFlags"> + <set>Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse</set> + </property> + </widget> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QTreeWidget" name="forwardTreeWidget"> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <property name="itemsExpandable"> + <bool>false</bool> + </property> + <property name="expandsOnDoubleClick"> + <bool>false</bool> + </property> + <attribute name="title"> + <string>Forward</string> + </attribute> + <column> + <property name="text"> + <string>Packet</string> + </property> + </column> + <column> + <property name="text"> + <string>Sequence</string> + </property> + </column> + <column> + <property name="text"> + <string>Delta (ms)</string> + </property> + </column> + <column> + <property name="text"> + <string>Jitter</string> + </property> + </column> + <column> + <property name="text"> + <string>Skew</string> + </property> + </column> + <column> + <property name="text"> + <string>Bandwidth</string> + </property> + </column> + <column> + <property name="text"> + <string>Marker</string> + </property> + </column> + <column> + <property name="text"> + <string>Status</string> + </property> + </column> + </widget> + <widget class="QTreeWidget" name="reverseTreeWidget"> + <property name="rootIsDecorated"> + <bool>false</bool> + </property> + <property name="itemsExpandable"> + <bool>false</bool> + </property> + <attribute name="title"> + <string>Reverse</string> + </attribute> + <column> + <property name="text"> + <string notr="true">1</string> + </property> + </column> + </widget> + <widget class="QWidget" name="graphTab"> + <attribute name="title"> + <string>Graph</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_2" stretch="1,0,0"> + <item> + <widget class="QCustomPlot" name="streamGraph" native="true"/> + </item> + <item> + <layout class="QHBoxLayout" name="forwardHorizontalLayout"> + <item> + <widget class="QCheckBox" name="fJitterCheckBox"> + <property name="toolTip"> + <string><html><head/><body><p>Show or hide forward jitter values.</p></body></html></string> + </property> + <property name="text"> + <string>Forward Jitter</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="fDiffCheckBox"> + <property name="toolTip"> + <string><html><head/><body><p>Show or hide forward difference values.</p></body></html></string> + </property> + <property name="text"> + <string>Forward Difference</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="fDeltaCheckBox"> + <property name="toolTip"> + <string><html><head/><body><p>Show or hide forward delta values.</p></body></html></string> + </property> + <property name="text"> + <string>Forward Delta</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="reverseHorizontalLayout"> + <item> + <widget class="QCheckBox" name="rJitterCheckBox"> + <property name="toolTip"> + <string><html><head/><body><p>Show or hide reverse jitter values.</p></body></html></string> + </property> + <property name="text"> + <string>Reverse Jitter</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="rDiffCheckBox"> + <property name="toolTip"> + <string><html><head/><body><p>Show or hide reverse difference values.</p></body></html></string> + </property> + <property name="text"> + <string>Reverse Difference</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="rDeltaCheckBox"> + <property name="toolTip"> + <string><html><head/><body><p>Show or hide reverse delta values.</p></body></html></string> + </property> + <property name="text"> + <string>Reverse Delta</string> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </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|QDialogButtonBox::Save</set> + </property> + </widget> + </item> + </layout> + <action name="actionSaveAudio"> + <property name="text"> + <string>Audio</string> + </property> + <property name="toolTip"> + <string>Save the audio data for both channels.</string> + </property> + </action> + <action name="actionSaveForwardAudio"> + <property name="text"> + <string>Forward Stream Audio</string> + </property> + <property name="toolTip"> + <string>Save the forward stream audio data.</string> + </property> + </action> + <action name="actionSaveReverseAudio"> + <property name="text"> + <string>Reverse Stream Audio</string> + </property> + <property name="toolTip"> + <string>Save the reverse stream audio data.</string> + </property> + </action> + <action name="actionSaveCsv"> + <property name="text"> + <string>CSV</string> + </property> + <property name="toolTip"> + <string>Save both tables as CSV.</string> + </property> + </action> + <action name="actionSaveForwardCsv"> + <property name="text"> + <string>Forward Stream CSV</string> + </property> + <property name="toolTip"> + <string>Save the forward table as CSV.</string> + </property> + </action> + <action name="actionSaveReverseCsv"> + <property name="text"> + <string>Reverse Stream CSV</string> + </property> + <property name="toolTip"> + <string>Save the reverse table as CSV.</string> + </property> + </action> + <action name="actionSaveGraph"> + <property name="text"> + <string>Save Graph</string> + </property> + <property name="toolTip"> + <string>Save the graph image.</string> + </property> + </action> + <action name="actionGoToPacket"> + <property name="text"> + <string>Go to Packet</string> + </property> + <property name="toolTip"> + <string>Select the corresponding packet in the packet list.</string> + </property> + <property name="shortcut"> + <string>G</string> + </property> + </action> + <action name="actionNextProblem"> + <property name="text"> + <string>Next Problem Packet</string> + </property> + <property name="toolTip"> + <string>Go to the next problem packet</string> + </property> + <property name="shortcut"> + <string>N</string> + </property> + </action> + </widget> + <customwidgets> + <customwidget> + <class>QCustomPlot</class> + <extends>QWidget</extends> + <header>qcustomplot.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>RtpAnalysisDialog</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>RtpAnalysisDialog</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/rtp_stream_dialog.cpp b/ui/qt/rtp_stream_dialog.cpp index 0685bcaf8e..9cbe8c86b7 100644 --- a/ui/qt/rtp_stream_dialog.cpp +++ b/ui/qt/rtp_stream_dialog.cpp @@ -316,7 +316,7 @@ bool RtpStreamDialog::eventFilter(QObject *, QEvent *event) void RtpStreamDialog::tapDraw(rtpstream_tapinfo_t *tapinfo) { - RtpStreamDialog *rtp_stream_dialog = static_cast<RtpStreamDialog *>(tapinfo->tap_data); + RtpStreamDialog *rtp_stream_dialog = dynamic_cast<RtpStreamDialog *>((RtpStreamDialog *)tapinfo->tap_data); if (rtp_stream_dialog) { rtp_stream_dialog->updateStreams(); } @@ -326,9 +326,8 @@ void RtpStreamDialog::tapMarkPacket(rtpstream_tapinfo_t *tapinfo, frame_data *fd { if (!tapinfo) return; - RtpStreamDialog *rtp_stream_dialog = static_cast<RtpStreamDialog *>(tapinfo->tap_data); + RtpStreamDialog *rtp_stream_dialog = dynamic_cast<RtpStreamDialog *>((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; } diff --git a/ui/qt/syntax_line_edit.cpp b/ui/qt/syntax_line_edit.cpp index 282d969edd..08cb23327d 100644 --- a/ui/qt/syntax_line_edit.cpp +++ b/ui/qt/syntax_line_edit.cpp @@ -92,7 +92,7 @@ void SyntaxLineEdit::setSyntaxState(SyntaxState state) { .arg("palette(text)") // Foreground .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 + .arg(ColorUtils::fromColorT(&prefs.gui_text_deprecated).name()) // Deprecated ; setStyleSheet(style_sheet_); } diff --git a/ui/rtp_analysis.h b/ui/rtp_analysis.h index a10d628c95..65dae42699 100644 --- a/ui/rtp_analysis.h +++ b/ui/rtp_analysis.h @@ -39,6 +39,10 @@ * @todo what's this? */ +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + void rtp_analysis( address *ip_src_fwd, guint32 port_src_fwd, @@ -88,7 +92,7 @@ typedef struct _tap_rtp_stat_t { double sumt2; double sumtTS; double time; /**< Unit is ms */ - double start_time; + double start_time; /**< Unit is ms */ double lastnominaltime; double max_delta; double max_jitter; @@ -127,6 +131,10 @@ extern int rtp_packet_analyse(tap_rtp_stat_t *statinfo, packet_info *pinfo, const struct _rtp_info *rtpinfo); +#ifdef __cplusplus +} +#endif /* __cplusplus */ + #endif /* __RTP_ANALYSIS_H__ */ /* diff --git a/ui/tap-rtp-common.c b/ui/tap-rtp-common.c index d260a0f1a4..a95e261b50 100644 --- a/ui/tap-rtp-common.c +++ b/ui/tap-rtp-common.c @@ -4,7 +4,7 @@ * Copyright 2008, Ericsson AB * By Balint Reczey <balint.reczey@ericsson.com> * - * most functions are copied from ui/gtk/rtp_stream.c and ui/gtk/rtp_analisys.c + * most functions are copied from ui/gtk/rtp_stream.c and ui/gtk/rtp_analysis.c * Copyright 2003, Alcatel Business Systems * By Lars Ruoff <lars.ruoff@gmx.net> * diff --git a/ui/tap-rtp-common.h b/ui/tap-rtp-common.h index a4242c794e..239180383c 100644 --- a/ui/tap-rtp-common.h +++ b/ui/tap-rtp-common.h @@ -27,14 +27,42 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef TAP_RTP_COMMON_H_INCLUDED -#define TAP_RTP_COMMON_H_INCLUDED +#ifndef __TAP_RTP_COMMON_H__ +#define __TAP_RTP_COMMON_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* type of error when saving voice in a file didn't succeed */ +typedef enum { + TAP_RTP_WRONG_CODEC, + TAP_RTP_WRONG_LENGTH, + TAP_RTP_PADDING_ERROR, + TAP_RTP_SHORT_FRAME, + TAP_RTP_FILE_OPEN_ERROR, + TAP_RTP_FILE_WRITE_ERROR, + TAP_RTP_NO_DATA +} error_type_t; + +typedef struct _tap_rtp_save_info_t { + FILE *fp; + guint32 count; + error_type_t error_type; + gboolean saved; +} tap_rtp_save_info_t; + +struct _rtp_stream_info; void rtpstream_reset_cb(void*); -void rtp_write_header(rtp_stream_info_t*, FILE*); +void rtp_write_header(struct _rtp_stream_info*, FILE*); int rtpstream_packet(void*, packet_info*, epan_dissect_t *, const void *); -#endif /*TAP_RTP_COMMON_H_INCLUDED*/ +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __TAP_RTP_COMMON_H__ */ /* * Editor modelines - http://www.wireshark.org/tools/modelines.html diff --git a/ui/utf8_entities.h b/ui/utf8_entities.h index 84d8bfd76e..05285d5517 100644 --- a/ui/utf8_entities.h +++ b/ui/utf8_entities.h @@ -31,16 +31,19 @@ * and other places */ -#define UTF8_MIDDLE_DOT "\xc2\xb7" /* 183 / 0xb7 */ +#define UTF8_MIDDLE_DOT "\xc2\xb7" /* 183 / 0xb7 */ -#define UTF8_BULLET "\xe2\x80\xa2" /* 8226 / 0x2024 */ -#define UTF8_EM_DASH "\xe2\x80\x94" /* 8212 / 0x2014 */ -#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 */ -#define UTF8_LEFT_RIGHT_ARROW "\xe2\x86\x94" /* 8596 / 0x2194 */ +#define UTF8_BULLET "\xe2\x80\xa2" /* 8226 / 0x2024 */ +#define UTF8_EM_DASH "\xe2\x80\x94" /* 8212 / 0x2014 */ +#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 */ +#define UTF8_LEFT_RIGHT_ARROW "\xe2\x86\x94" /* 8596 / 0x2194 */ -#define UTF8_PLACE_OF_INTEREST_SIGN "\xe2\x8c\x98" /* 8984 / 0x2318 */ +#define UTF8_PLACE_OF_INTEREST_SIGN "\xe2\x8c\x98" /* 8984 / 0x2318 */ + +#define UTF8_CHECK_MARK "\xe2\x9c\x93" /* 10003 / 0x2713 */ +#define UTF8_BALLOT_X "\xe2\x9c\x97" /* 10007 / 0x2717 */ #endif /* __UTF8_ENTITIES_H__ */ /* |