aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorj.novak@netsystem.cz <j.novak@netsystem.cz>2021-04-07 05:40:09 +0000
committerWireshark GitLab Utility <6629907-ws-gitlab-utility@users.noreply.gitlab.com>2021-04-07 05:40:09 +0000
commit2b072b8e76c37f9db67a9f37e4b964c9eadeec8e (patch)
tree45be6df3e8cba219e1a924de95cde0e9c21b665a
parentfe7bf413fca8e5aca1e5124a34fe09a2f819be9c (diff)
RTP Stream Analysis: Process more streams than two, audio save removed
-rw-r--r--ui/qt/main_window.h9
-rw-r--r--ui/qt/main_window_slots.cpp72
-rw-r--r--ui/qt/rtp_analysis_dialog.cpp1539
-rw-r--r--ui/qt/rtp_analysis_dialog.h119
-rw-r--r--ui/qt/rtp_analysis_dialog.ui344
-rw-r--r--ui/qt/rtp_player_dialog.cpp1
-rw-r--r--ui/qt/rtp_player_dialog.h2
-rw-r--r--ui/qt/rtp_stream_dialog.cpp46
-rw-r--r--ui/qt/rtp_stream_dialog.h11
-rw-r--r--ui/qt/utils/stock_icon.cpp73
-rw-r--r--ui/qt/utils/stock_icon.h3
11 files changed, 745 insertions, 1474 deletions
diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h
index cad88cf6cd..3a8353c6fa 100644
--- a/ui/qt/main_window.h
+++ b/ui/qt/main_window.h
@@ -77,6 +77,7 @@
#include <ui/qt/models/pref_models.h>
#include "rtp_stream_dialog.h"
#include "voip_calls_dialog.h"
+#include "rtp_analysis_dialog.h"
class AccordionFrame;
class ByteViewTab;
@@ -250,6 +251,7 @@ private:
QPointer<VoipCallsDialog> voip_calls_dialog_; // Singleton pattern used
QPointer<VoipCallsDialog> sip_calls_dialog_; // Singleton pattern used
QPointer<RtpPlayerDialog> rtp_player_dialog_; // Singleton pattern used
+ QPointer<RtpAnalysisDialog> rtp_analysis_dialog_; // Singleton pattern used
void freeze();
void thaw();
@@ -312,9 +314,6 @@ signals:
void framesSelected(QList<int>);
void captureActive(int);
- void replaceRtpStreams(QVector<rtpstream_info_t *> stream_infos);
- void addRtpStreams(QVector<rtpstream_info_t *> stream_infos);
- void removeRtpStreams(QVector<rtpstream_info_t *> stream_infos);
void selectRtpStream(rtpstream_id_t *id);
void deselectRtpStream(rtpstream_id_t *id);
@@ -367,6 +366,9 @@ public slots:
void rtpPlayerDialogReplaceRtpStreams(QVector<rtpstream_info_t *> stream_infos);
void rtpPlayerDialogAddRtpStreams(QVector<rtpstream_info_t *> stream_infos);
void rtpPlayerDialogRemoveRtpStreams(QVector<rtpstream_info_t *> stream_infos);
+ void rtpAnalysisDialogReplaceRtpStreams(QVector<rtpstream_info_t *> stream_infos);
+ void rtpAnalysisDialogAddRtpStreams(QVector<rtpstream_info_t *> stream_infos);
+ void rtpAnalysisDialogRemoveRtpStreams(QVector<rtpstream_info_t *> stream_infos);
void rtpStreamsDialogSelectRtpStream(rtpstream_id_t *id);
void rtpStreamsDialogDeselectRtpStream(rtpstream_id_t *id);
@@ -686,6 +688,7 @@ private slots:
void openTelephonyRtpStreamsDialog();
void openTelephonyRtpPlayerDialog();
void openTelephonyVoipCallsDialog(bool all_flows);
+ void openTelephonyRtpAnalysisDialog();
void on_actionTelephonyVoipCalls_triggered();
void on_actionTelephonyGsmMapSummary_triggered();
void statCommandLteMacStatistics(const char *arg, void *);
diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp
index 51de8657fc..5bec70ed5b 100644
--- a/ui/qt/main_window_slots.cpp
+++ b/ui/qt/main_window_slots.cpp
@@ -3311,12 +3311,6 @@ void MainWindow::openTelephonyRtpPlayerDialog()
connect(rtp_player_dialog_, SIGNAL(goToPacket(int)),
packet_list_, SLOT(goToPacket(int)));
- connect(this, SIGNAL(replaceRtpStreams(QVector<rtpstream_info_t *>)),
- rtp_player_dialog_, SLOT(replaceRtpStreams(QVector<rtpstream_info_t *>)));
- connect(this, SIGNAL(addRtpStreams(QVector<rtpstream_info_t *>)),
- rtp_player_dialog_, SLOT(addRtpStreams(QVector<rtpstream_info_t *>)));
- connect(this, SIGNAL(removeRtpStreams(QVector<rtpstream_info_t *>)),
- rtp_player_dialog_, SLOT(removeRtpStreams(QVector<rtpstream_info_t *>)));
}
rtp_player_dialog_->show();
}
@@ -3356,6 +3350,23 @@ void MainWindow::openTelephonyVoipCallsDialog(bool all_flows)
dlg->show();
}
+void MainWindow::openTelephonyRtpAnalysisDialog()
+{
+ if (!rtp_analysis_dialog_) {
+ rtp_analysis_dialog_ = new RtpAnalysisDialog(*this, capture_file_);
+
+ connect(rtp_analysis_dialog_, SIGNAL(goToPacket(int)),
+ packet_list_, SLOT(goToPacket(int)));
+ connect(rtp_analysis_dialog_, SIGNAL(rtpPlayerDialogReplaceRtpStreams(QVector<rtpstream_info_t *>)),
+ this, SLOT(rtpPlayerDialogReplaceRtpStreams(QVector<rtpstream_info_t *>)));
+ connect(rtp_analysis_dialog_, SIGNAL(rtpPlayerDialogAddRtpStreams(QVector<rtpstream_info_t *>)),
+ this, SLOT(rtpPlayerDialogAddRtpStreams(QVector<rtpstream_info_t *>)));
+ connect(rtp_analysis_dialog_, SIGNAL(rtpPlayerDialogRemoveRtpStreams(QVector<rtpstream_info_t *>)),
+ this, SLOT(rtpPlayerDialogRemoveRtpStreams(QVector<rtpstream_info_t *>)));
+ }
+ rtp_analysis_dialog_->show();
+}
+
void MainWindow::on_actionTelephonyVoipCalls_triggered()
{
openTelephonyVoipCallsDialog(false);
@@ -3447,7 +3458,6 @@ void MainWindow::openTelephonyRtpStreamsDialog()
{
if (!rtp_stream_dialog_) {
rtp_stream_dialog_ = new RtpStreamDialog(*this, capture_file_);
-
connect(rtp_stream_dialog_, SIGNAL(packetsMarked()),
packet_list_, SLOT(redrawVisiblePackets()));
connect(rtp_stream_dialog_, SIGNAL(goToPacket(int)),
@@ -3462,8 +3472,12 @@ void MainWindow::openTelephonyRtpStreamsDialog()
this, SLOT(rtpPlayerDialogAddRtpStreams(QVector<rtpstream_info_t *>)));
connect(rtp_stream_dialog_, SIGNAL(rtpPlayerDialogRemoveRtpStreams(QVector<rtpstream_info_t *>)),
this, SLOT(rtpPlayerDialogRemoveRtpStreams(QVector<rtpstream_info_t *>)));
- connect(this, SIGNAL(selectRtpStream(rtpstream_id_t *)), rtp_stream_dialog_, SLOT(selectRtpStream(rtpstream_id_t *)));
- connect(this, SIGNAL(deselectRtpStream(rtpstream_id_t *)), rtp_stream_dialog_, SLOT(deselectRtpStream(rtpstream_id_t *)));
+ connect(rtp_stream_dialog_, SIGNAL(rtpAnalysisDialogReplaceRtpStreams(QVector<rtpstream_info_t *>)),
+ this, SLOT(rtpAnalysisDialogReplaceRtpStreams(QVector<rtpstream_info_t *>)));
+ connect(rtp_stream_dialog_, SIGNAL(rtpAnalysisDialogAddRtpStreams(QVector<rtpstream_info_t *>)),
+ this, SLOT(rtpAnalysisDialogAddRtpStreams(QVector<rtpstream_info_t *>)));
+ connect(rtp_stream_dialog_, SIGNAL(rtpAnalysisDialogRemoveRtpStreams(QVector<rtpstream_info_t *>)),
+ this, SLOT(rtpAnalysisDialogRemoveRtpStreams(QVector<rtpstream_info_t *>)));
}
rtp_stream_dialog_->show();
}
@@ -3475,16 +3489,8 @@ void MainWindow::on_actionTelephonyRtpStreams_triggered()
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)));
- connect(rtp_analysis_dialog, SIGNAL(rtpPlayerDialogReplaceRtpStreams(QVector<rtpstream_info_t *>)),
- this, SLOT(rtpPlayerDialogReplaceRtpStreams(QVector<rtpstream_info_t *>)));
- connect(rtp_analysis_dialog, SIGNAL(rtpPlayerDialogAddRtpStreams(QVector<rtpstream_info_t *>)),
- this, SLOT(rtpPlayerDialogAddRtpStreams(QVector<rtpstream_info_t *>)));
- connect(rtp_analysis_dialog, SIGNAL(rtpPlayerDialogRemoveRtpStreams(QVector<rtpstream_info_t *>)),
- this, SLOT(rtpPlayerDialogRemoveRtpStreams(QVector<rtpstream_info_t *>)));
- rtp_analysis_dialog->show();
+ openTelephonyRtpAnalysisDialog();
+ rtp_analysis_dialog_->findRtpStreams();
}
void MainWindow::on_actionTelephonyRTSPPacketCounter_triggered()
@@ -4061,31 +4067,49 @@ void MainWindow::activatePluginIFToolbar(bool)
void MainWindow::rtpPlayerDialogReplaceRtpStreams(QVector<rtpstream_info_t *> stream_infos)
{
openTelephonyRtpPlayerDialog();
- emit replaceRtpStreams(stream_infos);
+ rtp_player_dialog_->replaceRtpStreams(stream_infos);
}
void MainWindow::rtpPlayerDialogAddRtpStreams(QVector<rtpstream_info_t *> stream_infos)
{
openTelephonyRtpPlayerDialog();
- emit addRtpStreams(stream_infos);
+ rtp_player_dialog_->addRtpStreams(stream_infos);
}
void MainWindow::rtpPlayerDialogRemoveRtpStreams(QVector<rtpstream_info_t *> stream_infos)
{
openTelephonyRtpPlayerDialog();
- emit removeRtpStreams(stream_infos);
+ rtp_player_dialog_->removeRtpStreams(stream_infos);
+}
+
+void MainWindow::rtpAnalysisDialogReplaceRtpStreams(QVector<rtpstream_info_t *> stream_infos)
+{
+ openTelephonyRtpAnalysisDialog();
+ rtp_analysis_dialog_->replaceRtpStreams(stream_infos);
+}
+
+void MainWindow::rtpAnalysisDialogAddRtpStreams(QVector<rtpstream_info_t *> stream_infos)
+{
+ openTelephonyRtpAnalysisDialog();
+ rtp_analysis_dialog_->addRtpStreams(stream_infos);
+}
+
+void MainWindow::rtpAnalysisDialogRemoveRtpStreams(QVector<rtpstream_info_t *> stream_infos)
+{
+ openTelephonyRtpAnalysisDialog();
+ rtp_analysis_dialog_->removeRtpStreams(stream_infos);
}
void MainWindow::rtpStreamsDialogSelectRtpStream(rtpstream_id_t *id)
{
openTelephonyRtpStreamsDialog();
- emit selectRtpStream(id);
+ rtp_stream_dialog_->selectRtpStream(id);
}
void MainWindow::rtpStreamsDialogDeselectRtpStream(rtpstream_id_t *id)
{
openTelephonyRtpStreamsDialog();
- emit deselectRtpStream(id);
+ rtp_stream_dialog_->deselectRtpStream(id);
}
#ifdef _MSC_VER
diff --git a/ui/qt/rtp_analysis_dialog.cpp b/ui/qt/rtp_analysis_dialog.cpp
index 7951f4a74c..c2c7b2427c 100644
--- a/ui/qt/rtp_analysis_dialog.cpp
+++ b/ui/qt/rtp_analysis_dialog.cpp
@@ -14,6 +14,7 @@
#include "frame_tvbuff.h"
#include "epan/epan_dissect.h"
+#include <epan/addr_resolv.h>
#include "epan/rtp_pt.h"
#include "epan/dfilter/dfilter.h"
@@ -31,7 +32,12 @@
#include <QMessageBox>
#include <QPushButton>
-#include <QTemporaryFile>
+#include <QVBoxLayout>
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QPushButton>
+#include <QWidget>
+#include <QCheckBox>
#include <ui/qt/utils/color_utils.h>
#include <ui/qt/utils/qt_ui_utils.h>
@@ -233,151 +239,54 @@ enum {
num_graphs_
};
-RtpAnalysisDialog::RtpAnalysisDialog(QWidget &parent, CaptureFile &cf, rtpstream_info_t *stream_fwd, rtpstream_info_t *stream_rev) :
+RtpAnalysisDialog::RtpAnalysisDialog(QWidget &parent, CaptureFile &cf, rtpstream_info_t *stream_fwd _U_, rtpstream_info_t *stream_rev _U_) :
WiresharkDialog(parent, cf),
ui(new Ui::RtpAnalysisDialog),
- num_streams_(0),
- save_payload_error_(TAP_RTP_NO_ERROR)
+ tab_seq(0)
{
ui->setupUi(this);
loadGeometry(parent.width() * 4 / 5, parent.height() * 4 / 5);
setWindowSubtitle(tr("RTP Stream Analysis"));
+ // Used when tab contains IPs
+ //ui->tabWidget->setStyleSheet("QTabBar::tab { height: 7ex; }");
+ ui->tabWidget->tabBar()->setTabsClosable(true);
ui->progressFrame->hide();
stream_ctx_menu_.addAction(ui->actionGoToPacket);
stream_ctx_menu_.addAction(ui->actionNextProblem);
stream_ctx_menu_.addSeparator();
- stream_ctx_menu_.addAction(ui->actionSaveAudioUnsync);
- stream_ctx_menu_.addAction(ui->actionSaveForwardAudioUnsync);
- stream_ctx_menu_.addAction(ui->actionSaveReverseAudioUnsync);
- stream_ctx_menu_.addSeparator();
- stream_ctx_menu_.addAction(ui->actionSaveAudioSyncStream);
- stream_ctx_menu_.addAction(ui->actionSaveForwardAudioSyncStream);
- stream_ctx_menu_.addAction(ui->actionSaveReverseAudioSyncStream);
- stream_ctx_menu_.addSeparator();
- stream_ctx_menu_.addAction(ui->actionSaveAudioSyncFile);
- stream_ctx_menu_.addAction(ui->actionSaveForwardAudioSyncFile);
- stream_ctx_menu_.addAction(ui->actionSaveReverseAudioSyncFile);
- 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_.addAction(ui->actionSaveOneCsv);
+ stream_ctx_menu_.addAction(ui->actionSaveAllCsv);
stream_ctx_menu_.addSeparator();
stream_ctx_menu_.addAction(ui->actionSaveGraph);
set_action_shortcuts_visible_in_context_menu(stream_ctx_menu_.actions());
- ui->forwardTreeWidget->installEventFilter(this);
- ui->forwardTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
- ui->forwardTreeWidget->header()->setSortIndicator(0, Qt::AscendingOrder);
- connect(ui->forwardTreeWidget, SIGNAL(customContextMenuRequested(QPoint)),
- SLOT(showStreamMenu(QPoint)));
- ui->reverseTreeWidget->installEventFilter(this);
- ui->reverseTreeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
- ui->reverseTreeWidget->header()->setSortIndicator(0, Qt::AscendingOrder);
- 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(&fwd_statinfo_, 0, sizeof(fwd_statinfo_));
- memset(&rev_statinfo_, 0, sizeof(rev_statinfo_));
-
- 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::graphColor(i)));
- graph->setName(graph_cbs[i]->text());
- graphs_ << graph;
- graph_cbs[i]->setChecked(true);
- graph_cbs[i]->setIcon(StockIcon::colorIcon(ColorUtils::graphColor(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->actionSaveAudioUnsync->setEnabled(false);
- ui->actionSaveForwardAudioUnsync->setEnabled(false);
- ui->actionSaveReverseAudioUnsync->setEnabled(false);
- ui->actionSaveAudioSyncStream->setEnabled(false);
- ui->actionSaveForwardAudioSyncStream->setEnabled(false);
- ui->actionSaveReverseAudioSyncStream->setEnabled(false);
- ui->actionSaveAudioSyncFile->setEnabled(false);
- ui->actionSaveForwardAudioSyncFile->setEnabled(false);
- ui->actionSaveReverseAudioSyncFile->setEnabled(false);
- }
-
player_button_ = RtpPlayerDialog::addPlayerButton(ui->buttonBox, this);
QPushButton *export_btn = ui->buttonBox->addButton(ui->actionExportButton->text(), QDialogButtonBox::ActionRole);
export_btn->setToolTip(ui->actionExportButton->toolTip());
QMenu *save_menu = new QMenu(export_btn);
- save_menu->addAction(ui->actionSaveAudioUnsync);
- save_menu->addAction(ui->actionSaveForwardAudioUnsync);
- save_menu->addAction(ui->actionSaveReverseAudioUnsync);
- save_menu->addSeparator();
- save_menu->addAction(ui->actionSaveAudioSyncStream);
- save_menu->addAction(ui->actionSaveForwardAudioSyncStream);
- save_menu->addAction(ui->actionSaveReverseAudioSyncStream);
- save_menu->addSeparator();
- save_menu->addAction(ui->actionSaveAudioSyncFile);
- save_menu->addAction(ui->actionSaveForwardAudioSyncFile);
- save_menu->addAction(ui->actionSaveReverseAudioSyncFile);
- save_menu->addSeparator();
- save_menu->addAction(ui->actionSaveCsv);
- save_menu->addAction(ui->actionSaveForwardCsv);
- save_menu->addAction(ui->actionSaveReverseCsv);
+ save_menu->addAction(ui->actionSaveOneCsv);
+ save_menu->addAction(ui->actionSaveAllCsv);
save_menu->addSeparator();
save_menu->addAction(ui->actionSaveGraph);
export_btn->setMenu(save_menu);
- if (stream_fwd) { // XXX What if stream_fwd == 0 && stream_rev != 0?
- rtpstream_info_copy_deep(&fwd_statinfo_, stream_fwd);
- num_streams_=1;
- if (stream_rev) {
- rtpstream_info_copy_deep(&rev_statinfo_, stream_rev);
- num_streams_=2;
- }
- } else {
- findStreams();
- }
-
- if (err_str_.isEmpty() && num_streams_ < 1) {
- err_str_ = tr("No streams found.");
- }
-
- registerTapListener("rtp", this, NULL, 0, tapReset, tapPacket, tapDraw);
- cap_file_.retapPackets();
- removeTapListeners();
-
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(ui->tabWidget->tabBar(), SIGNAL(tabCloseRequested(int)),
+ this, SLOT(closeTab(int)));
+
updateWidgets();
updateStatistics();
@@ -386,35 +295,216 @@ RtpAnalysisDialog::RtpAnalysisDialog(QWidget &parent, CaptureFile &cf, rtpstream
RtpAnalysisDialog::~RtpAnalysisDialog()
{
delete ui;
-// remove_tap_listener_rtpstream(&tapinfo_);
- rtpstream_info_free_data(&fwd_statinfo_);
- rtpstream_info_free_data(&rev_statinfo_);
- delete fwd_tempfile_;
- delete rev_tempfile_;
+ for(int i=0; i<tabs_.count(); i++) {
+ deleteTabInfo(tabs_[i]);
+ g_free(tabs_[i]);
+ }
+}
+
+void RtpAnalysisDialog::deleteTabInfo(tab_info_t *tab_info)
+{
+ delete tab_info->time_vals;
+ delete tab_info->jitter_vals;
+ delete tab_info->diff_vals;
+ delete tab_info->delta_vals;
+ // tab_info->tree_widget was deleted by ui
+ // tab_info->statistics_label was deleted by ui
+ rtpstream_info_free_data(&tab_info->stream);
+}
+
+int RtpAnalysisDialog::addTabUI(tab_info_t *new_tab)
+{
+ int new_tab_no;
+ rtpstream_info_calc_t s_calc;
+ rtpstream_info_calculate(&new_tab->stream, &s_calc);
+ new_tab->tab_name = new QString(QString("%1:%2 " UTF8_RIGHTWARDS_ARROW "\n%3:%4\n(%5)")
+ .arg(s_calc.src_addr_str)
+ .arg(s_calc.src_port)
+ .arg(s_calc.dst_addr_str)
+ .arg(s_calc.dst_port)
+ .arg(int_to_qstring(s_calc.ssrc, 8, 16)));
+
+ QWidget *tab = new QWidget();
+ tab->setProperty("tab_data", QVariant::fromValue((void *)new_tab));
+ QHBoxLayout *horizontalLayout = new QHBoxLayout(tab);
+ QVBoxLayout *verticalLayout = new QVBoxLayout();
+ new_tab->statistics_label = new QLabel();
+ //new_tab->statistics_label->setStyleSheet("QLabel { color : blue; }");
+ new_tab->statistics_label->setTextFormat(Qt::RichText);
+ new_tab->statistics_label->setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse);
+
+ verticalLayout->addWidget(new_tab->statistics_label);
+
+ QSpacerItem *verticalSpacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
+
+ verticalLayout->addItem(verticalSpacer);
+
+ horizontalLayout->addLayout(verticalLayout);
+
+ new_tab->tree_widget = new QTreeWidget();
+ new_tab->tree_widget->setRootIsDecorated(false);
+ new_tab->tree_widget->setUniformRowHeights(true);
+ new_tab->tree_widget->setItemsExpandable(false);
+ new_tab->tree_widget->setSortingEnabled(true);
+ new_tab->tree_widget->setExpandsOnDoubleClick(false);
+
+ new_tab->tree_widget->installEventFilter(this);
+ new_tab->tree_widget->setContextMenuPolicy(Qt::CustomContextMenu);
+ new_tab->tree_widget->header()->setSortIndicator(0, Qt::AscendingOrder);
+ connect(new_tab->tree_widget, SIGNAL(customContextMenuRequested(QPoint)),
+ SLOT(showStreamMenu(QPoint)));
+ connect(new_tab->tree_widget, SIGNAL(itemSelectionChanged()),
+ this, SLOT(updateWidgets()));
+
+ QTreeWidgetItem *ti = new_tab->tree_widget->headerItem();
+ ti->setText(packet_col_, tr("Packet"));
+ ti->setText(sequence_col_, tr("Sequence"));
+ ti->setText(delta_col_, tr("Delta (ms)"));
+ ti->setText(jitter_col_, tr("Jitter (ms)"));
+ ti->setText(skew_col_, tr("Skew"));
+ ti->setText(bandwidth_col_, tr("Bandwidth"));
+ ti->setText(marker_col_, tr("Marker"));
+ ti->setText(status_col_, tr("Status"));
+
+ QColor color = ColorUtils::graphColor(tab_seq++);
+ ui->tabWidget->setUpdatesEnabled(false);
+ horizontalLayout->addWidget(new_tab->tree_widget);
+ new_tab_no = ui->tabWidget->count() - 1;
+ // Used when tab contains IPs
+ //ui->tabWidget->insertTab(new_tab_no, tab, *new_tab->tab_name);
+ ui->tabWidget->insertTab(new_tab_no, tab, QString(tr("Stream %1")).arg(tab_seq - 1));
+ ui->tabWidget->tabBar()->setTabTextColor(new_tab_no, color);
+ ui->tabWidget->tabBar()->setTabToolTip(new_tab_no, *new_tab->tab_name);
+ ui->tabWidget->setUpdatesEnabled(true);
+
+ QPen pen = QPen(color);
+ QCPScatterStyle jitter_shape;
+ QCPScatterStyle diff_shape;
+ QCPScatterStyle delta_shape;
+ jitter_shape.setShape(QCPScatterStyle::ssCircle);
+ //jitter_shape.setSize(5);
+ diff_shape.setShape(QCPScatterStyle::ssCross);
+ //diff_shape.setSize(5);
+ delta_shape.setShape(QCPScatterStyle::ssTriangle);
+ //delta_shape.setSize(5);
+
+ new_tab->jitter_graph = ui->streamGraph->addGraph();
+ new_tab->diff_graph = ui->streamGraph->addGraph();
+ new_tab->delta_graph = ui->streamGraph->addGraph();
+ new_tab->jitter_graph->setPen(pen);
+ new_tab->diff_graph->setPen(pen);
+ new_tab->delta_graph->setPen(pen);
+ new_tab->jitter_graph->setScatterStyle(jitter_shape);
+ new_tab->diff_graph->setScatterStyle(diff_shape);
+ new_tab->delta_graph->setScatterStyle(delta_shape);
+
+ new_tab->graphHorizontalLayout = new QHBoxLayout();
+
+ new_tab->stream_checkbox = new QCheckBox(tr("Stream %1").arg(tab_seq - 1), ui->graphTab);
+ new_tab->stream_checkbox->setChecked(true);
+ new_tab->stream_checkbox->setIcon(StockIcon::colorIcon(color.rgb(), QPalette::Text));
+ new_tab->graphHorizontalLayout->addWidget(new_tab->stream_checkbox);
+ new_tab->graphHorizontalLayout->addItem(new QSpacerItem(10, 5, QSizePolicy::Expanding, QSizePolicy::Minimum));
+ connect(new_tab->stream_checkbox, SIGNAL(stateChanged(int)),
+ this, SLOT(rowCheckboxChanged(int)));
+
+ new_tab->jitter_checkbox = new QCheckBox(tr("Stream %1 Forward Jitter").arg(tab_seq - 1), ui->graphTab);
+ new_tab->jitter_checkbox->setChecked(true);
+ new_tab->jitter_checkbox->setIcon(StockIcon::colorIconCircle(color.rgb(), QPalette::Text));
+ new_tab->graphHorizontalLayout->addWidget(new_tab->jitter_checkbox);
+ new_tab->graphHorizontalLayout->addItem(new QSpacerItem(10, 5, QSizePolicy::Expanding, QSizePolicy::Minimum));
+ connect(new_tab->jitter_checkbox, SIGNAL(stateChanged(int)),
+ this, SLOT(singleCheckboxChanged(int)));
+
+ new_tab->diff_checkbox = new QCheckBox(tr("Stream %1 Forward Difference").arg(tab_seq - 1), ui->graphTab);
+ new_tab->diff_checkbox->setChecked(true);
+ new_tab->diff_checkbox->setIcon(StockIcon::colorIconCross(color.rgb(), QPalette::Text));
+ new_tab->graphHorizontalLayout->addWidget(new_tab->diff_checkbox);
+ new_tab->graphHorizontalLayout->addItem(new QSpacerItem(10, 5, QSizePolicy::Expanding, QSizePolicy::Minimum));
+ connect(new_tab->diff_checkbox, SIGNAL(stateChanged(int)),
+ this, SLOT(singleCheckboxChanged(int)));
+
+ new_tab->delta_checkbox = new QCheckBox(tr("Stream %1 Forward Delta").arg(tab_seq - 1), ui->graphTab);
+ new_tab->delta_checkbox->setChecked(true);
+ new_tab->delta_checkbox->setIcon(StockIcon::colorIconTriangle(color.rgb(), QPalette::Text));
+ new_tab->graphHorizontalLayout->addWidget(new_tab->delta_checkbox);
+ new_tab->graphHorizontalLayout->addItem(new QSpacerItem(10, 5, QSizePolicy::Expanding, QSizePolicy::Minimum));
+ connect(new_tab->delta_checkbox, SIGNAL(stateChanged(int)),
+ this, SLOT(singleCheckboxChanged(int)));
+
+ new_tab->graphHorizontalLayout->setStretch(6, 1);
+
+ ui->verticalLayout_2->addLayout(new_tab->graphHorizontalLayout);
+
+ return new_tab_no;
+}
+
+// Handles all row checkBoxes
+void RtpAnalysisDialog::rowCheckboxChanged(int checked)
+{
+ QObject *obj = sender();
+
+ // Find correct tab data
+ for(int i=0; i<tabs_.count(); i++) {
+ tab_info_t *tab = tabs_[i];
+ if (obj == tab->stream_checkbox) {
+ // Set new state for all checkboxes on row
+ Qt::CheckState new_state;
+
+ if (checked) {
+ new_state = Qt::Checked;
+ } else {
+ new_state = Qt::Unchecked;
+ }
+ tab->jitter_checkbox->setCheckState(new_state);
+ tab->diff_checkbox->setCheckState(new_state);
+ tab->delta_checkbox->setCheckState(new_state);
+ break;
+ }
+ }
+}
+
+// Handles all single CheckBoxes
+void RtpAnalysisDialog::singleCheckboxChanged(int checked)
+{
+ QObject *obj = sender();
+
+ // Find correct tab data
+ for(int i=0; i<tabs_.count(); i++) {
+ tab_info_t *tab = tabs_[i];
+ if (obj == tab->jitter_checkbox) {
+ tab->jitter_graph->setVisible(checked);
+ updateGraph();
+ break;
+ } else if (obj == tab->diff_checkbox) {
+ tab->diff_graph->setVisible(checked);
+ updateGraph();
+ break;
+ } else if (obj == tab->delta_checkbox) {
+ tab->delta_graph->setVisible(checked);
+ updateGraph();
+ break;
+ }
+ }
}
void RtpAnalysisDialog::updateWidgets()
{
bool enable_tab = false;
+ bool enable_nav = false;
QString hint = err_str_;
- if (hint.isEmpty()) {
- enable_tab = true;
- hint = tr("%1 streams found.").arg(num_streams_);
- } else if (save_payload_error_ != TAP_RTP_NO_ERROR) {
- /* We cannot save the payload but can still display the widget
- or save CSV data */
+ if ((!file_closed_) &&
+ (tabs_.count() > 0)) {
enable_tab = true;
}
- 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))) {
+ if ((!file_closed_) &&
+ (tabs_.count() > 0) &&
+ (ui->tabWidget->currentIndex() < (ui->tabWidget->count()-1))) {
enable_nav = true;
}
+
ui->actionGoToPacket->setEnabled(enable_nav);
ui->actionNextProblem->setEnabled(enable_nav);
@@ -422,26 +512,13 @@ void RtpAnalysisDialog::updateWidgets()
hint.append(tr(" G: Go to packet, N: Next problem packet"));
}
- bool enable_save_fwd_audio = fwd_statinfo_.rtp_stats.total_nr && (save_payload_error_ == TAP_RTP_NO_ERROR) && !file_closed_;
- bool enable_save_rev_audio = rev_statinfo_.rtp_stats.total_nr && (save_payload_error_ == TAP_RTP_NO_ERROR) && !file_closed_;
- ui->actionSaveAudioUnsync->setEnabled(enable_save_fwd_audio && enable_save_rev_audio);
- ui->actionSaveForwardAudioUnsync->setEnabled(enable_save_fwd_audio);
- ui->actionSaveReverseAudioUnsync->setEnabled(enable_save_rev_audio);
- ui->actionSaveAudioSyncStream->setEnabled(enable_save_fwd_audio && enable_save_rev_audio);
- ui->actionSaveForwardAudioSyncStream->setEnabled(enable_save_fwd_audio && enable_save_rev_audio);
- ui->actionSaveReverseAudioSyncStream->setEnabled(enable_save_fwd_audio && enable_save_rev_audio);
- ui->actionSaveAudioSyncFile->setEnabled(enable_save_fwd_audio && enable_save_rev_audio);
- ui->actionSaveForwardAudioSyncFile->setEnabled(enable_save_fwd_audio);
- ui->actionSaveReverseAudioSyncFile->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->actionExportButton->setEnabled(enable_tab);
+ ui->actionSaveOneCsv->setEnabled(enable_nav);
+ ui->actionSaveAllCsv->setEnabled(enable_tab);
+ ui->actionSaveGraph->setEnabled(enable_tab);
#if defined(QT_MULTIMEDIA_LIB)
- player_button_->setEnabled(num_streams_ > 0 && !file_closed_);
+ player_button_->setEnabled(enable_tab);
#endif
ui->tabWidget->setEnabled(enable_tab);
@@ -454,8 +531,10 @@ void RtpAnalysisDialog::updateWidgets()
void RtpAnalysisDialog::on_actionGoToPacket_triggered()
{
- if (file_closed_) return;
- QTreeWidget *cur_tree = qobject_cast<QTreeWidget *>(ui->tabWidget->currentWidget());
+ tab_info_t *tab_data = getTabInfoForCurrentTab();
+ if (!tab_data) return;
+
+ QTreeWidget *cur_tree = tab_data->tree_widget;
if (!cur_tree || cur_tree->selectedItems().length() < 1) return;
QTreeWidgetItem *ti = cur_tree->selectedItems()[0];
@@ -467,7 +546,10 @@ void RtpAnalysisDialog::on_actionGoToPacket_triggered()
void RtpAnalysisDialog::on_actionNextProblem_triggered()
{
- QTreeWidget *cur_tree = qobject_cast<QTreeWidget *>(ui->tabWidget->currentWidget());
+ tab_info_t *tab_data = getTabInfoForCurrentTab();
+ if (!tab_data) return;
+
+ QTreeWidget *cur_tree = tab_data->tree_widget;
if (!cur_tree || cur_tree->topLevelItemCount() < 2) return;
// Choose convenience over correctness.
@@ -491,100 +573,14 @@ void RtpAnalysisDialog::on_actionNextProblem_triggered()
}
}
-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_actionSaveAudioUnsync_triggered()
-{
- saveAudio(dir_both_, sync_unsync_);
-}
-
-void RtpAnalysisDialog::on_actionSaveForwardAudioUnsync_triggered()
-{
- saveAudio(dir_forward_, sync_unsync_);
-}
-
-void RtpAnalysisDialog::on_actionSaveReverseAudioUnsync_triggered()
+void RtpAnalysisDialog::on_actionSaveOneCsv_triggered()
{
- saveAudio(dir_reverse_, sync_unsync_);
+ saveCsv(dir_one_);
}
-void RtpAnalysisDialog::on_actionSaveAudioSyncStream_triggered()
+void RtpAnalysisDialog::on_actionSaveAllCsv_triggered()
{
- saveAudio(dir_both_, sync_sync_stream_);
-}
-
-void RtpAnalysisDialog::on_actionSaveForwardAudioSyncStream_triggered()
-{
- saveAudio(dir_forward_, sync_sync_stream_);
-}
-
-void RtpAnalysisDialog::on_actionSaveReverseAudioSyncStream_triggered()
-{
- saveAudio(dir_reverse_, sync_sync_stream_);
-}
-
-void RtpAnalysisDialog::on_actionSaveAudioSyncFile_triggered()
-{
- saveAudio(dir_both_, sync_sync_file_);
-}
-
-void RtpAnalysisDialog::on_actionSaveForwardAudioSyncFile_triggered()
-{
- saveAudio(dir_forward_, sync_sync_file_);
-}
-
-void RtpAnalysisDialog::on_actionSaveReverseAudioSyncFile_triggered()
-{
- saveAudio(dir_reverse_, sync_sync_file_);
-}
-
-void RtpAnalysisDialog::on_actionSaveCsv_triggered()
-{
- saveCsv(dir_both_);
-}
-
-void RtpAnalysisDialog::on_actionSaveForwardCsv_triggered()
-{
- saveCsv(dir_forward_);
-}
-
-void RtpAnalysisDialog::on_actionSaveReverseCsv_triggered()
-{
- saveCsv(dir_reverse_);
+ saveCsv(dir_all_);
}
void RtpAnalysisDialog::on_actionSaveGraph_triggered()
@@ -661,15 +657,15 @@ tap_packet_status RtpAnalysisDialog::tapPacket(void *tapinfo_ptr, packet_info *p
else if (rtpinfo->info_version != 2)
return TAP_PACKET_DONT_REDRAW;
/* is it the forward direction? */
- else if (rtpstream_id_equal_pinfo_rtp_info(&(rtp_analysis_dialog->fwd_statinfo_.id),pinfo,rtpinfo)) {
-
- rtp_analysis_dialog->addPacket(true, pinfo, rtpinfo);
+ else {
+ for(int i=0; i<rtp_analysis_dialog->tabs_.count(); i++) {
+ tab_info_t *tab = rtp_analysis_dialog->tabs_[i];
+ if (rtpstream_id_equal_pinfo_rtp_info(&(tab->stream.id),pinfo,rtpinfo)) {
+ rtp_analysis_dialog->addPacket(tab, pinfo, rtpinfo);
+ }
+ }
}
- /* is it the reversed direction? */
- else if (rtpstream_id_equal_pinfo_rtp_info(&(rtp_analysis_dialog->rev_statinfo_.id),pinfo,rtpinfo)) {
- rtp_analysis_dialog->addPacket(false, pinfo, rtpinfo);
- }
return TAP_PACKET_DONT_REDRAW;
}
@@ -677,263 +673,97 @@ 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_.rtp_stats, 0, sizeof(fwd_statinfo_.rtp_stats));
- memset(&rev_statinfo_.rtp_stats, 0, sizeof(rev_statinfo_.rtp_stats));
-
- fwd_statinfo_.rtp_stats.first_packet = true;
- rev_statinfo_.rtp_stats.first_packet = true;
- fwd_statinfo_.rtp_stats.reg_pt = PT_UNDEFINED;
- rev_statinfo_.rtp_stats.reg_pt = PT_UNDEFINED;
-
- ui->forwardTreeWidget->clear();
- ui->reverseTreeWidget->clear();
+ for(int i=0; i<tabs_.count(); i++) {
+ tab_info_t *tab = tabs_[i];
+ memset(&tab->stream.rtp_stats, 0, sizeof(tab->stream.rtp_stats));
+
+ tab->stream.rtp_stats.first_packet = true;
+ tab->stream.rtp_stats.reg_pt = PT_UNDEFINED;
+ tab->time_vals->clear();
+ tab->jitter_vals->clear();
+ tab->diff_vals->clear();
+ tab->delta_vals->clear();
+ tab->tree_widget->clear();
+ }
for (int i = 0; i < ui->streamGraph->graphCount(); i++) {
ui->streamGraph->graph(i)->data()->clear();
}
-
- 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)
+void RtpAnalysisDialog::addPacket(tab_info_t *tab, packet_info *pinfo, const _rtp_info *rtpinfo)
{
- /* add this RTP for future listening using the RTP Player*/
-// add_rtp_packet(rtpinfo, pinfo);
-
- if (forward) {
- rtppacket_analyse(&fwd_statinfo_.rtp_stats, pinfo, rtpinfo);
- new RtpAnalysisTreeWidgetItem(ui->forwardTreeWidget, &fwd_statinfo_.rtp_stats, pinfo, rtpinfo);
-
- fwd_time_vals_.append(fwd_statinfo_.rtp_stats.time / 1000);
- fwd_jitter_vals_.append(fwd_statinfo_.rtp_stats.jitter);
- fwd_diff_vals_.append(fwd_statinfo_.rtp_stats.diff);
- fwd_delta_vals_.append(fwd_statinfo_.rtp_stats.delta);
-
- savePayload(fwd_tempfile_, &fwd_statinfo_.rtp_stats, pinfo, rtpinfo);
- } else {
- rtppacket_analyse(&rev_statinfo_.rtp_stats, pinfo, rtpinfo);
- new RtpAnalysisTreeWidgetItem(ui->reverseTreeWidget, &rev_statinfo_.rtp_stats, pinfo, rtpinfo);
-
- rev_time_vals_.append(rev_statinfo_.rtp_stats.time / 1000);
- rev_jitter_vals_.append(rev_statinfo_.rtp_stats.jitter);
- rev_diff_vals_.append(rev_statinfo_.rtp_stats.diff);
- rev_delta_vals_.append(rev_statinfo_.rtp_stats.delta);
-
- savePayload(rev_tempfile_, &rev_statinfo_.rtp_stats, pinfo, rtpinfo);
- }
-
-}
-
-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.");
- save_payload_error_ = TAP_RTP_WRONG_LENGTH;
- 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.");
- save_payload_error_ = TAP_RTP_PADDING_ERROR;
- return;
- }
-
- if ((rtpinfo->info_payload_type == PT_CN) ||
- (rtpinfo->info_payload_type == PT_CN_OLD)) {
- } else { /* All other payloads */
- const char *data;
- int64_t nchars;
- tap_rtp_save_data_t save_data;
-
- 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.");
- save_payload_error_ = TAP_RTP_WRONG_LENGTH;
- 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;
-
- /* Store information about timestamp, payload_type and payload in file */
- save_data.timestamp = statinfo->timestamp;
- save_data.payload_type = rtpinfo->info_payload_type;
- save_data.payload_len = rtpinfo->info_payload_len - rtpinfo->info_padding_count;
- nchars = tmpfile->write((char *)&save_data, sizeof(save_data));
- if (nchars != sizeof(save_data)) {
- /* Write error or short write */
- err_str_ = tr("Can't save in a file: File I/O problem.");
- save_payload_error_ = TAP_RTP_FILE_WRITE_ERROR;
- tmpfile->close();
- return;
- }
- if (save_data.payload_len > 0) {
- nchars = tmpfile->write(data, save_data.payload_len);
- if ((size_t)nchars != save_data.payload_len) {
- /* Write error or short write */
- err_str_ = tr("Can't save in a file: File I/O problem.");
- save_payload_error_ = TAP_RTP_FILE_WRITE_ERROR;
- tmpfile->close();
- return;
- }
- }
- return;
- }
- return;
+ rtppacket_analyse(&tab->stream.rtp_stats, pinfo, rtpinfo);
+ new RtpAnalysisTreeWidgetItem(tab->tree_widget, &tab->stream.rtp_stats, pinfo, rtpinfo);
+ tab->time_vals->append(tab->stream.rtp_stats.time / 1000);
+ tab->jitter_vals->append(tab->stream.rtp_stats.jitter);
+ tab->diff_vals->append(tab->stream.rtp_stats.diff);
+ tab->delta_vals->append(tab->stream.rtp_stats.delta);
}
void RtpAnalysisDialog::updateStatistics()
{
- rtpstream_info_calc_t f_calc;
- rtpstream_info_calc_t r_calc;
-
- rtpstream_info_calculate(&fwd_statinfo_, &f_calc);
- rtpstream_info_calculate(&rev_statinfo_, &r_calc);
-
- QString stats_tables = "<html><head><style>td{vertical-align:bottom;}</style></head><body>\n";
- stats_tables += "<h4>Forward</h4>\n";
- stats_tables += QString("<p>%1:%2 " UTF8_RIGHTWARDS_ARROW)
- .arg(f_calc.src_addr_str)
- .arg(f_calc.src_port);
- stats_tables += QString("<br>%1:%2</p>\n")
- .arg(f_calc.dst_addr_str)
- .arg(f_calc.dst_port);
- stats_tables += "<p><table>\n";
- stats_tables += QString("<tr><th align=\"left\">SSRC</th><td>%1</td></tr>")
- .arg(int_to_qstring(f_calc.ssrc, 8, 16));
- stats_tables += QString("<tr><th align=\"left\">Max Delta</th><td>%1 ms @ %2</td></tr>")
- .arg(f_calc.max_delta, 0, 'f', prefs.gui_decimal_places3)
- .arg(f_calc.last_packet_num);
- stats_tables += QString("<tr><th align=\"left\">Max Jitter</th><td>%1 ms</td></tr>")
- .arg(f_calc.max_jitter, 0, 'f', prefs.gui_decimal_places3);
- stats_tables += QString("<tr><th align=\"left\">Mean Jitter</th><td>%1 ms</td></tr>")
- .arg(f_calc.mean_jitter, 0, 'f', prefs.gui_decimal_places3);
- stats_tables += QString("<tr><th align=\"left\">Max Skew</th><td>%1 ms</td></tr>")
- .arg(f_calc.max_skew, 0, 'f', prefs.gui_decimal_places3);
- stats_tables += QString("<tr><th align=\"left\">RTP Packets</th><td>%1</td></tr>")
- .arg(f_calc.total_nr);
- stats_tables += QString("<tr><th align=\"left\">Expected</th><td>%1</td></tr>")
- .arg(f_calc.packet_expected);
- stats_tables += QString("<tr><th align=\"left\">Lost</th><td>%1 (%2 %)</td></tr>")
- .arg(f_calc.lost_num).arg(f_calc.lost_perc, 0, 'f', prefs.gui_decimal_places1);
- stats_tables += QString("<tr><th align=\"left\">Seq Errs</th><td>%1</td></tr>")
- .arg(f_calc.sequence_err);
- stats_tables += QString("<tr><th align=\"left\">Start at</th><td>%1 s @ %2</td></tr>")
- .arg(f_calc.start_time_ms, 0, 'f', 6)
- .arg(f_calc.first_packet_num);
- stats_tables += QString("<tr><th align=\"left\">Duration</th><td>%1 s</td></tr>")
- .arg(f_calc.duration_ms, 0, 'f', prefs.gui_decimal_places1);
- stats_tables += QString("<tr><th align=\"left\">Clock Drift</th><td>%1 ms</td></tr>")
- .arg(f_calc.clock_drift_ms, 0, 'f', 0);
- stats_tables += QString("<tr><th align=\"left\">Freq Drift</th><td>%1 Hz (%2 %)</td></tr>") // XXX Terminology?
- .arg(f_calc.freq_drift_hz, 0, 'f', 0).arg(f_calc.freq_drift_perc, 0, 'f', 2);
- stats_tables += "</table></p>\n";
-
- stats_tables += "<h4>Reverse</h4>\n";
- stats_tables += QString("<p>%1:%2 " UTF8_RIGHTWARDS_ARROW)
- .arg(r_calc.src_addr_str)
- .arg(r_calc.src_port);
- stats_tables += QString("<br>%1:%2</p>\n")
- .arg(r_calc.dst_addr_str)
- .arg(r_calc.dst_port);
- stats_tables += "<p><table>\n";
- stats_tables += QString("<tr><th align=\"left\">SSRC</th><td>%1</td></tr>")
- .arg(int_to_qstring(r_calc.ssrc, 8, 16));
- stats_tables += QString("<tr><th align=\"left\">Max Delta</th><td>%1 ms @ %2</td></tr>")
- .arg(r_calc.max_delta, 0, 'f', prefs.gui_decimal_places3)
- .arg(r_calc.last_packet_num);
- stats_tables += QString("<tr><th align=\"left\">Max Jitter</th><td>%1 ms</td></tr>")
- .arg(r_calc.max_jitter, 0, 'f', prefs.gui_decimal_places3);
- stats_tables += QString("<tr><th align=\"left\">Mean Jitter</th><td>%1 ms</td></tr>")
- .arg(r_calc.mean_jitter, 0, 'f', prefs.gui_decimal_places3);
- stats_tables += QString("<tr><th align=\"left\">Max Skew</th><td>%1 ms</td></tr>")
- .arg(r_calc.max_skew, 0, 'f', prefs.gui_decimal_places3);
- stats_tables += QString("<tr><th align=\"left\">RTP Packets</th><td>%1</td></tr>")
- .arg(r_calc.total_nr);
- stats_tables += QString("<tr><th align=\"left\">Expected</th><td>%1</td></tr>")
- .arg(r_calc.packet_expected);
- stats_tables += QString("<tr><th align=\"left\">Lost</th><td>%1 (%2 %)</td></tr>")
- .arg(r_calc.lost_num).arg(r_calc.lost_perc, 0, 'f', prefs.gui_decimal_places1);
- stats_tables += QString("<tr><th align=\"left\">Seq Errs</th><td>%1</td></tr>")
- .arg(r_calc.sequence_err);
- stats_tables += QString("<tr><th align=\"left\">Start at</th><td>%1 s @ %2</td></tr>")
- .arg(r_calc.start_time_ms, 0, 'f', 6)
- .arg(r_calc.first_packet_num);
- stats_tables += QString("<tr><th align=\"left\">Duration</th><td>%1 s</td></tr>")
- .arg(r_calc.duration_ms, 0, 'f', prefs.gui_decimal_places1);
- stats_tables += QString("<tr><th align=\"left\">Clock Drift</th><td>%1 ms</td></tr>")
- .arg(r_calc.clock_drift_ms, 0, 'f', 0);
- stats_tables += QString("<tr><th align=\"left\">Freq Drift</th><td>%1 Hz (%2 %)</td></tr>") // XXX Terminology?
- .arg(r_calc.freq_drift_hz, 0, 'f', 0).arg(r_calc.freq_drift_perc, 0, 'f', 2);
- stats_tables += "</table></p>";
- if (rev_statinfo_.rtp_stats.total_nr) {
- stats_tables += QString("<h4>Forward to reverse<br/>start diff %1 s @ %2</h4>")
- .arg(qAbs(r_calc.start_time_ms - f_calc.start_time_ms), 0, 'f', 6)
- .arg(qAbs((int64_t)r_calc.first_packet_num - (int64_t)f_calc.first_packet_num));
- }
- stats_tables += "</body></html>\n";
-
- ui->statisticsLabel->setText(stats_tables);
+ for(int i=0; i<tabs_.count(); i++) {
+ rtpstream_info_t *stream = &tabs_[i]->stream;
+ rtpstream_info_calc_t s_calc;
+ rtpstream_info_calculate(stream, &s_calc);
+
+ QString stats_tables = "<html><head><style>td{vertical-align:bottom;}</style></head><body>\n";
+ stats_tables += "<h4>Stream</h4>\n";
+ stats_tables += QString("<p>%1:%2 " UTF8_RIGHTWARDS_ARROW)
+ .arg(s_calc.src_addr_str)
+ .arg(s_calc.src_port);
+ stats_tables += QString("<br>%1:%2</p>\n")
+ .arg(s_calc.dst_addr_str)
+ .arg(s_calc.dst_port);
+ stats_tables += "<p><table>\n";
+ stats_tables += QString("<tr><th align=\"left\">SSRC</th><td>%1</td></tr>")
+ .arg(int_to_qstring(s_calc.ssrc, 8, 16));
+ stats_tables += QString("<tr><th align=\"left\">Max Delta</th><td>%1 ms @ %2</td></tr>")
+ .arg(s_calc.max_delta, 0, 'f', prefs.gui_decimal_places3)
+ .arg(s_calc.last_packet_num);
+ stats_tables += QString("<tr><th align=\"left\">Max Jitter</th><td>%1 ms</td></tr>")
+ .arg(s_calc.max_jitter, 0, 'f', prefs.gui_decimal_places3);
+ stats_tables += QString("<tr><th align=\"left\">Mean Jitter</th><td>%1 ms</td></tr>")
+ .arg(s_calc.mean_jitter, 0, 'f', prefs.gui_decimal_places3);
+ stats_tables += QString("<tr><th align=\"left\">Max Skew</th><td>%1 ms</td></tr>")
+ .arg(s_calc.max_skew, 0, 'f', prefs.gui_decimal_places3);
+ stats_tables += QString("<tr><th align=\"left\">RTP Packets</th><td>%1</td></tr>")
+ .arg(s_calc.total_nr);
+ stats_tables += QString("<tr><th align=\"left\">Expected</th><td>%1</td></tr>")
+ .arg(s_calc.packet_expected);
+ stats_tables += QString("<tr><th align=\"left\">Lost</th><td>%1 (%2 %)</td></tr>")
+ .arg(s_calc.lost_num).arg(s_calc.lost_perc, 0, 'f', prefs.gui_decimal_places1);
+ stats_tables += QString("<tr><th align=\"left\">Seq Errs</th><td>%1</td></tr>")
+ .arg(s_calc.sequence_err);
+ stats_tables += QString("<tr><th align=\"left\">Start at</th><td>%1 s @ %2</td></tr>")
+ .arg(s_calc.start_time_ms, 0, 'f', 6)
+ .arg(s_calc.first_packet_num);
+ stats_tables += QString("<tr><th align=\"left\">Duration</th><td>%1 s</td></tr>")
+ .arg(s_calc.duration_ms, 0, 'f', prefs.gui_decimal_places1);
+ stats_tables += QString("<tr><th align=\"left\">Clock Drift</th><td>%1 ms</td></tr>")
+ .arg(s_calc.clock_drift_ms, 0, 'f', 0);
+ stats_tables += QString("<tr><th align=\"left\">Freq Drift</th><td>%1 Hz (%2 %)</td></tr>") // XXX Terminology?
+ .arg(s_calc.freq_drift_hz, 0, 'f', 0).arg(s_calc.freq_drift_perc, 0, 'f', 2);
+ stats_tables += "</table></p>\n";
+
+ tabs_[i]->statistics_label->setText(stats_tables);
+
+ for (int col = 0; col < tabs_[i]->tree_widget->columnCount() - 1; col++) {
+ tabs_[i]->tree_widget->resizeColumnToContents(col);
+ }
- for (int col = 0; col < ui->forwardTreeWidget->columnCount() - 1; col++) {
- ui->forwardTreeWidget->resizeColumnToContents(col);
- ui->reverseTreeWidget->resizeColumnToContents(col);
+ tabs_[i]->jitter_graph->setData(*tabs_[i]->time_vals, *tabs_[i]->jitter_vals);
+ tabs_[i]->diff_graph->setData(*tabs_[i]->time_vals, *tabs_[i]->diff_vals);
+ tabs_[i]->delta_graph->setData(*tabs_[i]->time_vals, *tabs_[i]->delta_vals);
}
- 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();
@@ -952,13 +782,8 @@ void RtpAnalysisDialog::updateGraph()
QVector<rtpstream_info_t *>RtpAnalysisDialog::getSelectedRtpStreams()
{
QVector<rtpstream_info_t *> stream_infos;
-
- if (num_streams_ > 0) {
- stream_infos << &fwd_statinfo_;
-
- if (num_streams_ > 1) {
- stream_infos << &rev_statinfo_;
- }
+ for(int i=0; i < tabs_.count(); i++) {
+ stream_infos << &tabs_[i]->stream;
}
return stream_infos;
@@ -966,472 +791,44 @@ QVector<rtpstream_info_t *>RtpAnalysisDialog::getSelectedRtpStreams()
void RtpAnalysisDialog::rtpPlayerReplace()
{
- if (num_streams_ < 1) return;
+ if (tabs_.count() < 1) return;
emit rtpPlayerDialogReplaceRtpStreams(getSelectedRtpStreams());
}
void RtpAnalysisDialog::rtpPlayerAdd()
{
- if (num_streams_ < 1) return;
+ if (tabs_.count() < 1) return;
emit rtpPlayerDialogAddRtpStreams(getSelectedRtpStreams());
}
void RtpAnalysisDialog::rtpPlayerRemove()
{
- if (num_streams_ < 1) return;
+ if (tabs_.count() < 1) return;
emit rtpPlayerDialogRemoveRtpStreams(getSelectedRtpStreams());
}
-/* Convert one packet payload to samples in row */
-/* It supports G.711 now, but can be extended to any other codecs */
-size_t RtpAnalysisDialog::convert_payload_to_samples(unsigned int payload_type, const gchar *payload_type_names[256], QTemporaryFile *tempfile, uint8_t *pd_out, size_t payload_len, struct _GHashTable *decoders_hash)
-{
- unsigned int channels = 0;
- unsigned int sample_rate = 0;
- /* Payload data are in bytes */
- uint8_t payload_data[2*4000];
- size_t decoded_bytes;
- /* Decoded audio is in samples (2 bytes) */
- SAMPLE *decode_buff = Q_NULLPTR;
- size_t decoded_samples;
- const gchar *payload_type_str = Q_NULLPTR;
-
- tempfile->read((char *)payload_data, payload_len);
- if (PT_UNDF_123 == payload_type) {
- /* 123 is payload used as silence in ED-137 */
- return 0;
- }
-
- if (payload_type_names[payload_type] != NULL) {
- payload_type_str = payload_type_names[payload_type];
- }
-
- /* Decoder returns count of bytes, but data are samples */
- decoded_bytes = decode_rtp_packet_payload(payload_type, payload_type_str, payload_data, payload_len, &decode_buff, decoders_hash, &channels, &sample_rate);
- decoded_samples = decoded_bytes/2;
-
- if (decoded_samples > 0) {
- if (sample_rate == 8000) {
- /* Change byte order to network order */
- for(size_t i = 0; i < decoded_samples; i++) {
- SAMPLE sample;
- uint8_t pd[4];
-
- sample = decode_buff[i];
- phton16(pd, sample);
- pd_out[2*i] = pd[0];
- pd_out[2*i+1] = pd[1];
- }
- } else {
- sae_unsupported_rate_ = true;
- decoded_samples = 0;
- }
- } else {
- sae_unsupported_codec_ = true;
- decoded_samples = 0;
- }
- g_free(decode_buff);
-
- return decoded_samples;
-}
-
-bool RtpAnalysisDialog::saveAudioAUSilence(size_t total_len, QFile *save_file, gboolean *stop_flag)
-{
- int64_t nchars;
- uint8_t pd_out[2*4000];
- int16_t silence;
- uint8_t pd[4];
-
- silence = 0x0000;
- phton16(pd, silence);
- pd_out[0] = pd[0];
- pd_out[1] = pd[1];
- /* Fill whole file with silence */
- for (size_t i=0; i<total_len; i++) {
- if (*stop_flag) {
- sae_stopped_ = true;
- return false;
- }
- nchars = save_file->write((const char *)pd_out, 2);
- if (nchars < 2) {
- sae_file_error_ = true;
- return false;
- }
- }
-
- return true;
-}
-
-bool RtpAnalysisDialog::saveAudioAUUnidir(tap_rtp_stat_t &statinfo, const gchar *payload_type_names[256], QTemporaryFile *tempfile, QFile *save_file, int64_t header_end, gboolean *stop_flag, gboolean interleave, size_t prefix_silence)
-{
- int64_t nchars;
- uint8_t pd_out[2*4000];
- uint8_t pd[4];
- tap_rtp_save_data_t save_data;
- struct _GHashTable *decoders_hash = rtp_decoder_hash_table_new();
-
- while (sizeof(save_data) == tempfile->read((char *)&save_data,sizeof(save_data))) {
- size_t sample_count;
-
- if (*stop_flag) {
- sae_stopped_ = true;
- return false;
- }
-
- ui->progressFrame->setValue(int(tempfile->pos() * 100 / tempfile->size()));
-
- sample_count=convert_payload_to_samples(save_data.payload_type, payload_type_names, tempfile, pd_out, save_data.payload_len, decoders_hash);
-
- if (!isSAEOK()) {
- return false;
- }
- if (sample_count > 0 ) {
- nchars = 0;
- /* Save payload samples with optional interleaving */
- for (size_t i = 0; i < sample_count; i++) {
- pd[0] = pd_out[ 2 * i ];
- pd[1] = pd_out[ 2 * i + 1 ];
- if (interleave) {
- save_file->seek(header_end+(prefix_silence + (guint32_wraparound_diff(save_data.timestamp, statinfo.first_timestamp) + i)) * 4);
- } else {
- save_file->seek(header_end+(prefix_silence + (guint32_wraparound_diff(save_data.timestamp, statinfo.first_timestamp) + i)) * 2);
- }
- nchars += save_file->write((const char *)pd, 2);
- }
- if ((size_t)nchars < sample_count*2) {
- sae_file_error_ = true;
- return false;
- }
- }
- }
- g_hash_table_destroy(decoders_hash);
-
- return true;
-}
-
-bool RtpAnalysisDialog::saveAudioAUBidir(tap_rtp_stat_t &fwd_statinfo, tap_rtp_stat_t &rev_statinfo, const gchar *fwd_payload_type_names[256], const gchar *rev_payload_type_names[256], QTemporaryFile *fwd_tempfile, QTemporaryFile *rev_tempfile, QFile *save_file, int64_t header_end, gboolean *stop_flag, size_t prefix_silence_fwd, size_t prefix_silence_rev)
+void RtpAnalysisDialog::saveCsvData(QFile *save_file, QTreeWidget *tree)
{
- if (!saveAudioAUUnidir(fwd_statinfo, fwd_payload_type_names, fwd_tempfile, save_file, header_end, stop_flag, true, prefix_silence_fwd)) {
- return false;
- }
-
- if (!saveAudioAUUnidir(rev_statinfo, rev_payload_type_names, rev_tempfile, save_file, header_end+2, stop_flag, true, prefix_silence_rev)) {
- return false;
- }
-
- return true;
-}
-
-bool RtpAnalysisDialog::saveAudioAU(StreamDirection direction, QFile *save_file, gboolean *stop_flag, RtpAnalysisDialog::SyncType sync)
-{
- uint8_t pd[4];
- int64_t nchars;
- int64_t header_end;
- size_t fwd_total_len;
- size_t rev_total_len;
- size_t total_len;
-
- /* https://pubs.opengroup.org/external/auformat.html */
- /* First we write the .au header. All values in the header are
- * 4-byte big-endian values, so we use pntoh32() to copy them
- * to a 4-byte buffer, in big-endian order, and then write out
- * the buffer. */
-
- /* the magic word 0x2e736e64 == .snd */
- phton32(pd, 0x2e736e64);
- nchars = save_file->write((const char *)pd, 4);
- if (nchars != 4) {
- sae_file_error_ = true;
- return false;
- }
- /* header offset == 24 bytes */
- phton32(pd, 24);
- nchars = save_file->write((const char *)pd, 4);
- if (nchars != 4) {
- sae_file_error_ = true;
- return false;
- }
- /* total length; it is permitted to set this to 0xffffffff */
- phton32(pd, 0xffffffff);
- nchars = save_file->write((const char *)pd, 4);
- if (nchars != 4) {
- sae_file_error_ = true;
- return false;
- }
- /* encoding format == 16-bit linear PCM */
- phton32(pd, 3);
- nchars = save_file->write((const char *)pd, 4);
- if (nchars != 4) {
- sae_file_error_ = true;
- return false;
- }
- /* sample rate == 8000 Hz */
- phton32(pd, 8000);
- nchars = save_file->write((const char *)pd, 4);
- if (nchars != 4) {
- sae_file_error_ = true;
- return false;
- }
- /* channels == 1 or == 2 */
- switch (direction) {
- case dir_forward_: {
- phton32(pd, 1);
- break;
- }
- case dir_reverse_: {
- phton32(pd, 1);
- break;
- }
- case dir_both_: {
- phton32(pd, 2);
- break;
- }
- }
- nchars = save_file->write((const char *)pd, 4);
- if (nchars != 4) {
- sae_file_error_ = true;
- return false;
- }
-
- header_end=save_file->pos();
-
- bool two_channels = rev_statinfo_.rtp_stats.total_nr && (save_payload_error_ == TAP_RTP_NO_ERROR);
- double t_min = MIN(fwd_statinfo_.rtp_stats.start_time, rev_statinfo_.rtp_stats.start_time);
- double t_fwd_diff = fwd_statinfo_.rtp_stats.start_time - t_min;
- double t_rev_diff = rev_statinfo_.rtp_stats.start_time - t_min;
- size_t fwd_samples_diff = 0;
- size_t rev_samples_diff = 0;
- size_t bidir_samples_diff = 0;
-
- switch (sync) {
- case sync_unsync_: {
- fwd_samples_diff = 0;
- rev_samples_diff = 0;
- bidir_samples_diff = 0;
- break;
- }
- case sync_sync_stream_: {
- if (! two_channels) {
- /* Only forward channel */
- /* This branch should not be reached ever */
- QMessageBox::warning(this, tr("Warning"), tr("Can't synchronize when only one channel is selected"));
- sae_other_error_ = true;
- return false;
- } else {
- /* Two channels */
- fwd_samples_diff = t_fwd_diff*8000/1000;
- rev_samples_diff = t_rev_diff*8000/1000;
- bidir_samples_diff = 0;
- }
- break;
- }
- case sync_sync_file_: {
- if (! two_channels) {
- /* Only forward channel */
- fwd_samples_diff = t_fwd_diff*8000/1000;
- rev_samples_diff = 0;
- bidir_samples_diff = fwd_samples_diff;
+ for (int row = 0; row < tree->topLevelItemCount(); row++) {
+ QTreeWidgetItem *ti = tree->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 {
- /* Two channels */
- fwd_samples_diff = t_fwd_diff*8000/1000;
- rev_samples_diff = t_rev_diff*8000/1000;
- bidir_samples_diff = t_min*8000/1000;
- }
- break;
- }
- }
-
- switch (direction) {
- /* Only forward direction */
- case dir_forward_: {
- fwd_total_len = guint32_wraparound_diff(fwd_statinfo_.rtp_stats.timestamp, fwd_statinfo_.rtp_stats.first_timestamp) + fwd_statinfo_.rtp_stats.last_payload_len;
- if (!saveAudioAUSilence(fwd_total_len + fwd_samples_diff + bidir_samples_diff, save_file, stop_flag)) {
- return false;
- }
- if (!saveAudioAUUnidir(fwd_statinfo_.rtp_stats, fwd_statinfo_.payload_type_names, fwd_tempfile_, save_file, header_end, stop_flag, false, fwd_samples_diff + bidir_samples_diff)) {
- return false;
- }
- break;
- }
- /* Only reverse direction */
- case dir_reverse_: {
- rev_total_len = guint32_wraparound_diff(rev_statinfo_.rtp_stats.timestamp, rev_statinfo_.rtp_stats.first_timestamp) + rev_statinfo_.rtp_stats.last_payload_len;
- if (!saveAudioAUSilence(rev_total_len + rev_samples_diff + bidir_samples_diff, save_file, stop_flag)) {
- return false;
- }
-
- if (!saveAudioAUUnidir(rev_statinfo_.rtp_stats, rev_statinfo_.payload_type_names, rev_tempfile_, save_file, header_end, stop_flag, false, rev_samples_diff + bidir_samples_diff)) {
- return false;
- }
- break;
- }
- /* Both directions */
- case dir_both_: {
- fwd_total_len = guint32_wraparound_diff(fwd_statinfo_.rtp_stats.timestamp, fwd_statinfo_.rtp_stats.first_timestamp) + fwd_statinfo_.rtp_stats.last_payload_len;
- rev_total_len = guint32_wraparound_diff(rev_statinfo_.rtp_stats.timestamp, rev_statinfo_.rtp_stats.first_timestamp) + rev_statinfo_.rtp_stats.last_payload_len;
- total_len = MAX(fwd_total_len + fwd_samples_diff, rev_total_len + rev_samples_diff);
- if (!saveAudioAUSilence((total_len + bidir_samples_diff) * 2, save_file, stop_flag)) {
- return false;
- }
- if (!saveAudioAUBidir(fwd_statinfo_.rtp_stats, rev_statinfo_.rtp_stats, fwd_statinfo_.payload_type_names, rev_statinfo_.payload_type_names, fwd_tempfile_, rev_tempfile_, save_file, header_end, stop_flag, fwd_samples_diff + bidir_samples_diff, rev_samples_diff + bidir_samples_diff)) {
- return false;
- }
- }
- }
-
- return true;
-}
-
-bool RtpAnalysisDialog::saveAudioRAW(StreamDirection direction, QFile *save_file, gboolean *stop_flag)
-{
- QFile *tempfile;
- tap_rtp_save_data_t save_data;
-
- switch (direction) {
- /* Only forward direction */
- case dir_forward_: {
- tempfile = fwd_tempfile_;
- break;
- }
- /* Only reversed direction */
- case dir_reverse_: {
- tempfile = rev_tempfile_;
- break;
- }
- default: {
- QMessageBox::warning(this, tr("Warning"), tr("None of channels was selected"));
- sae_other_error_ = true;
- return false;
- }
- }
-
- /* Copy just payload */
- while (sizeof(save_data) == tempfile->read((char *)&save_data,sizeof(save_data))) {
- char f_rawvalue;
-
- if (*stop_flag) {
- sae_stopped_ = true;
- return false;
- }
-
- ui->progressFrame->setValue(int(tempfile->pos() * 100 / fwd_tempfile_->size()));
-
- if (save_data.payload_len > 0) {
- for (size_t i = 0; i < save_data.payload_len; i++) {
- if (sizeof(f_rawvalue) != tempfile->read((char *)&f_rawvalue, sizeof(f_rawvalue))) {
- sae_file_error_ = true;
- return false;
- }
- if (sizeof(f_rawvalue) != save_file->write((char *)&f_rawvalue, sizeof(f_rawvalue))) {
- sae_file_error_ = true;
- return false;
- }
- }
- }
- }
-
- return true;
-}
-
-// rtp_analysis.c:copy_file
-enum { save_audio_none_, save_audio_au_, save_audio_raw_ };
-void RtpAnalysisDialog::saveAudio(RtpAnalysisDialog::StreamDirection direction, RtpAnalysisDialog::SyncType sync)
-{
- 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 forward and reverse stream audio");
- break;
- }
-
- QString ext_filter = "";
- QString ext_filter_au = tr("Sun Audio (*.au)");
- QString ext_filter_raw = tr("Raw (*.raw)");
- ext_filter.append(ext_filter_au);
- if (direction != dir_both_) {
- ext_filter.append(";;");
- ext_filter.append(ext_filter_raw);
- }
- QString sel_filter;
- QString file_path = WiresharkFileDialog::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 (0 == QString::compare(sel_filter, ext_filter_au)) {
- save_format = save_audio_au_;
- } else if (0 == QString::compare(sel_filter, ext_filter_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);
- gboolean stop_flag = false;
-
- 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;
- }
-
- ui->hintLabel->setText(tr("Saving %1…").arg(save_file.fileName()));
- ui->progressFrame->showProgress(tr("Analyzing RTP"), true, true, &stop_flag);
-
- clearSAEErrors();
- if (save_format == save_audio_au_) { /* au format */
-
- if (!saveAudioAU(direction, &save_file, &stop_flag, sync)) {
- }
- } else if (save_format == save_audio_raw_) { /* raw format */
- if (!saveAudioRAW(direction, &save_file, &stop_flag)) {
- }
- }
- if (!isSAEOK()) {
- // Other error was already handled
- if (!sae_other_error_) {
- if (sae_stopped_) {
- QMessageBox::warning(this, tr("Information"), tr("Save was interrupted"));
- }
- else if (sae_file_error_) {
- QMessageBox::warning(this, tr("Error"), tr("Save or read of file was failed during saving"));
- }
- else if (sae_unsupported_codec_) {
- QMessageBox::warning(this, tr("Warning"), tr("Codec is not supported, file is incomplete"));
- }
- else if (sae_unsupported_rate_) {
- QMessageBox::warning(this, tr("Warning"), tr("Codec rate is not supported, file is incomplete"));
- }
- else {
- QMessageBox::warning(this, tr("Warning"), tr("Unknown error occurred"));
+ values << v.toString();
}
}
+ save_file->write(values.join(",").toUtf8());
+ save_file->write("\n");
}
-
- ui->progressFrame->hide();
- updateWidgets();
- return;
}
// XXX The GTK+ UI saves the length and timestamp.
@@ -1440,15 +837,12 @@ 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");
+ case dir_one_:
+ caption = tr("Save one stream CSV");
break;
- case dir_both_:
+ case dir_all_:
default:
- caption = tr("Save CSV");
+ caption = tr("Save all stream's CSV");
break;
}
@@ -1461,50 +855,33 @@ void RtpAnalysisDialog::saveCsv(RtpAnalysisDialog::StreamDirection direction)
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();
- }
+ switch (direction) {
+ case dir_one_:
+ {
+ tab_info_t *tab_data = getTabInfoForCurrentTab();
+ if (tab_data) {
+
+ QString n = QString(*tab_data->tab_name);
+ n.replace("\n"," ");
+ save_file.write("\"");
+ save_file.write(n.toUtf8());
+ save_file.write("\"\n");
+ saveCsvData(&save_file, tab_data->tree_widget);
}
- 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->reverseTreeWidget->topLevelItemCount(); row++) {
- QTreeWidgetItem *ti = ui->reverseTreeWidget->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 (v.type() == QVariant::String) {
- values << QString("\"%1\"").arg(v.toString());
- } else {
- values << v.toString();
- }
- }
- save_file.write(values.join(",").toUtf8());
+ break;
+ case dir_all_:
+ default:
+ for(int i=0; i<tabs_.count(); i++) {
+ QString n = QString(*tabs_[i]->tab_name);
+ n.replace("\n"," ");
+ save_file.write("\"");
+ save_file.write(n.toUtf8());
+ save_file.write("\"\n");
+ saveCsvData(&save_file, tabs_[i]->tree_widget);
save_file.write("\n");
}
+ break;
}
}
@@ -1535,26 +912,46 @@ void RtpAnalysisDialog::graphClicked(QMouseEvent *event)
}
}
-void RtpAnalysisDialog::clearSAEErrors()
-{ sae_stopped_ = false;
- sae_file_error_ = false;
- sae_unsupported_codec_ = false;
- sae_unsupported_rate_ = false;
- sae_other_error_ = false;
+void RtpAnalysisDialog::clearLayout(QLayout *layout)
+{
+ if (layout) {
+ QLayoutItem *item;
+
+ //the key point here is that the layout items are stored inside the layout in a stack
+ while((item = layout->takeAt(0)) != 0) {
+ if (item->widget()) {
+ layout->removeWidget(item->widget());
+ delete item->widget();
+ }
+
+ delete item;
+ }
+ }
}
-bool RtpAnalysisDialog::isSAEOK()
-{ return !(sae_stopped_ ||
- sae_file_error_ ||
- sae_unsupported_codec_ ||
- sae_unsupported_rate_ ||
- sae_other_error_
- )
- ;
+void RtpAnalysisDialog::closeTab(int index)
+{
+ // Do not close last tab with graph
+ if (index != tabs_.count()) {
+ QWidget *remove_tab = qobject_cast<QWidget *>(ui->tabWidget->widget(index));
+ tab_info_t *tab = tabs_[index];
+ ui->tabWidget->removeTab(index);
+ ui->streamGraph->removeGraph(tab->jitter_graph);
+ ui->streamGraph->removeGraph(tab->diff_graph);
+ ui->streamGraph->removeGraph(tab->delta_graph);
+ clearLayout(tab->graphHorizontalLayout);
+ delete remove_tab;
+ deleteTabInfo(tabs_[index]);
+ g_free(tabs_[index]);
+ tabs_.remove(index);
+
+ updateGraph();
+ }
}
-void RtpAnalysisDialog::findStreams()
+void RtpAnalysisDialog::findRtpStreams()
{
+ rtpstream_info_t fwd_info, rev_info;
const gchar filter_text[] = "rtp && rtp.version == 2 && rtp.ssrc && (ip || ipv6)";
dfilter_t *sfcode;
gchar *err_msg;
@@ -1605,10 +1002,10 @@ void RtpAnalysisDialog::findStreams()
dfilter_free(sfcode);
/* OK, it is an RTP frame. Let's get the IP and port values */
- rtpstream_id_copy_pinfo(&(edt.pi),&(fwd_statinfo_.id),false);
+ rtpstream_id_copy_pinfo(&(edt.pi),&(fwd_info.id),false);
/* assume the inverse ip/port combination for the reverse direction */
- rtpstream_id_copy_pinfo(&(edt.pi),&(rev_statinfo_.id),true);
+ rtpstream_id_copy_pinfo(&(edt.pi),&(rev_info.id),true);
/* now we need the SSRC value of the current frame */
GPtrArray *gp = proto_get_finfo_ptr_array(edt.tree, hfid_rtp_ssrc);
@@ -1619,7 +1016,7 @@ void RtpAnalysisDialog::findStreams()
updateWidgets();
return;
}
- fwd_statinfo_.id.ssrc = fvalue_get_uinteger(&((field_info *)gp->pdata[0])->value);
+ fwd_info.id.ssrc = fvalue_get_uinteger(&((field_info *)gp->pdata[0])->value);
epan_dissect_cleanup(&edt);
@@ -1628,42 +1025,148 @@ void RtpAnalysisDialog::findStreams()
tapinfo_.tap_data = this;
tapinfo_.mode = TAP_ANALYSE;
-// register_tap_listener_rtpstream(&tapinfo_, NULL);
/* Scan for RTP streams (redissect all packets) */
rtpstream_scan(&tapinfo_, cap_file_.capFile(), Q_NULLPTR);
+ QVector<rtpstream_info_t *> stream_infos;
+
for (GList *strinfo_list = g_list_first(tapinfo_.strinfo_list); strinfo_list; strinfo_list = gxx_list_next(strinfo_list)) {
rtpstream_info_t * strinfo = gxx_list_data(rtpstream_info_t*, strinfo_list);
- if (rtpstream_id_equal(&(strinfo->id), &(fwd_statinfo_.id),RTPSTREAM_ID_EQUAL_NONE))
+ if (rtpstream_id_equal(&(strinfo->id), &(fwd_info.id),RTPSTREAM_ID_EQUAL_NONE))
{
- fwd_statinfo_.packet_count = strinfo->packet_count;
- fwd_statinfo_.setup_frame_number = strinfo->setup_frame_number;
- nstime_copy(&fwd_statinfo_.start_rel_time, &strinfo->start_rel_time);
- nstime_copy(&fwd_statinfo_.stop_rel_time, &strinfo->stop_rel_time);
- nstime_copy(&fwd_statinfo_.start_abs_time, &strinfo->start_abs_time);
- num_streams_++;
+ fwd_info.packet_count = strinfo->packet_count;
+ fwd_info.setup_frame_number = strinfo->setup_frame_number;
+ nstime_copy(&fwd_info.start_rel_time, &strinfo->start_rel_time);
+ nstime_copy(&fwd_info.stop_rel_time, &strinfo->stop_rel_time);
+ nstime_copy(&fwd_info.start_abs_time, &strinfo->start_abs_time);
+ stream_infos << &fwd_info;
}
- if (rtpstream_id_equal(&(strinfo->id), &(rev_statinfo_.id),RTPSTREAM_ID_EQUAL_NONE))
+ if (rtpstream_id_equal(&(strinfo->id), &(rev_info.id),RTPSTREAM_ID_EQUAL_NONE))
{
- rev_statinfo_.packet_count = strinfo->packet_count;
- rev_statinfo_.setup_frame_number = strinfo->setup_frame_number;
- nstime_copy(&rev_statinfo_.start_rel_time, &strinfo->start_rel_time);
- nstime_copy(&rev_statinfo_.stop_rel_time, &strinfo->stop_rel_time);
- nstime_copy(&rev_statinfo_.start_abs_time, &strinfo->start_abs_time);
- num_streams_++;
- if (rev_statinfo_.id.ssrc == 0) {
- rev_statinfo_.id.ssrc = strinfo->id.ssrc;
+ rev_info.packet_count = strinfo->packet_count;
+ rev_info.setup_frame_number = strinfo->setup_frame_number;
+ nstime_copy(&rev_info.start_rel_time, &strinfo->start_rel_time);
+ nstime_copy(&rev_info.stop_rel_time, &strinfo->stop_rel_time);
+ nstime_copy(&rev_info.start_abs_time, &strinfo->start_abs_time);
+ stream_infos << &rev_info;
+ if (rev_info.id.ssrc == 0) {
+ rev_info.id.ssrc = strinfo->id.ssrc;
}
}
}
+
+ addRtpStreams(stream_infos);
}
void RtpAnalysisDialog::showStreamMenu(QPoint pos)
{
- QTreeWidget *cur_tree = qobject_cast<QTreeWidget *>(ui->tabWidget->currentWidget());
+ tab_info_t *tab_data = getTabInfoForCurrentTab();
+ if (!tab_data) return;
+
+ QTreeWidget *cur_tree = tab_data->tree_widget;
if (!cur_tree) return;
updateWidgets();
stream_ctx_menu_.popup(cur_tree->viewport()->mapToGlobal(pos));
}
+
+void RtpAnalysisDialog::replaceRtpStreams(QVector<rtpstream_info_t *> stream_infos)
+{
+ // Delete existing tabs (from last to first)
+ if (tabs_.count() > 0) {
+ for(int i=tabs_.count(); i>0; i--) {
+ closeTab(i-1);
+ }
+ }
+ addRtpStreams(stream_infos);
+}
+
+void RtpAnalysisDialog::addRtpStreams(QVector<rtpstream_info_t *> stream_infos)
+{
+ int first_tab_no = -1;
+
+ foreach(rtpstream_info_t *rtpstream, stream_infos) {
+ bool found = false;
+ for(int i=0; i < tabs_.count(); i++) {
+ if (rtpstream_id_equal(&(tabs_[i]->stream.id), &(rtpstream->id), RTPSTREAM_ID_EQUAL_SSRC)) {
+ found = true;
+ }
+ }
+
+ if (!found) {
+ int cur_tab_no;
+
+ tab_info_t *new_tab = g_new0(tab_info_t, 1);
+ rtpstream_info_copy_deep(&new_tab->stream, rtpstream);
+ new_tab->time_vals = new QVector<double>();
+ new_tab->jitter_vals = new QVector<double>();
+ new_tab->diff_vals = new QVector<double>();
+ new_tab->delta_vals = new QVector<double>();
+ tabs_ << new_tab;
+ cur_tab_no = addTabUI(new_tab);
+ if (first_tab_no == -1) {
+ first_tab_no = cur_tab_no;
+ }
+ }
+ }
+ if (first_tab_no != -1) {
+ ui->tabWidget->setCurrentIndex(first_tab_no);
+ }
+ registerTapListener("rtp", this, NULL, 0, tapReset, tapPacket, tapDraw);
+ cap_file_.retapPackets();
+ removeTapListeners();
+
+ updateGraph();
+}
+
+void RtpAnalysisDialog::removeRtpStreams(QVector<rtpstream_info_t *> stream_infos _U_)
+{
+ foreach(rtpstream_info_t *rtpstream, stream_infos) {
+ for(int i=0; i < tabs_.count(); i++) {
+ if (rtpstream_id_equal(&(tabs_[i]->stream.id), &(rtpstream->id), RTPSTREAM_ID_EQUAL_SSRC)) {
+ closeTab(i);
+ }
+ }
+ }
+
+ updateGraph();
+}
+
+tab_info_t *RtpAnalysisDialog::getTabInfoForCurrentTab()
+{
+ tab_info_t *tab_data;
+
+ if (file_closed_) return NULL;
+ QWidget *cur_tab = qobject_cast<QWidget *>(ui->tabWidget->currentWidget());
+ if (!cur_tab) return NULL;
+ tab_data = static_cast<tab_info_t *>(cur_tab->property("tab_data").value<void*>());
+
+ return tab_data;
+}
+
+QPushButton *RtpAnalysisDialog::addAnalyzeButton(QDialogButtonBox *button_box, QDialog *dialog)
+{
+ if (!button_box) return NULL;
+
+ QPushButton *analysis_button;
+ analysis_button = button_box->addButton(tr("&Analyze"), QDialogButtonBox::ActionRole);
+ analysis_button->setToolTip(tr("Open the analysis window for the selected stream(s)"));
+
+ QMenu *button_menu = new QMenu(analysis_button);
+ button_menu->setToolTipsVisible(true);
+ QAction *ca;
+ ca = button_menu->addAction(tr("&Set list"));
+ ca->setToolTip(tr("Replace existing list in RTP Analysis Dialog with new one"));
+ connect(ca, SIGNAL(triggered()), dialog, SLOT(rtpAnalysisReplace()));
+ ca = button_menu->addAction(tr("&Add to list"));
+ ca->setToolTip(tr("Add new set to existing list in RTP Analysis Dialog"));
+ connect(ca, SIGNAL(triggered()), dialog, SLOT(rtpAnalysisAdd()));
+ ca = button_menu->addAction(tr("&Remove from playlist"));
+ ca->setToolTip(tr("Remove selected streams from list in RTP Analysis Dialog"));
+ connect(ca, SIGNAL(triggered()), dialog, SLOT(rtpAnalysisRemove()));
+ analysis_button->setMenu(button_menu);
+
+ return analysis_button;
+}
+
diff --git a/ui/qt/rtp_analysis_dialog.h b/ui/qt/rtp_analysis_dialog.h
index 8c0df74bdf..09d08cc08a 100644
--- a/ui/qt/rtp_analysis_dialog.h
+++ b/ui/qt/rtp_analysis_dialog.h
@@ -20,9 +20,12 @@
#include "ui/tap-rtp-common.h"
#include "ui/tap-rtp-analysis.h"
-#include <QAbstractButton>
#include <QMenu>
+#include <QTreeWidget>
+#include <QLabel>
#include <QFile>
+#include <QCheckBox>
+#include <QHBoxLayout>
#include "wireshark_dialog.h"
@@ -32,6 +35,26 @@ class RtpAnalysisDialog;
class QCPGraph;
class QTemporaryFile;
+class QDialogButtonBox;
+
+typedef struct {
+ rtpstream_info_t stream;
+ QVector<double> *time_vals;
+ QVector<double> *jitter_vals;
+ QVector<double> *diff_vals;
+ QVector<double> *delta_vals;
+ QTreeWidget *tree_widget;
+ QLabel *statistics_label;
+ QString *tab_name;
+ QCPGraph *jitter_graph;
+ QCPGraph *diff_graph;
+ QCPGraph *delta_graph;
+ QHBoxLayout *graphHorizontalLayout;
+ QCheckBox *stream_checkbox;
+ QCheckBox *jitter_checkbox;
+ QCheckBox *diff_checkbox;
+ QCheckBox *delta_checkbox;
+} tab_info_t;
class RtpAnalysisDialog : public WiresharkDialog
{
@@ -40,6 +63,24 @@ class RtpAnalysisDialog : public WiresharkDialog
public:
explicit RtpAnalysisDialog(QWidget &parent, CaptureFile &cf, rtpstream_info_t *stream_fwd = 0, rtpstream_info_t *stream_rev = 0);
~RtpAnalysisDialog();
+ /**
+ * @brief Common routine to add a "Analyze" button to a QDialogButtonBox.
+ * @param button_box Caller's QDialogButtonBox.
+ * @return The new "Analyze" button.
+ */
+ static QPushButton *addAnalyzeButton(QDialogButtonBox *button_box, QDialog *dialog);
+
+ /** Replace/Add/Remove an RTP streams to analyse.
+ * Requires array of rtpstream_info_t.
+ * Each item must have filled items: src_addr, src_port, dest_addr,
+ * dest_port, ssrc, packet_count, setup_frame_number, and start_rel_time.
+ *
+ * @param rtpstream struct with rtpstream info
+ */
+ void replaceRtpStreams(QVector<rtpstream_info_t *> stream_infos);
+ void addRtpStreams(QVector<rtpstream_info_t *> stream_infos);
+ void removeRtpStreams(QVector<rtpstream_info_t *> stream_infos);
+ void findRtpStreams();
signals:
void goToPacket(int packet_num);
@@ -58,102 +99,54 @@ protected slots:
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_actionSaveAudioUnsync_triggered();
- void on_actionSaveForwardAudioUnsync_triggered();
- void on_actionSaveReverseAudioUnsync_triggered();
- void on_actionSaveAudioSyncStream_triggered();
- void on_actionSaveForwardAudioSyncStream_triggered();
- void on_actionSaveReverseAudioSyncStream_triggered();
- void on_actionSaveAudioSyncFile_triggered();
- void on_actionSaveForwardAudioSyncFile_triggered();
- void on_actionSaveReverseAudioSyncFile_triggered();
- void on_actionSaveCsv_triggered();
- void on_actionSaveForwardCsv_triggered();
- void on_actionSaveReverseCsv_triggered();
+ void on_actionSaveOneCsv_triggered();
+ void on_actionSaveAllCsv_triggered();
void on_actionSaveGraph_triggered();
void on_buttonBox_helpRequested();
void showStreamMenu(QPoint pos);
void graphClicked(QMouseEvent *event);
+ void closeTab(int index);
+ void rowCheckboxChanged(int checked);
+ void singleCheckboxChanged(int checked);
private:
Ui::RtpAnalysisDialog *ui;
- enum StreamDirection { dir_both_, dir_forward_, dir_reverse_ };
- enum SyncType { sync_unsync_, sync_sync_stream_, sync_sync_file_ };
+ enum StreamDirection { dir_all_, dir_one_ };
+ int tab_seq;
- /* Save Audio Errors */
- bool sae_stopped_;
- bool sae_file_error_;
- bool sae_unsupported_codec_;
- bool sae_unsupported_rate_;
- bool sae_other_error_;
-
- int num_streams_;
-
- rtpstream_info_t fwd_statinfo_;
- rtpstream_info_t rev_statinfo_;
+ QVector<tab_info_t *> tabs_;
QPushButton *player_button_;
- 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_;
- tap_rtp_error_type_t save_payload_error_;
QMenu stream_ctx_menu_;
QMenu graph_ctx_menu_;
- void findStreams();
-
// Tap callbacks
static void tapReset(void *tapinfo_ptr);
static tap_packet_status 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 addPacket(tab_info_t *tab, packet_info *pinfo, const struct _rtp_info *rtpinfo);
void updateStatistics();
void updateGraph();
- void showPlayer();
-
- size_t convert_payload_to_samples(unsigned int payload_type, const gchar *payload_type_names[256], QTemporaryFile *tempfile, uint8_t *pd_out, size_t expected_nchars, struct _GHashTable *decoders_hash);
- bool saveAudioAUSilence(size_t total_len, QFile *save_file, gboolean *stop_flag);
- bool saveAudioAUUnidir(tap_rtp_stat_t &statinfo, const gchar *payload_type_names[256], QTemporaryFile *tempfile, QFile *save_file, int64_t header_end, gboolean *stop_flag, gboolean interleave, size_t prefix_silence);
- bool saveAudioAUBidir(tap_rtp_stat_t &fwd_statinfo, tap_rtp_stat_t &rev_statinfo, const gchar *fwd_payload_type_names[256], const gchar *rev_payload_type_names[256], QTemporaryFile *fwd_tempfile, QTemporaryFile *rev_tempfile, QFile *save_file, int64_t header_end, gboolean *stop_flag, size_t prefix_silence_fwd, size_t prefix_silence_rev);
- bool saveAudioAU(StreamDirection direction, QFile *save_file, gboolean *stop_flag, RtpAnalysisDialog::SyncType sync);
- bool saveAudioRAW(StreamDirection direction, QFile *save_file, gboolean *stop_flag);
- void saveAudio(StreamDirection direction, RtpAnalysisDialog::SyncType sync);
+ void saveCsvData(QFile *save_file, QTreeWidget *tree);
void saveCsv(StreamDirection direction);
- uint32_t processNode(proto_node *ptree_node, header_field_info *hfinformation, const gchar* proto_field, bool *ok);
- uint32_t getIntFromProtoTree(proto_tree *protocol_tree, const gchar *proto_name, const gchar *proto_field, bool *ok);
-
bool eventFilter(QObject*, QEvent* event);
- void clearSAEErrors();
- bool isSAEOK();
QVector<rtpstream_info_t *>getSelectedRtpStreams();
+ int addTabUI(tab_info_t *new_tab);
+ tab_info_t *getTabInfoForCurrentTab();
+ void deleteTabInfo(tab_info_t *tab_info);
+ void clearLayout(QLayout *layout);
};
#endif // RTP_ANALYSIS_DIALOG_H
diff --git a/ui/qt/rtp_analysis_dialog.ui b/ui/qt/rtp_analysis_dialog.ui
index fee564a97b..33df9bfa11 100644
--- a/ui/qt/rtp_analysis_dialog.ui
+++ b/ui/qt/rtp_analysis_dialog.ui
@@ -17,122 +17,10 @@
<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="uniformRowHeights">
- <bool>true</bool>
- </property>
- <property name="itemsExpandable">
- <bool>false</bool>
- </property>
- <property name="sortingEnabled">
- <bool>true</bool>
- </property>
- <property name="expandsOnDoubleClick">
- <bool>false</bool>
- </property>
- <attribute name="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 (ms)</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="uniformRowHeights">
- <bool>true</bool>
- </property>
- <property name="itemsExpandable">
- <bool>false</bool>
- </property>
- <property name="sortingEnabled">
- <bool>true</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>
@@ -141,152 +29,6 @@
<item>
<widget class="QCustomPlot" name="streamGraph" native="true"/>
</item>
- <item>
- <layout class="QHBoxLayout" name="forwardHorizontalLayout" stretch="0,0,0,0,0,1">
- <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>
- <spacer name="horizontalSpacer_3">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>10</width>
- <height>5</height>
- </size>
- </property>
- </spacer>
- </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>
- <spacer name="horizontalSpacer_5">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>10</width>
- <height>5</height>
- </size>
- </property>
- </spacer>
- </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>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>10</width>
- <height>5</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </item>
- <item>
- <layout class="QHBoxLayout" name="reverseHorizontalLayout" stretch="0,0,0,0,0,1">
- <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>
- <spacer name="horizontalSpacer_4">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>10</width>
- <height>5</height>
- </size>
- </property>
- </spacer>
- </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>
- <spacer name="horizontalSpacer_6">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>10</width>
- <height>5</height>
- </size>
- </property>
- </spacer>
- </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>
- <item>
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>10</width>
- <height>5</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </item>
</layout>
</widget>
</widget>
@@ -336,52 +78,28 @@
<string>Open export menu</string>
</property>
</action>
- <action name="actionSaveAudioUnsync">
- <property name="text">
- <string>Unsynchronized Forward and Reverse Audio</string>
- </property>
- <property name="toolTip">
- <string>Save the unsynchronized audio data for both channels.</string>
- </property>
- </action>
- <action name="actionSaveForwardAudioUnsync">
- <property name="text">
- <string>Unsynchronized Forward Stream Audio</string>
- </property>
- <property name="toolTip">
- <string>Save the unsynchronized forward stream audio data.</string>
- </property>
- </action>
- <action name="actionSaveReverseAudioUnsync">
- <property name="text">
- <string>Unsynchronized Reverse Stream Audio</string>
- </property>
- <property name="toolTip">
- <string>Save the unsynchronized 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>
+ <string>Save tables as CSV.</string>
</property>
</action>
- <action name="actionSaveForwardCsv">
+ <action name="actionSaveOneCsv">
<property name="text">
- <string>Forward Stream CSV</string>
+ <string>Current Tab Stream CSV</string>
</property>
<property name="toolTip">
- <string>Save the forward table as CSV.</string>
+ <string>Save the table on the current tab as CSV.</string>
</property>
</action>
- <action name="actionSaveReverseCsv">
+ <action name="actionSaveAllCsv">
<property name="text">
- <string>Reverse Stream CSV</string>
+ <string>All Tab Streams CSV</string>
</property>
<property name="toolTip">
- <string>Save the reverse table as CSV.</string>
+ <string>Save the table from all tabs as CSV.</string>
</property>
</action>
<action name="actionSaveGraph">
@@ -414,54 +132,6 @@
<string>N</string>
</property>
</action>
- <action name="actionSaveAudioSyncStream">
- <property name="text">
- <string>Stream Synchronized Forward and Reverse Audio</string>
- </property>
- <property name="toolTip">
- <string>Save the audio data for both channels synchronized to start of earlier stream.</string>
- </property>
- </action>
- <action name="actionSaveForwardAudioSyncStream">
- <property name="text">
- <string>Stream Synchronized Forward Stream Audio</string>
- </property>
- <property name="toolTip">
- <string>Save the forward stream audio data synchronized to start of earlier stream.</string>
- </property>
- </action>
- <action name="actionSaveReverseAudioSyncStream">
- <property name="text">
- <string>Stream Synchronized Reverse Stream Audio</string>
- </property>
- <property name="toolTip">
- <string>Save the reverse stream audio data synchronized to start of earlier stream.</string>
- </property>
- </action>
- <action name="actionSaveAudioSyncFile">
- <property name="text">
- <string>File Synchronized Forward and Reverse Audio</string>
- </property>
- <property name="toolTip">
- <string>Save the audio data for both channels synchronized to start of file.</string>
- </property>
- </action>
- <action name="actionSaveForwardAudioSyncFile">
- <property name="text">
- <string>File Synchronized Forward Stream Audio</string>
- </property>
- <property name="toolTip">
- <string>Save the forward stream audio data synchronized to start of file.</string>
- </property>
- </action>
- <action name="actionSaveReverseAudioSyncFile">
- <property name="text">
- <string>File Synchronized Reverse Stream Audio</string>
- </property>
- <property name="toolTip">
- <string>Save the reverse stream audio data synchronized to start of file.</string>
- </property>
- </action>
</widget>
<customwidgets>
<customwidget>
diff --git a/ui/qt/rtp_player_dialog.cpp b/ui/qt/rtp_player_dialog.cpp
index 34058579a9..5dc89f9c8d 100644
--- a/ui/qt/rtp_player_dialog.cpp
+++ b/ui/qt/rtp_player_dialog.cpp
@@ -14,6 +14,7 @@
#ifdef QT_MULTIMEDIA_LIB
#include <epan/dissectors/packet-rtp.h>
+#include <epan/to_str.h>
#include <wsutil/report_message.h>
#include <wsutil/utf8_entities.h>
diff --git a/ui/qt/rtp_player_dialog.h b/ui/qt/rtp_player_dialog.h
index 1a2f67a970..c86001cfb8 100644
--- a/ui/qt/rtp_player_dialog.h
+++ b/ui/qt/rtp_player_dialog.h
@@ -62,7 +62,6 @@ public:
* @param button_box Caller's QDialogButtonBox.
* @return The new "Play call" button.
*/
- // XXX We might want to move this to qt_ui_utils.
static QPushButton *addPlayerButton(QDialogButtonBox *button_box, QDialog *dialog);
#ifdef QT_MULTIMEDIA_LIB
@@ -73,7 +72,6 @@ public:
void setMarkers();
-public slots:
/** Replace/Add/Remove an RTP streams to play.
* Requires array of rtpstream_info_t.
* Each item must have filled items: src_addr, src_port, dest_addr,
diff --git a/ui/qt/rtp_stream_dialog.cpp b/ui/qt/rtp_stream_dialog.cpp
index 9d025289ef..7893f0c455 100644
--- a/ui/qt/rtp_stream_dialog.cpp
+++ b/ui/qt/rtp_stream_dialog.cpp
@@ -273,8 +273,7 @@ RtpStreamDialog::RtpStreamDialog(QWidget &parent, CaptureFile &cf) :
// this? Perhaps you should volunteer to maintain this code!
find_reverse_button_ = ui->buttonBox->addButton(ui->actionFindReverse->text(), QDialogButtonBox::ActionRole);
find_reverse_button_->setToolTip(ui->actionFindReverse->toolTip());
- analyze_button_ = ui->buttonBox->addButton(ui->actionAnalyze->text(), QDialogButtonBox::ActionRole);
- analyze_button_->setToolTip(ui->actionAnalyze->toolTip());
+ analyze_button_ = RtpAnalysisDialog::addAnalyzeButton(ui->buttonBox, this);
prepare_button_ = ui->buttonBox->addButton(ui->actionPrepareFilter->text(), QDialogButtonBox::ActionRole);
prepare_button_->setToolTip(ui->actionPrepareFilter->toolTip());
player_button_ = RtpPlayerDialog::addPlayerButton(ui->buttonBox, this);
@@ -619,26 +618,6 @@ void RtpStreamDialog::showStreamMenu(QPoint pos)
ctx_menu_.popup(ui->streamTreeWidget->viewport()->mapToGlobal(pos));
}
-void RtpStreamDialog::on_actionAnalyze_triggered()
-{
- rtpstream_info_t *stream_a, *stream_b = NULL;
-
- QTreeWidgetItem *ti = ui->streamTreeWidget->selectedItems()[0];
- RtpStreamTreeWidgetItem *rsti = static_cast<RtpStreamTreeWidgetItem*>(ti);
- stream_a = rsti->streamInfo();
- if (ui->streamTreeWidget->selectedItems().count() > 1) {
- ti = ui->streamTreeWidget->selectedItems()[1];
- rsti = static_cast<RtpStreamTreeWidgetItem*>(ti);
- stream_b = rsti->streamInfo();
- }
-
- if (stream_a == NULL && stream_b == NULL) return;
-
- RtpAnalysisDialog *rtp_analysis_dialog = new RtpAnalysisDialog(*this, cap_file_, stream_a, stream_b);
- connect(rtp_analysis_dialog, SIGNAL(goToPacket(int)), this, SIGNAL(goToPacket(int)));
- rtp_analysis_dialog->show();
-}
-
void RtpStreamDialog::on_actionCopyAsCsv_triggered()
{
QString csv;
@@ -807,8 +786,6 @@ void RtpStreamDialog::on_buttonBox_clicked(QAbstractButton *button)
on_actionPrepareFilter_triggered();
} else if (button == export_button_) {
on_actionExportAsRtpDump_triggered();
- } else if (button == analyze_button_) {
- on_actionAnalyze_triggered();
}
}
@@ -891,6 +868,27 @@ void RtpStreamDialog::rtpPlayerRemove()
emit rtpPlayerDialogRemoveRtpStreams(getSelectedRtpStreams());
}
+void RtpStreamDialog::rtpAnalysisReplace()
+{
+ if (ui->streamTreeWidget->selectedItems().count() < 1) return;
+
+ emit rtpAnalysisDialogReplaceRtpStreams(getSelectedRtpStreams());
+}
+
+void RtpStreamDialog::rtpAnalysisAdd()
+{
+ if (ui->streamTreeWidget->selectedItems().count() < 1) return;
+
+ emit rtpAnalysisDialogAddRtpStreams(getSelectedRtpStreams());
+}
+
+void RtpStreamDialog::rtpAnalysisRemove()
+{
+ if (ui->streamTreeWidget->selectedItems().count() < 1) return;
+
+ emit rtpAnalysisDialogRemoveRtpStreams(getSelectedRtpStreams());
+}
+
void RtpStreamDialog::displayFilterSuccess(bool success)
{
if (success && ui->displayFilterCheckBox->isChecked()) {
diff --git a/ui/qt/rtp_stream_dialog.h b/ui/qt/rtp_stream_dialog.h
index 7ac66bf15e..d085bfb213 100644
--- a/ui/qt/rtp_stream_dialog.h
+++ b/ui/qt/rtp_stream_dialog.h
@@ -29,6 +29,8 @@ class RtpStreamDialog : public WiresharkDialog
public:
explicit RtpStreamDialog(QWidget &parent, CaptureFile &cf);
~RtpStreamDialog();
+ void selectRtpStream(rtpstream_id_t *id);
+ void deselectRtpStream(rtpstream_id_t *id);
signals:
// Tells the packet list to redraw. An alternative might be to add a
@@ -40,14 +42,18 @@ signals:
void rtpPlayerDialogReplaceRtpStreams(QVector<rtpstream_info_t *> stream_infos);
void rtpPlayerDialogAddRtpStreams(QVector<rtpstream_info_t *> stream_infos);
void rtpPlayerDialogRemoveRtpStreams(QVector<rtpstream_info_t *> stream_infos);
+ void rtpAnalysisDialogReplaceRtpStreams(QVector<rtpstream_info_t *> stream_infos);
+ void rtpAnalysisDialogAddRtpStreams(QVector<rtpstream_info_t *> stream_infos);
+ void rtpAnalysisDialogRemoveRtpStreams(QVector<rtpstream_info_t *> stream_infos);
public slots:
- void selectRtpStream(rtpstream_id_t *id);
- void deselectRtpStream(rtpstream_id_t *id);
void displayFilterSuccess(bool success);
void rtpPlayerReplace();
void rtpPlayerAdd();
void rtpPlayerRemove();
+ void rtpAnalysisReplace();
+ void rtpAnalysisAdd();
+ void rtpAnalysisRemove();
protected:
bool eventFilter(QObject *obj, QEvent *event);
@@ -94,7 +100,6 @@ private slots:
void on_buttonBox_helpRequested();
void on_buttonBox_clicked(QAbstractButton *button);
void on_actionExportAsRtpDump_triggered();
- void on_actionAnalyze_triggered();
void captureEvent(CaptureEvent e);
void on_displayFilterCheckBox_toggled(bool checked);
void on_todCheckBox_toggled(bool checked);
diff --git a/ui/qt/utils/stock_icon.cpp b/ui/qt/utils/stock_icon.cpp
index 824d1bb633..8bf4fe258d 100644
--- a/ui/qt/utils/stock_icon.cpp
+++ b/ui/qt/utils/stock_icon.cpp
@@ -46,6 +46,7 @@
#include <QFontMetrics>
#include <QMap>
#include <QPainter>
+#include <QPainterPath>
#include <QStyle>
#include <QStyleOption>
@@ -170,6 +171,78 @@ QIcon StockIcon::colorIcon(const QRgb bg_color, const QRgb fg_color, const QStri
return color_icon;
}
+// Create a triangle icon filled with the specified color.
+QIcon StockIcon::colorIconTriangle(const QRgb bg_color, const QRgb fg_color)
+{
+ QList<int> sizes = QList<int>() << 12 << 16 << 24 << 32 << 48;
+ QIcon color_icon;
+
+ foreach (int size, sizes) {
+ QPixmap pm(size, size);
+ QPainter painter(&pm);
+ QPainterPath triangle;
+ pm.fill();
+ painter.fillRect(0, 0, size-1, size-1, Qt::transparent);
+ painter.setPen(fg_color);
+ painter.setBrush(QColor(bg_color));
+ triangle.moveTo(0, size-1);
+ triangle.lineTo(size-1, size-1);
+ triangle.lineTo((size-1)/2, 0);
+ triangle.closeSubpath();
+ painter.fillPath(triangle, QColor(bg_color));
+
+ color_icon.addPixmap(pm);
+ }
+ return color_icon;
+}
+
+// Create a cross icon filled with the specified color.
+QIcon StockIcon::colorIconCross(const QRgb bg_color, const QRgb fg_color)
+{
+ QList<int> sizes = QList<int>() << 12 << 16 << 24 << 32 << 48;
+ QIcon color_icon;
+
+ foreach (int size, sizes) {
+ QPixmap pm(size, size);
+ QPainter painter(&pm);
+ QPainterPath cross;
+ pm.fill();
+ painter.fillRect(0, 0, size-1, size-1, Qt::transparent);
+ painter.setPen(QPen(QBrush(bg_color), 3));
+ painter.setBrush(QColor(fg_color));
+ cross.moveTo(0, 0);
+ cross.lineTo(size-1, size-1);
+ cross.moveTo(0, size-1);
+ cross.lineTo(size-1, 0);
+ painter.drawPath(cross);
+
+ color_icon.addPixmap(pm);
+ }
+ return color_icon;
+}
+
+// Create a circle icon filled with the specified color.
+QIcon StockIcon::colorIconCircle(const QRgb bg_color, const QRgb fg_color)
+{
+ QList<int> sizes = QList<int>() << 12 << 16 << 24 << 32 << 48;
+ QIcon color_icon;
+
+ foreach (int size, sizes) {
+ QPixmap pm(size, size);
+ QPainter painter(&pm);
+ QRect border(2, 2, size - 3, size - 3);
+ pm.fill();
+ painter.fillRect(0, 0, size-1, size-1, Qt::transparent);
+ painter.setPen(QPen(QBrush(bg_color), 3));
+ painter.setBrush(QColor(fg_color));
+ painter.setBrush(QColor(bg_color));
+ painter.drawEllipse(border);
+
+ color_icon.addPixmap(pm);
+ }
+ return color_icon;
+}
+
void StockIcon::fillIconNameMap()
{
// Note that some of Qt's standard pixmaps are awful. We shouldn't add an
diff --git a/ui/qt/utils/stock_icon.h b/ui/qt/utils/stock_icon.h
index 1d07672ec6..d22701e78b 100644
--- a/ui/qt/utils/stock_icon.h
+++ b/ui/qt/utils/stock_icon.h
@@ -29,6 +29,9 @@ public:
explicit StockIcon(const QString icon_name);
static QIcon colorIcon(const QRgb bg_color, const QRgb fg_color, const QString glyph = QString());
+ static QIcon colorIconTriangle(const QRgb bg_color, const QRgb fg_color);
+ static QIcon colorIconCross(const QRgb bg_color, const QRgb fg_color);
+ static QIcon colorIconCircle(const QRgb bg_color, const QRgb fg_color);
private:
void fillIconNameMap();