summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGerald Combs <gerald@wireshark.org>2015-02-02 09:01:29 -0800
committerGerald Combs <gerald@wireshark.org>2015-07-16 00:30:14 +0000
commitd2acb04b4cadd90bf09bdc4959e6bffb25dcfd38 (patch)
tree44079a225b9df1ca5dbb72d58b55ff2112c15cc4
parent2e215db532f408697ba16f2d83524a64cc9e6424 (diff)
downloadwireshark-d2acb04b4cadd90bf09bdc4959e6bffb25dcfd38.tar.gz
Add the RTP Stream Analysis dialog.
Combine the GTK+ RTP Stream Analysis and RTP Graph Analysis dialogs into one. Yell at the user less. Disable the Analyze RTP Stream menu item if we don't have an RTP stream selected. There are a *lot* of moving parts in this dialog. I've tested with the few RTP captures I have but it's by no means complete. "To do" items are listed at the top of rtp_analysis.cpp. Change-Id: Id503977f069bebc46cc68bc749f0c9cbf4d37bf6 Reviewed-on: https://code.wireshark.org/review/9650 Petri-Dish: Gerald Combs <gerald@wireshark.org> Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org> Reviewed-by: Gerald Combs <gerald@wireshark.org>
-rw-r--r--epan/proto.c5
-rw-r--r--epan/proto.h3
-rw-r--r--ui/gtk/main_menubar.c2
-rw-r--r--ui/gtk/rtp_analysis.c27
-rw-r--r--ui/qt/CMakeLists.txt3
-rw-r--r--ui/qt/Makefile.am4
-rw-r--r--ui/qt/Makefile.common4
-rw-r--r--ui/qt/Wireshark.pro3
-rw-r--r--ui/qt/color_utils.cpp20
-rw-r--r--ui/qt/color_utils.h1
-rw-r--r--ui/qt/follow_stream_dialog.cpp2
-rw-r--r--ui/qt/io_graph_dialog.cpp18
-rw-r--r--ui/qt/main_window.cpp2
-rw-r--r--ui/qt/main_window.h1
-rw-r--r--ui/qt/main_window.ui9
-rw-r--r--ui/qt/main_window_slots.cpp14
-rw-r--r--ui/qt/packet_list.cpp2
-rw-r--r--ui/qt/qt_ui_utils.cpp6
-rw-r--r--ui/qt/qt_ui_utils.h3
-rw-r--r--ui/qt/rtp_analysis_dialog.cpp1538
-rw-r--r--ui/qt/rtp_analysis_dialog.h156
-rw-r--r--ui/qt/rtp_analysis_dialog.ui346
-rw-r--r--ui/qt/rtp_stream_dialog.cpp5
-rw-r--r--ui/qt/syntax_line_edit.cpp2
-rw-r--r--ui/rtp_analysis.h10
-rw-r--r--ui/tap-rtp-common.c2
-rw-r--r--ui/tap-rtp-common.h36
-rw-r--r--ui/utf8_entities.h19
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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:medium; font-weight:600;&quot;&gt;Forward&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style=&quot; font-size:medium; font-weight:600;&quot;&gt;Reverse&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show or hide forward jitter values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Forward Jitter</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="fDiffCheckBox">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show or hide forward difference values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Forward Difference</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="fDeltaCheckBox">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show or hide forward delta values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show or hide reverse jitter values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Reverse Jitter</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="rDiffCheckBox">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show or hide reverse difference values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string>Reverse Difference</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="rDeltaCheckBox">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show or hide reverse delta values.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</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>&lt;small&gt;&lt;i&gt;A hint.&lt;/i&gt;&lt;/small&gt;</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Close|QDialogButtonBox::Help|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__ */
/*