VoIP dialogs: Performance improvements

Retap and UI response are much faster when many RTP streams are
processed. RTP Streams/Analyse 1000+, RTP Player 500+.

Changes:
- RTP streams are searched with hash, not by iterating over list.
- UI operations do not redraw screen after every change, just after all
  changes. UI is locked when rereading packets.
- Sample list during RTP decoding is stored in memory so wireshark uses
  just half of opened files for audio decoding than before.
- Analysis window checkbox area is limited in height
- Dialogs shows shows count of streams, count of selected streams and
  count of unmuted streams
- Documentation extended with chapter about RTP decoding parameters
- Documentation extended with performance estimates
This commit is contained in:
Jirka Novak 2021-04-13 16:38:13 +02:00 committed by Wireshark GitLab Utility
parent 38d279326a
commit c7f5646249
16 changed files with 312 additions and 49 deletions

View File

@ -188,6 +188,39 @@ Default value of btn:[Output Audio Rate] is btn:[Automatic]. When multiple codec
In this case user must manually select one of rates in btn:[Output Audio Rate], streams will be resampled and audio export succeeds.
====
==== RTP Decoding Settings
RTP is carried usually in UDP packets, on random source and destination port. Therefore without "help" Wireshark can't recognize it and shows just UDP packets. Wireshark recognizes RTP streams based on VoIP signaling, e. g. based on SDP message in SIP signaling. When signaling is not captured, Wireshark shows just UDP packets. There are multiple settings which helps Wireshark to recognize RTP even there is no related signaling.
You can use <<ChAdvDecodeAsFig,Decode As...>> function from menu:Analyze[Decode As...] menu or in mouse context menu. Here you can set that traffic on specific source or destination should be decoded as RTP. You can save settings for later use.
Use of menu:Decode As...[] menu works fine, but for many streams it is arduous.
You can enable heuristic dissector menu:rtp_udp[] in menu:Analyze[Enabled Protocols...]. See <<ChCustProtocolDissectionSection>> for details. Once menu:rtp_udp[] is enabled, Wireshark tries every UDP packet to decode as RTP. If decoding is possible, packet (and entire UDP stream) is decoded as RTP.
When RTP stream uses well know port, heuristic dissector ignores it. So you might miss some RTP streams. You can enable setting for udp protocol menu:Preferences[Protocols > udp > Try heuristic sub-dissectors first], see <<ChCustPreferencesSection>>. In this case heuristics dissector tries to decode UDP packet even it uses well known.
[NOTE]
====
Take into account that heuristics is just simple "test" whether packet can be read as RTP. It can be false positive and you can see decoded as RTP more UDP packets than expected.
When you enable menu:udp[Try heuristic sub-dissectors first], it increases possibility of false positives. If you capture all traffic in network, false positives rate can be quite high.
====
==== VoIP Processing Performance and Related Limits
Processing of RTP and decoding RTP voice takes resources. There are raw estimates you can use as guidelines...
RTP Streams window can show as many streams as found in the capture. Its performance is limited just by memory and CPU.
RTP Player can handle 1000+ streams, but take into account that waveforms are very small in this case.
RTP Player creates temporary file for decoding of each stream. If your OS or user has OS enforced limit for count of opened files (most of Unix/Linux systems), you can see less streams that was added to playlist. Warnings are printed on console in this case and you will see less streams in the playlist than you send to it from other tools.
RTP Player plays audio by OS sound system and OS is responsible for mixing audio when multiple streams are played. In many cases OS sound system has limited count of mixed streams it can play/mix. RTP Player tries to handle playback failures and show warning. If it happens, just mute some streams and start playback again.
RTP Analysis window can handle 1000+ streams, but it is difficult to use it with so many streams - it is difficult to navigate between them. It is expected that RTP Analysis window will be used for analysis of lower tens of streams.
[[ChTelVoipCalls]]
@ -251,7 +284,7 @@ streams of a selected IAX2 call along with a graph.
=== ISUP Messages Window
Integrated Service User Part (ISUP) protocol provides voice and non-voice signalling for telephone communications. ISUP Messages menu opens the window which shows the related statistics. The user can filter, copy or save the data into a file.
Integrated Service User Part (ISUP) protocol provides voice and non-voice signaling for telephone communications. ISUP Messages menu opens the window which shows the related statistics. The user can filter, copy or save the data into a file.
[[ChTelLTE]]
@ -319,7 +352,7 @@ This menu shows MTP3 Statistics and MTP3 Summary windows.
=== Osmux Windows
OSmux is a multiplex protocol which benefits satellite based GSM back-haul systems by reducing the bandwidth consumption of the voice proxying (RTP-AMR) and signalling traffic. The OSmux menu opens the packet counter window with the related statistic data. The user can filter, copy or save the data into a file.
OSmux is a multiplex protocol which benefits satellite based GSM back-haul systems by reducing the bandwidth consumption of the voice proxying (RTP-AMR) and signaling traffic. The OSmux menu opens the packet counter window with the related statistic data. The user can filter, copy or save the data into a file.
=== RTP
@ -463,7 +496,7 @@ The Universal Computer Protocol (UCP) plays role in transferring Short Messages
=== H.225 Window
H.225 telecommunication protocol which is responsible for messages in call signalling and media stream packetization for packet-based multimedia communication systems. The H.225 window shows the counted messages by types and reasons. The user can filter, copy or save the data into a file.
H.225 telecommunication protocol which is responsible for messages in call signaling and media stream packetization for packet-based multimedia communication systems. The H.225 window shows the counted messages by types and reasons. The user can filter, copy or save the data into a file.
[[ChTelSIPFlows]]

View File

@ -2184,7 +2184,7 @@ sharkd_session_process_tap(char *buf, const jsmntok_t *tokens, int count)
int i;
rtpstream_tapinfo_t rtp_tapinfo =
{ NULL, NULL, NULL, NULL, 0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, FALSE, FALSE};
{ NULL, NULL, NULL, NULL, 0, NULL, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, FALSE, FALSE};
for (i = 0; i < 16; i++)
{

View File

@ -44,7 +44,7 @@ static void rtpstreams_stat_draw_cb(rtpstream_tapinfo_t *tapinfo);
*/
static rtpstream_tapinfo_t the_tapinfo_struct =
{ NULL, rtpstreams_stat_draw_cb, NULL,
NULL, 0, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, FALSE, FALSE
NULL, 0, NULL, NULL, 0, TAP_ANALYSE, NULL, NULL, NULL, FALSE, FALSE
};
static void

View File

@ -429,7 +429,7 @@ int RtpAnalysisDialog::addTabUI(tab_info_t *new_tab)
new_tab->graphHorizontalLayout->setStretch(6, 1);
ui->verticalLayout_2->addLayout(new_tab->graphHorizontalLayout);
ui->layout->addLayout(new_tab->graphHorizontalLayout);
return new_tab_no;
}
@ -504,6 +504,7 @@ void RtpAnalysisDialog::updateWidgets()
ui->actionNextProblem->setEnabled(enable_nav);
if (enable_nav) {
hint.append(tr(" %1 streams, ").arg(tabs_.count() - 1));
hint.append(tr(" G: Go to packet, N: Next problem packet"));
}
@ -653,10 +654,13 @@ tap_packet_status RtpAnalysisDialog::tapPacket(void *tapinfo_ptr, packet_info *p
return TAP_PACKET_DONT_REDRAW;
/* is it the forward direction? */
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)) {
// Search tab in hash key, if there are multiple tabs with same hash
QList<tab_info_t *> tabs = rtp_analysis_dialog->tab_hash_.values(pinfo_rtp_info_to_hash(pinfo, rtpinfo));
for (int i = 0; i < tabs.size(); i++) {
tab_info_t *tab = tabs.at(i);
if (rtpstream_id_equal_pinfo_rtp_info(&tab->stream.id, pinfo, rtpinfo)) {
rtp_analysis_dialog->addPacket(tab, pinfo, rtpinfo);
break;
}
}
}
@ -930,6 +934,7 @@ void RtpAnalysisDialog::closeTab(int index)
if (index != tabs_.count()) {
QWidget *remove_tab = qobject_cast<QWidget *>(ui->tabWidget->widget(index));
tab_info_t *tab = tabs_[index];
tab_hash_.remove(rtpstream_to_hash(&tab->stream), tab);
ui->tabWidget->removeTab(index);
ui->streamGraph->removeGraph(tab->jitter_graph);
ui->streamGraph->removeGraph(tab->diff_graph);
@ -1089,11 +1094,16 @@ void RtpAnalysisDialog::addRtpStreamsPrivate(QVector<rtpstream_info_t *> stream_
{
int first_tab_no = -1;
setUpdatesEnabled(false);
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)) {
QList<tab_info_t *> tabs = tab_hash_.values(rtpstream_to_hash(rtpstream));
for (int i = 0; i < tabs.size(); i++) {
tab_info_t *tab = tabs.at(i);
if (rtpstream_id_equal(&tab->stream.id, &rtpstream->id, RTPSTREAM_ID_EQUAL_SSRC)) {
found = true;
break;
}
}
@ -1108,6 +1118,7 @@ void RtpAnalysisDialog::addRtpStreamsPrivate(QVector<rtpstream_info_t *> stream_
new_tab->delta_vals = new QVector<double>();
tabs_ << new_tab;
cur_tab_no = addTabUI(new_tab);
tab_hash_.insert(rtpstream_to_hash(rtpstream), new_tab);
if (first_tab_no == -1) {
first_tab_no = cur_tab_no;
}
@ -1116,6 +1127,7 @@ void RtpAnalysisDialog::addRtpStreamsPrivate(QVector<rtpstream_info_t *> stream_
if (first_tab_no != -1) {
ui->tabWidget->setCurrentIndex(first_tab_no);
}
setUpdatesEnabled(true);
registerTapListener("rtp", this, NULL, 0, tapReset, tapPacket, tapDraw);
cap_file_.retapPackets();
removeTapListeners();
@ -1125,13 +1137,17 @@ void RtpAnalysisDialog::addRtpStreamsPrivate(QVector<rtpstream_info_t *> stream_
void RtpAnalysisDialog::removeRtpStreams(QVector<rtpstream_info_t *> stream_infos _U_)
{
setUpdatesEnabled(false);
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);
QList<tab_info_t *> tabs = tab_hash_.values(rtpstream_to_hash(rtpstream));
for (int i = 0; i < tabs.size(); i++) {
tab_info_t *tab = tabs.at(i);
if (rtpstream_id_equal(&tab->stream.id, &rtpstream->id, RTPSTREAM_ID_EQUAL_SSRC)) {
closeTab(tabs_.indexOf(tab));
}
}
}
setUpdatesEnabled(true);
updateGraph();
}

View File

@ -115,6 +115,7 @@ private:
int tab_seq;
QVector<tab_info_t *> tabs_;
QMultiHash<guint, tab_info_t *> tab_hash_;
QPushButton *player_button_;

View File

@ -25,10 +25,37 @@
<attribute name="title">
<string>Graph</string>
</attribute>
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="1,0,0">
<layout class="QVBoxLayout" name="verticalLayout_2" stretch="1,0">
<item>
<widget class="QCustomPlot" name="streamGraph" native="true"/>
</item>
<item>
<widget class="QScrollArea" name="scrollarea">
<property name="minimumSize">
<size>
<width>0</width>
<height>200</height>
</size>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="qwidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>606</width>
<height>298</height>
</rect>
</property>
<layout class="QVBoxLayout" name="layout"/>
</widget>
</widget>
</item>
</layout>
</widget>
</widget>

View File

@ -36,6 +36,7 @@
#include <QVariant>
#include <QTimer>
#include <QDebug>
#include <QBuffer>
// To do:
// - Only allow one rtpstream_info_t per RtpAudioStream?
@ -80,7 +81,7 @@ RtpAudioStream::RtpAudioStream(QObject *parent, rtpstream_info_t *rtpstream, boo
qWarning() << "Can't create temp file in " << tempname;
throw -1;
}
sample_file_frame_ = new QTemporaryFile(tempname, this);
sample_file_frame_ = new QBuffer(this);
if (! sample_file_frame_->open(QIODevice::ReadWrite)) {
// We are out of file resources
delete sample_file_;
@ -179,7 +180,7 @@ void RtpAudioStream::reset(double global_start_time)
if (!sample_file_->open(QIODevice::ReadWrite)) {
qWarning() << "Can't create temp file in " << tempname << " during retap";
}
sample_file_frame_ = new QTemporaryFile(tempname, this);
sample_file_frame_ = new QBuffer(this);
if (!sample_file_frame_->open(QIODevice::ReadWrite)) {
qWarning() << "Can't create temp file in " << tempname << " during retap";
}

View File

@ -154,6 +154,7 @@ public:
qint64 getLeadSilenceSamples() { return prepend_samples_; }
qint64 getTotalSamples() { return (sample_file_->size()/(qint64)sizeof(SAMPLE)); }
bool savePayload(QIODevice *file);
guint getHash() { return rtpstream_id_to_hash(&id_); }
QString getIDAsQString();
signals:

View File

@ -8,6 +8,7 @@
*/
#include <ui/rtp_media.h>
#include <ui/tap-rtp-common.h>
#include "rtp_player_dialog.h"
#include <ui_rtp_player_dialog.h>
@ -149,6 +150,8 @@ RtpPlayerDialog::RtpPlayerDialog(QWidget &parent, CaptureFile &cf) :
, marker_stream_requested_out_rate_(0)
, last_ti_(0)
, listener_removed_(true)
, block_redraw_(false)
, lock_ui_(0)
{
ui->setupUi(this);
loadGeometry(parent.width(), parent.height());
@ -414,6 +417,7 @@ void RtpPlayerDialog::retapPackets()
void RtpPlayerDialog::rescanPackets(bool rescale_axes)
{
lockUI();
// Show information for a user - it can last long time...
ui->hintLabel->setText("<i><small>" + tr("Decoding streams...") + "</i></small>");
wsApp->processEvents();
@ -453,6 +457,7 @@ void RtpPlayerDialog::rescanPackets(bool rescale_axes)
createPlot(rescale_axes);
updateWidgets();
unlockUI();
}
void RtpPlayerDialog::createPlot(bool rescale_axes)
@ -600,6 +605,8 @@ void RtpPlayerDialog::createPlot(bool rescale_axes)
void RtpPlayerDialog::addSingleRtpStream(rtpstream_info_t *rtpstream)
{
bool found = false;
AudioRouting audio_routing = AudioRouting(AUDIO_UNMUTED, channel_mono);
if (!rtpstream) return;
@ -607,23 +614,24 @@ void RtpPlayerDialog::addSingleRtpStream(rtpstream_info_t *rtpstream)
// Find the RTP streams associated with this conversation.
// gtk/rtp_player.c:mark_rtp_stream_to_play does this differently.
RtpAudioStream *audio_stream = NULL;
int tli_count = ui->streamTreeWidget->topLevelItemCount();
for (int row = 0; row < tli_count; row++) {
QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
RtpAudioStream *row_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
QList<RtpAudioStream *> streams = stream_hash_.values(rtpstream_to_hash(rtpstream));
for (int i = 0; i < streams.size(); i++) {
RtpAudioStream *row_stream = streams.at(i);
if (row_stream->isMatch(rtpstream)) {
audio_stream = row_stream;
found = true;
break;
}
}
if (!audio_stream) {
int tli_count = ui->streamTreeWidget->topLevelItemCount();
if (!found) {
try {
audio_stream = new RtpAudioStream(this, rtpstream, stereo_available_);
RtpAudioStream *audio_stream = new RtpAudioStream(this, rtpstream, stereo_available_);
audio_stream->setColor(ColorUtils::graphColor(tli_count));
QTreeWidgetItem *ti = new RtpPlayerTreeWidgetItem(ui->streamTreeWidget);
stream_hash_.insert(rtpstream_to_hash(rtpstream), audio_stream);
ti->setText(src_addr_col_, address_to_qstring(&rtpstream->id.src_addr));
ti->setText(src_port_col_, QString::number(rtpstream->id.src_port));
ti->setText(dst_addr_col_, address_to_qstring(&rtpstream->id.dst_addr));
@ -686,15 +694,19 @@ void RtpPlayerDialog::addSingleRtpStream(rtpstream_info_t *rtpstream)
void RtpPlayerDialog::lockUI()
{
if (playing_streams_.count() > 0) {
on_stopButton_clicked();
if (0 == lock_ui_++) {
if (playing_streams_.count() > 0) {
on_stopButton_clicked();
}
setEnabled(false);
}
setEnabled(false);
}
void RtpPlayerDialog::unlockUI()
{
setEnabled(true);
if (--lock_ui_ == 0) {
setEnabled(true);
}
}
void RtpPlayerDialog::replaceRtpStreams(QVector<rtpstream_info_t *> stream_infos)
@ -1197,15 +1209,16 @@ tap_packet_status RtpPlayerDialog::tapPacket(void *tapinfo_ptr, packet_info *pin
void RtpPlayerDialog::addPacket(packet_info *pinfo, const _rtp_info *rtpinfo)
{
for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
RtpAudioStream *row_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
// Search stream in hash key, if there are multiple streams with same hash
QList<RtpAudioStream *> streams = stream_hash_.values(pinfo_rtp_info_to_hash(pinfo, rtpinfo));
for (int i = 0; i < streams.size(); i++) {
RtpAudioStream *row_stream = streams.at(i);
if (row_stream->isMatch(pinfo, rtpinfo)) {
row_stream->addRtpPacket(pinfo, rtpinfo);
return;
break;
}
}
// qDebug() << "=ap no match!" << address_to_qstring(&pinfo->src) << address_to_qstring(&pinfo->dst);
}
@ -1448,8 +1461,10 @@ void RtpPlayerDialog::on_streamTreeWidget_itemSelectionChanged()
ui->actionSavePayload->setEnabled(false);
}
ui->audioPlot->replot();
updateHintLabel();
if (!block_redraw_) {
ui->audioPlot->replot();
updateHintLabel();
}
}
// Change channel audio routing if double clicked channel column
@ -1471,6 +1486,7 @@ void RtpPlayerDialog::removeRow(QTreeWidgetItem *ti)
{
RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
if (audio_stream) {
stream_hash_.remove(audio_stream->getHash(), audio_stream);
ti->setData(stream_data_col_, Qt::UserRole, QVariant());
delete audio_stream;
}
@ -1513,13 +1529,16 @@ void RtpPlayerDialog::on_actionRemoveStream_triggered()
{
QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();
block_redraw_ = true;
if (last_ti_) {
highlightItem(last_ti_, false);
last_ti_ = NULL;
}
for(int i = 0; i<items.count(); i++ ) {
//for(int i = 0; i<items.count(); i++ ) {
for(int i = items.count() - 1; i>=0; i-- ) {
removeRow(items[i]);
}
block_redraw_ = false;
// TODO: Recalculate legend
// - Graphs used for legend could be removed above and we must add new
// - If no legend is required, it should be removed
@ -1551,7 +1570,9 @@ void RtpPlayerDialog::changeAudioRoutingOnItem(QTreeWidgetItem *ti, AudioRouting
audio_graph->setSelected(ti->isSelected());
audio_graph->setMuted(audio_routing.isMuted());
ui->audioPlot->replot();
if (!block_redraw_) {
ui->audioPlot->replot();
}
}
}
@ -1560,11 +1581,14 @@ void RtpPlayerDialog::changeAudioRouting(AudioRouting new_audio_routing)
{
QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();
block_redraw_ = true;
for(int i = 0; i<items.count(); i++ ) {
QTreeWidgetItem *ti = items[i];
changeAudioRoutingOnItem(ti, new_audio_routing);
}
block_redraw_ = false;
ui->audioPlot->replot();
updateHintLabel();
}
@ -1621,11 +1645,14 @@ void RtpPlayerDialog::on_actionAudioRoutingMuteInvert_triggered()
{
QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();
block_redraw_ = true;
for(int i = 0; i<items.count(); i++ ) {
QTreeWidgetItem *ti = items[i];
invertAudioMutingOnItem(ti);
}
block_redraw_ = false;
ui->audioPlot->replot();
updateHintLabel();
}
@ -1695,6 +1722,7 @@ void RtpPlayerDialog::cleanupMarkerStream()
void RtpPlayerDialog::on_outputDeviceComboBox_currentIndexChanged(const QString &)
{
lockUI();
stereo_available_ = isStereoAvailable();
for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
@ -1709,10 +1737,12 @@ void RtpPlayerDialog::on_outputDeviceComboBox_currentIndexChanged(const QString
cleanupMarkerStream();
fillAudioRateMenu();
rescanPackets();
unlockUI();
}
void RtpPlayerDialog::on_outputAudioRate_currentIndexChanged(const QString & rate_string)
{
lockUI();
// Any unconvertable string is converted to 0 => used as Automatic rate
unsigned selected_rate = rate_string.toInt();
@ -1727,6 +1757,7 @@ void RtpPlayerDialog::on_outputAudioRate_currentIndexChanged(const QString & rat
marker_stream_requested_out_rate_ = selected_rate;
cleanupMarkerStream();
rescanPackets();
unlockUI();
}
void RtpPlayerDialog::on_jitterSpinBox_valueChanged(double)
@ -1844,10 +1875,14 @@ bool RtpPlayerDialog::isStereoAvailable()
void RtpPlayerDialog::invertSelection()
{
block_redraw_ = true;
for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
ti->setSelected(!ti->isSelected());
}
block_redraw_ = false;
ui->audioPlot->replot();
updateHintLabel();
}
void RtpPlayerDialog::on_actionSelectAll_triggered()

View File

@ -20,6 +20,7 @@
#include "rtp_audio_stream.h"
#include <QMap>
#include <QMultiHash>
#include <QTreeWidgetItem>
#include <QMetaType>
#include <ui/qt/widgets/qcustomplot.h>
@ -177,6 +178,9 @@ private:
QTreeWidgetItem *last_ti_;
bool listener_removed_;
QPushButton *export_btn_;
QMultiHash<guint, RtpAudioStream *> stream_hash_;
bool block_redraw_;
int lock_ui_;
// const QString streamKey(const rtpstream_info_t *rtpstream);
// const QString streamKey(const packet_info *pinfo, const struct _rtp_info *rtpinfo);

View File

@ -138,7 +138,6 @@ void rtpstream_mark(rtpstream_tapinfo_t *tapinfo, capture_file *cap_file, rtpstr
remove_tap_listener_rtpstream(tapinfo);
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*

View File

@ -90,6 +90,9 @@ struct _rtpstream_tapinfo {
void *tap_data; /**< data for tap callbacks */
int nstreams; /**< number of streams in the list */
GList *strinfo_list; /**< list of rtpstream_info_t* */
GHashTable *strinfo_hash; /**< multihash of rtpstream_info_t **/
/* multihash means that there can be */
/* more values related to one hash key */
int npackets; /**< total number of rtp packets of all streams */
/* used while tapping. user shouldn't modify these */
tap_mode_t mode;

View File

@ -65,6 +65,26 @@ void rtpstream_id_free(rtpstream_id_t *id)
memset(id, 0, sizeof(*id));
}
/****************************************************************************/
/* convert rtpstream_id_t to hash */
guint rtpstream_id_to_hash(const rtpstream_id_t *id)
{
guint hash = 0;
if (!id) { return 0; }
/* XOR of: */
/* SRC PORT | DST_PORT */
/* SSRC */
/* SRC ADDR */
/* DST ADDR */
hash ^= id->src_port | id->dst_port << 16;
hash ^= id->ssrc;
add_address_to_hash(hash, &id->src_addr);
add_address_to_hash(hash, &id->dst_addr);
return hash;
}
/****************************************************************************/
/* compare two ids by flags */
gboolean rtpstream_id_equal(const rtpstream_id_t *id1, const rtpstream_id_t *id2, guint flags)
@ -104,6 +124,27 @@ gboolean rtpstream_id_equal_pinfo_rtp_info(const rtpstream_id_t *id, const packe
return FALSE;
}
/****************************************************************************/
/* convert packet_info and _rtp_info to hash */
guint pinfo_rtp_info_to_hash(const packet_info *pinfo, const struct _rtp_info *rtp_info)
{
guint hash = 0;
if (!pinfo || !rtp_info) { return 0; }
/* XOR of: */
/* SRC PORT | DST_PORT */
/* SSRC */
/* SRC ADDR */
/* DST ADDR */
hash ^= pinfo->srcport | pinfo->destport << 16;
hash ^= rtp_info->info_sync_src;
add_address_to_hash(hash, &pinfo->src);
add_address_to_hash(hash, &pinfo->dst);
return hash;
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*

View File

@ -37,6 +37,11 @@ typedef struct _rtpstream_id {
guint32 ssrc;
} rtpstream_id_t;
/**
* Get hash of rtpstream_id
*/
guint rtpstream_id_to_hash(const rtpstream_id_t *id);
/**
* Copy rtpstream_id_t structure
*/
@ -70,6 +75,11 @@ gboolean rtpstream_id_equal(const rtpstream_id_t *id1, const rtpstream_id_t *id2
*/
gboolean rtpstream_id_equal_pinfo_rtp_info(const rtpstream_id_t *id, const packet_info *pinfo, const struct _rtp_info *rtp_info);
/**
* Get hash of rtpstream_id extracted from packet_info and _rtp_info
*/
guint pinfo_rtp_info_to_hash(const packet_info *pinfo, const struct _rtp_info *rtp_info);
#ifdef __cplusplus
}
#endif /* __cplusplus */

View File

@ -147,6 +147,10 @@ void rtpstream_reset(rtpstream_tapinfo_t *tapinfo)
if (tapinfo->mode == TAP_ANALYSE) {
/* free the data items first */
if (tapinfo->strinfo_hash) {
g_hash_table_foreach(tapinfo->strinfo_hash, rtpstream_info_multihash_destroy_value, NULL);
g_hash_table_destroy(tapinfo->strinfo_hash);
}
list = g_list_first(tapinfo->strinfo_list);
while (list)
{
@ -157,6 +161,7 @@ void rtpstream_reset(rtpstream_tapinfo_t *tapinfo)
}
g_list_free(tapinfo->strinfo_list);
tapinfo->strinfo_list = NULL;
tapinfo->strinfo_hash = NULL;
tapinfo->nstreams = 0;
tapinfo->npackets = 0;
}
@ -366,7 +371,6 @@ tap_packet_status rtpstream_packet_cb(void *arg, packet_info *pinfo, epan_dissec
const struct _rtp_info *rtpinfo = (const struct _rtp_info *)arg2;
rtpstream_info_t new_stream_info;
rtpstream_info_t *stream_info = NULL;
GList* list;
rtpdump_info_t rtpdump_info;
struct _rtp_conversation_info *p_conv_data = NULL;
@ -386,15 +390,8 @@ tap_packet_status rtpstream_packet_cb(void *arg, packet_info *pinfo, epan_dissec
}
/* check whether we already have a stream with these parameters in the list */
list = g_list_first(tapinfo->strinfo_list);
while (list)
{
if (rtpstream_info_cmp(&new_stream_info, (rtpstream_info_t *)(list->data))==0)
{
stream_info = (rtpstream_info_t *)(list->data); /*found!*/
break;
}
list = g_list_next(list);
if (tapinfo->strinfo_hash) {
stream_info = rtpstream_info_multihash_lookup(tapinfo->strinfo_hash, &new_stream_info);
}
/* not in the list? then create a new entry */
@ -419,6 +416,10 @@ tap_packet_status rtpstream_packet_cb(void *arg, packet_info *pinfo, epan_dissec
stream_info = rtpstream_info_malloc_and_init();
rtpstream_info_copy_deep(stream_info, &new_stream_info);
tapinfo->strinfo_list = g_list_prepend(tapinfo->strinfo_list, stream_info);
if (!tapinfo->strinfo_hash) {
tapinfo->strinfo_hash = g_hash_table_new(g_direct_hash, g_direct_equal);
}
rtpstream_info_multihash_insert(tapinfo->strinfo_hash, stream_info);
}
/* get RTP stats for the packet */
@ -535,6 +536,77 @@ void rtpstream_info_calc_free(rtpstream_info_calc_t *calc)
wmem_free(NULL, calc->all_payload_type_names);
}
/****************************************************************************/
/* Get hash for rtpstream_info_t */
guint rtpstream_to_hash(gconstpointer key)
{
if (key) {
return rtpstream_id_to_hash(&((rtpstream_info_t *)key)->id);
} else {
return 0;
}
}
/****************************************************************************/
/* Inserts new_stream_info to multihash if its not there */
void rtpstream_info_multihash_insert(GHashTable *multihash, rtpstream_info_t *new_stream_info)
{
GList *hlist = (GList *)g_hash_table_lookup(multihash, GINT_TO_POINTER(rtpstream_to_hash(new_stream_info)));
gboolean found = FALSE;
if (hlist) {
// Key exists in hash
GList *list = g_list_first(hlist);
while (list)
{
if (rtpstream_info_cmp(new_stream_info, (rtpstream_info_t *)(list->data))==0) {
found = TRUE;
break;
}
list = g_list_next(list);
}
if (!found) {
// stream_info is not in list yet, add it
hlist = g_list_prepend(hlist, new_stream_info);
}
} else {
// No key in hash, init new list
hlist = g_list_prepend(hlist, new_stream_info);
}
g_hash_table_insert(multihash, GINT_TO_POINTER(rtpstream_to_hash(new_stream_info)), hlist);
}
/****************************************************************************/
/* Lookup stream_info in multihash */
rtpstream_info_t *rtpstream_info_multihash_lookup(GHashTable *multihash, rtpstream_info_t *stream_info)
{
GList *hlist = (GList *)g_hash_table_lookup(multihash, GINT_TO_POINTER(rtpstream_to_hash(stream_info)));
if (hlist) {
// Key exists in hash
GList *list = g_list_first(hlist);
while (list)
{
if (rtpstream_info_cmp(stream_info, (rtpstream_info_t *)(list->data))==0) {
return (rtpstream_info_t *)(list->data);
}
list = g_list_next(list);
}
}
// No stream_info in hash or was not found in existing list
return NULL;
}
/****************************************************************************/
/* Destroys GList used in multihash */
void rtpstream_info_multihash_destroy_value(gpointer key _U_, gpointer value, gpointer user_data _U_)
{
g_list_free((GList *)value);
}
/*
* Editor modelines - https://www.wireshark.org/tools/modelines.html
*

View File

@ -138,6 +138,26 @@ void rtpstream_info_calculate(const rtpstream_info_t *strinfo, rtpstream_info_ca
*/
void rtpstream_info_calc_free(rtpstream_info_calc_t *calc);
/**
* Get hash key for rtpstream_info_t
*/
guint rtpstream_to_hash(gconstpointer key);
/**
* Insert new_stream_info into multihash
*/
void rtpstream_info_multihash_insert(GHashTable *multihash, rtpstream_info_t *new_stream_info);
/**
* Lookup stream_info in stream_info multihash
*/
rtpstream_info_t *rtpstream_info_multihash_lookup(GHashTable *multihash, rtpstream_info_t *stream_info);
/**
* GHFunc () for destroying GList in multihash
*/
void rtpstream_info_multihash_destroy_value(gpointer key, gpointer value, gpointer user_data);
#ifdef __cplusplus
}
#endif /* __cplusplus */