aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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__ */
/*