/usr/include/x86_64-linux-gnu/qt6/QtCore/qsharedpointer_impl.h (2024)

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name rtp_player_dialog.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -fno-delete-null-pointer-checks -mframe-pointer=all -relaxed-aliasing -fmath-errno -ffp-contract=on -fno-rounding-math -ffloat16-excess-precision=fast -fbfloat16-excess-precision=fast -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/builds/wireshark/wireshark/build -fcoverage-compilation-dir=/builds/wireshark/wireshark/build -resource-dir /usr/lib/llvm-18/lib/clang/18 -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/build/ui/qt -isystem /builds/wireshark/wireshark/ui/qt -isystem /usr/include/x86_64-linux-gnu/qt6/QtWidgets -isystem /usr/include/x86_64-linux-gnu/qt6 -isystem /usr/include/x86_64-linux-gnu/qt6/QtCore -isystem /usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -isystem /usr/include/x86_64-linux-gnu/qt6/QtGui -isystem /usr/include/x86_64-linux-gnu/qt6/QtCore5Compat -isystem /usr/include/x86_64-linux-gnu/qt6/QtConcurrent -isystem /usr/include/x86_64-linux-gnu/qt6/QtPrintSupport -isystem /usr/include/x86_64-linux-gnu/qt6/QtMultimedia -isystem /usr/include/x86_64-linux-gnu/qt6/QtNetwork -isystem /usr/include/x86_64-linux-gnu/qt6/QtDBus -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D QT_CONCURRENT_LIB -D QT_CORE5COMPAT_LIB -D QT_CORE_LIB -D QT_DBUS_LIB -D QT_GUI_LIB -D QT_MULTIMEDIA_LIB -D QT_NETWORK_LIB -D QT_PRINTSUPPORT_LIB -D QT_WIDGETS_LIB -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build/ui/qt/qtui_autogen/include -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -D _GLIBCXX_ASSERTIONS -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-18/lib/clang/18/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -Wno-format-truncation -Wno-format-nonliteral -std=c++17 -fdeprecated-macro -ferror-limit 19 -fwrapv -fstrict-flex-arrays=3 -stack-protector 2 -fstack-clash-protection -fcf-protection=full -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcxx-exceptions -fexceptions -fcolor-diagnostics -analyzer-output=html -dwarf-debug-flags /usr/lib/llvm-18/bin/clang --driver-mode=g++ -### --analyze -x c++ -D G_DISABLE_DEPRECATED -D G_DISABLE_SINGLE_INCLUDES -D QT_CONCURRENT_LIB -D QT_CORE5COMPAT_LIB -D QT_CORE_LIB -D QT_DBUS_LIB -D QT_GUI_LIB -D QT_MULTIMEDIA_LIB -D QT_NETWORK_LIB -D QT_PRINTSUPPORT_LIB -D QT_WIDGETS_LIB -D WS_DEBUG -D WS_DEBUG_UTF_8 -I /builds/wireshark/wireshark/build/ui/qt/qtui_autogen/include -I /builds/wireshark/wireshark/build -I /builds/wireshark/wireshark -I /builds/wireshark/wireshark/include -isystem /usr/include/glib-2.0 -isystem /usr/lib/x86_64-linux-gnu/glib-2.0/include -isystem /builds/wireshark/wireshark/build/ui/qt -isystem /builds/wireshark/wireshark/ui/qt -isystem /usr/include/x86_64-linux-gnu/qt6/QtWidgets -isystem /usr/include/x86_64-linux-gnu/qt6 -isystem /usr/include/x86_64-linux-gnu/qt6/QtCore -isystem /usr/lib/x86_64-linux-gnu/qt6/mkspecs/linux-g++ -isystem /usr/include/x86_64-linux-gnu/qt6/QtGui -isystem /usr/include/x86_64-linux-gnu/qt6/QtCore5Compat -isystem /usr/include/x86_64-linux-gnu/qt6/QtConcurrent -isystem /usr/include/x86_64-linux-gnu/qt6/QtPrintSupport -isystem /usr/include/x86_64-linux-gnu/qt6/QtMultimedia -isystem /usr/include/x86_64-linux-gnu/qt6/QtNetwork -isystem /usr/include/x86_64-linux-gnu/qt6/QtDBus -fexcess-precision=fast -fstrict-flex-arrays=3 -fstack-clash-protection -fcf-protection=full -D _GLIBCXX_ASSERTIONS -fstack-protector-strong -fno-delete-null-pointer-checks -fno-strict-overflow -fno-strict-aliasing -fexceptions -Wno-format-truncation -Wno-format-nonliteral -fdiagnostics-color=always -fmacro-prefix-map=/builds/wireshark/wireshark/= -fmacro-prefix-map=/builds/wireshark/wireshark/build/= -fmacro-prefix-map=../= -std=c++17 -fPIC -fPIC /builds/wireshark/wireshark/ui/qt/rtp_player_dialog.cpp -o /builds/wireshark/wireshark/sbout/2024-08-28-100346-3897-1 -Xclang -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2024-08-28-100346-3897-1 -x c++ /builds/wireshark/wireshark/ui/qt/rtp_player_dialog.cpp

1/* rtp_player_dialog.cpp2 *3 * Wireshark - Network traffic analyzer4 * By Gerald Combs <[emailprotected]>5 * Copyright 1998 Gerald Combs6 *7 * SPDX-License-Identifier: GPL-2.0-or-later8 */9 10#include "config.h"11 12#include <ui/rtp_media.h>13#include <ui/tap-rtp-common.h>14#include "rtp_player_dialog.h"15#include <ui_rtp_player_dialog.h>16#include "epan/epan_dissect.h"17 18#include "file.h"19#include "frame_tvbuff.h"20 21#include "rtp_analysis_dialog.h"22 23#ifdef QT_MULTIMEDIA_LIB124 25#include <epan/dissectors/packet-rtp.h>26#include <epan/to_str.h>27 28#include <wsutil/report_message.h>29#include <wsutil/utf8_entities.h>30#include <wsutil/pint.h>31 32#include <ui/qt/utils/color_utils.h>33#include <ui/qt/widgets/qcustomplot.h>34#include <ui/qt/utils/qt_ui_utils.h>35#include "rtp_audio_stream.h"36#include <ui/qt/utils/tango_colors.h>37#include <widgets/rtp_audio_graph.h>38#include "main_application.h"39#include "ui/qt/widgets/wireshark_file_dialog.h"40 41#include <QAudio>42#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))43#include <algorithm>44#include <QAudioDevice>45#include <QAudioSink>46#include <QMediaDevices>47#else48#include <QAudioDeviceInfo>49#endif50#include <QFrame>51#include <QMenu>52#include <QVBoxLayout>53#include <QTimer>54 55#include <QAudioFormat>56#include <QAudioOutput>57#include <ui/qt/utils/rtp_audio_silence_generator.h>58 59#endif // QT_MULTIMEDIA_LIB60 61#include <QPushButton>62#include <QToolButton>63 64#include <ui/qt/utils/stock_icon.h>65#include "main_application.h"66 67// To do:68// - Threaded decoding?69 70// Current and former RTP player bugs. Many have attachments that can be usef for testing.71// Bug 3368 - The timestamp line in a RTP or RTCP packet display's "Not Representable"72// Bug 3952 - VoIP Call RTP Player: audio played is corrupted when RFC2833 packets are present73// Bug 4960 - RTP Player: Audio and visual feedback get rapidly out of sync74// Bug 5527 - Adding arbitrary value to x-axis RTP player75// Bug 7935 - Wrong Timestamps in RTP Player-Decode76// Bug 8007 - UI gets confused on playing decoded audio in rtp_player77// Bug 9007 - Switching SSRC values in RTP stream78// Bug 10613 - RTP audio player crashes79// Bug 11125 - RTP Player does not show progress in selected stream in Window 780// Bug 11409 - Wireshark crashes when using RTP player81// Bug 12166 - RTP audio player crashes82 83// In some places we match by conv/call number, in others we match by first frame.84 85enum {86 channel_col_,87 src_addr_col_,88 src_port_col_,89 dst_addr_col_,90 dst_port_col_,91 ssrc_col_,92 first_pkt_col_,93 num_pkts_col_,94 time_span_col_,95 sample_rate_col_,96 play_rate_col_,97 payload_col_,98 99 stream_data_col_ = src_addr_col_, // RtpAudioStream100 graph_audio_data_col_ = src_port_col_, // QCPGraph (wave)101 graph_sequence_data_col_ = dst_addr_col_, // QCPGraph (sequence)102 graph_jitter_data_col_ = dst_port_col_, // QCPGraph (jitter)103 graph_timestamp_data_col_ = ssrc_col_, // QCPGraph (timestamp)104 // first_pkt_col_ is skipped, it is used for real data105 graph_silence_data_col_ = num_pkts_col_, // QCPGraph (silence)106};107 108class RtpPlayerTreeWidgetItem : public QTreeWidgetItem109{110public:111 RtpPlayerTreeWidgetItem(QTreeWidget *tree) :112 QTreeWidgetItem(tree)113 {114 }115 116 bool operator< (const QTreeWidgetItem &other) const117 {118 // Handle numeric sorting119 switch (treeWidget()->sortColumn()) {120 case src_port_col_:121 case dst_port_col_:122 case num_pkts_col_:123 case sample_rate_col_:124 return text(treeWidget()->sortColumn()).toInt() < other.text(treeWidget()->sortColumn()).toInt();125 case play_rate_col_:126 return text(treeWidget()->sortColumn()).toInt() < other.text(treeWidget()->sortColumn()).toInt();127 case first_pkt_col_:128 int v1;129 int v2;130 131 v1 = data(first_pkt_col_, Qt::UserRole).toInt();132 v2 = other.data(first_pkt_col_, Qt::UserRole).toInt();133 134 return v1 < v2;135 default:136 // Fall back to string comparison137 return QTreeWidgetItem::operator <(other);138 }139 }140};141 142RtpPlayerDialog *RtpPlayerDialog::pinstance_{nullptr};143std::mutex RtpPlayerDialog::init_mutex_;144std::mutex RtpPlayerDialog::run_mutex_;145 146RtpPlayerDialog *RtpPlayerDialog::openRtpPlayerDialog(QWidget &parent, CaptureFile &cf, QObject *packet_list, bool capture_running)147{148 std::lock_guard<std::mutex> lock(init_mutex_);149 if (pinstance_ == nullptr)150 {151 pinstance_ = new RtpPlayerDialog(parent, cf, capture_running);152 connect(pinstance_, SIGNAL(goToPacket(int))qFlagLocation("2" "goToPacket(int)" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "152")
,153 packet_list, SLOT(goToPacket(int))qFlagLocation("1" "goToPacket(int)" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "153")
);154 }155 return pinstance_;156}157 158RtpPlayerDialog::RtpPlayerDialog(QWidget &parent, CaptureFile &cf, bool capture_running _U___attribute__((unused))) :159 WiresharkDialog(parent, cf)160#ifdef QT_MULTIMEDIA_LIB1161 , ui(new Ui::RtpPlayerDialog)162 , first_stream_rel_start_time_(0.0)163 , first_stream_abs_start_time_(0.0)164 , first_stream_rel_stop_time_(0.0)165 , streams_length_(0.0)166 , start_marker_time_(0.0)167 , number_ticker_(new QCPAxisTicker)168 , datetime_ticker_(new QCPAxisTickerDateTime)169 , stereo_available_(false)170 , marker_stream_(0)171 , marker_stream_requested_out_rate_(0)172 , last_ti_(0)173 , listener_removed_(true)174 , block_redraw_(false)175 , lock_ui_(0)176 , read_capture_enabled_(capture_running)177 , silence_skipped_time_(0.0)178#endif // QT_MULTIMEDIA_LIB179{180 ui->setupUi(this);181 loadGeometry(parent.width(), parent.height());182 setWindowTitle(mainApp->windowTitleString(tr("RTP Player")));183 ui->streamTreeWidget->installEventFilter(this);184 ui->audioPlot->installEventFilter(this);185 installEventFilter(this);186 187#ifdef QT_MULTIMEDIA_LIB1188 ui->splitter->setStretchFactor(0, 3);189 ui->splitter->setStretchFactor(1, 1);190 191 ui->streamTreeWidget->sortByColumn(first_pkt_col_, Qt::AscendingOrder);192 193 graph_ctx_menu_ = new QMenu(this);194 195 graph_ctx_menu_->addAction(ui->actionZoomIn);196 graph_ctx_menu_->addAction(ui->actionZoomOut);197 graph_ctx_menu_->addAction(ui->actionReset);198 graph_ctx_menu_->addSeparator();199 graph_ctx_menu_->addAction(ui->actionMoveRight10);200 graph_ctx_menu_->addAction(ui->actionMoveLeft10);201 graph_ctx_menu_->addAction(ui->actionMoveRight1);202 graph_ctx_menu_->addAction(ui->actionMoveLeft1);203 graph_ctx_menu_->addSeparator();204 graph_ctx_menu_->addAction(ui->actionGoToPacket);205 graph_ctx_menu_->addAction(ui->actionGoToSetupPacketPlot);206 set_action_shortcuts_visible_in_context_menu(graph_ctx_menu_->actions());207 208 ui->audioPlot->setContextMenuPolicy(Qt::CustomContextMenu);209 connect(ui->audioPlot, &QCustomPlot::customContextMenuRequested, this, &RtpPlayerDialog::showGraphContextMenu);210 211 ui->streamTreeWidget->setMouseTracking(true);212 mouse_update_timer_ = new QTimer(this);213 mouse_update_timer_->setSingleShot(true);214 mouse_update_timer_->setInterval(10);215 connect(mouse_update_timer_, &QTimer::timeout, this, &RtpPlayerDialog::mouseMoveUpdate);216 217 connect(ui->streamTreeWidget, &QTreeWidget::itemEntered, this, &RtpPlayerDialog::itemEntered);218 219 connect(ui->audioPlot, &QCustomPlot::mouseMove, this, &RtpPlayerDialog::mouseMovePlot);220 connect(ui->audioPlot, &QCustomPlot::mousePress, this, &RtpPlayerDialog::graphClicked);221 connect(ui->audioPlot, &QCustomPlot::mouseDoubleClick, this, &RtpPlayerDialog::graphDoubleClicked);222 connect(ui->audioPlot, &QCustomPlot::plottableClick, this, &RtpPlayerDialog::plotClicked);223 224 cur_play_pos_ = new QCPItemStraightLine(ui->audioPlot);225 cur_play_pos_->setVisible(false);226 227 start_marker_pos_ = new QCPItemStraightLine(ui->audioPlot);228 start_marker_pos_->setPen(QPen(Qt::green,4));229 setStartPlayMarker(0);230 drawStartPlayMarker();231 start_marker_pos_->setVisible(true);232 233#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))234 notify_timer_.setInterval(100); // ~15 fps235 connect(&notify_timer_, &QTimer::timeout, this, &RtpPlayerDialog::outputNotify);236#endif237 238 datetime_ticker_->setDateTimeFormat("yyyy-MM-dd\nhh:mm:ss.zzz");239 240 ui->audioPlot->xAxis->setNumberFormat("gb");241 ui->audioPlot->xAxis->setNumberPrecision(3);242 ui->audioPlot->xAxis->setTicker(datetime_ticker_);243 ui->audioPlot->yAxis->setVisible(false);244 245 ui->playButton->setIcon(StockIcon("media-playback-start"));246 ui->playButton->setEnabled(false);247 ui->pauseButton->setIcon(StockIcon("media-playback-pause"));248 ui->pauseButton->setCheckable(true);249 ui->pauseButton->setVisible(false);250 ui->stopButton->setIcon(StockIcon("media-playback-stop"));251 ui->stopButton->setEnabled(false);252 ui->skipSilenceButton->setIcon(StockIcon("media-seek-forward"));253 ui->skipSilenceButton->setCheckable(true);254 ui->skipSilenceButton->setEnabled(false);255 256 read_btn_ = ui->buttonBox->addButton(ui->actionReadCapture->text(), QDialogButtonBox::ActionRole);257 read_btn_->setToolTip(ui->actionReadCapture->toolTip());258 read_btn_->setEnabled(false);259 connect(read_btn_, &QPushButton::pressed, this, &RtpPlayerDialog::on_actionReadCapture_triggered);260 261 inaudible_btn_ = new QToolButton();262 ui->buttonBox->addButton(inaudible_btn_, QDialogButtonBox::ActionRole);263 inaudible_btn_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);264 inaudible_btn_->setPopupMode(QToolButton::MenuButtonPopup);265 266 connect(ui->actionInaudibleButton, &QAction::triggered, this, &RtpPlayerDialog::on_actionSelectInaudible_triggered);267 inaudible_btn_->setDefaultAction(ui->actionInaudibleButton);268 // Overrides text striping of shortcut undercode in QAction269 inaudible_btn_->setText(ui->actionInaudibleButton->text());270 inaudible_btn_->setEnabled(false);271 inaudible_btn_->setMenu(ui->menuInaudible);272 273 analyze_btn_ = RtpAnalysisDialog::addAnalyzeButton(ui->buttonBox, this);274 275 prepare_btn_ = ui->buttonBox->addButton(ui->actionPrepareFilter->text(), QDialogButtonBox::ActionRole);276 prepare_btn_->setToolTip(ui->actionPrepareFilter->toolTip());277 connect(prepare_btn_, &QPushButton::pressed, this, &RtpPlayerDialog::on_actionPrepareFilter_triggered);278 279 export_btn_ = ui->buttonBox->addButton(ui->actionExportButton->text(), QDialogButtonBox::ActionRole);280 export_btn_->setToolTip(ui->actionExportButton->toolTip());281 export_btn_->setEnabled(false);282 export_btn_->setMenu(ui->menuExport);283 284 // Ordered, unique device names starting with the system default285 QMap<QString, bool> out_device_map; // true == default device286#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))287 out_device_map.insert(QMediaDevices::defaultAudioOutput().description(), true);288 foreach (QAudioDevice out_device, QMediaDevices::audioOutputs())for (auto _container_288 = QtPrivate::qMakeForeachContainer(QMediaDevices
::audioOutputs()); _container_288.i != _container_288.e; ++_container_288
.i) if (QAudioDevice out_device = *_container_288.i; false) {
} else
{289 if (!out_device_map.contains(out_device.description())) {290 out_device_map.insert(out_device.description(), false);291 }292 }293#else294 out_device_map.insert(QAudioDeviceInfo::defaultOutputDevice().deviceName(), true);295 foreach (QAudioDeviceInfo out_device, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))for (auto _container_295 = QtPrivate::qMakeForeachContainer(QAudioDeviceInfo
::availableDevices(QAudio::AudioOutput)); _container_295.i !=
_container_295.e; ++_container_295.i) if (QAudioDeviceInfo out_device
= *_container_295.i; false) {} else
{296 if (!out_device_map.contains(out_device.deviceName())) {297 out_device_map.insert(out_device.deviceName(), false);298 }299 }300#endif301 302 ui->outputDeviceComboBox->blockSignals(true);303 foreach (QString out_name, out_device_map.keys())for (auto _container_303 = QtPrivate::qMakeForeachContainer(out_device_map
.keys()); _container_303.i != _container_303.e; ++_container_303
.i) if (QString out_name = *_container_303.i; false) {} else
{304 ui->outputDeviceComboBox->addItem(out_name);305 if (out_device_map.value(out_name)) {306 ui->outputDeviceComboBox->setCurrentIndex(ui->outputDeviceComboBox->count() - 1);307 }308 }309 if (ui->outputDeviceComboBox->count() < 1) {310 ui->outputDeviceComboBox->setEnabled(false);311 ui->playButton->setEnabled(false);312 ui->pauseButton->setEnabled(false);313 ui->stopButton->setEnabled(false);314 ui->skipSilenceButton->setEnabled(false);315 ui->minSilenceSpinBox->setEnabled(false);316 ui->outputDeviceComboBox->addItem(tr("No devices available"));317 ui->outputAudioRate->setEnabled(false);318 } else {319 stereo_available_ = isStereoAvailable();320 fillAudioRateMenu();321 }322 ui->outputDeviceComboBox->blockSignals(false);323 324 ui->audioPlot->setMouseTracking(true);325 ui->audioPlot->setEnabled(true);326 ui->audioPlot->setInteractions(327 QCP::iRangeDrag |328 QCP::iRangeZoom329 );330 331 graph_ctx_menu_->addSeparator();332 list_ctx_menu_ = new QMenu(this);333 list_ctx_menu_->addAction(ui->actionPlay);334 graph_ctx_menu_->addAction(ui->actionPlay);335 list_ctx_menu_->addAction(ui->actionStop);336 graph_ctx_menu_->addAction(ui->actionStop);337 list_ctx_menu_->addMenu(ui->menuSelect);338 graph_ctx_menu_->addMenu(ui->menuSelect);339 list_ctx_menu_->addMenu(ui->menuAudioRouting);340 graph_ctx_menu_->addMenu(ui->menuAudioRouting);341 list_ctx_menu_->addAction(ui->actionRemoveStream);342 graph_ctx_menu_->addAction(ui->actionRemoveStream);343 list_ctx_menu_->addAction(ui->actionGoToSetupPacketTree);344 set_action_shortcuts_visible_in_context_menu(list_ctx_menu_->actions());345 346 connect(&cap_file_, &CaptureFile::captureEvent, this, &RtpPlayerDialog::captureEvent);347 connect(this, SIGNAL(updateFilter(QString, bool))qFlagLocation("2" "updateFilter(QString, bool)" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "347")
,348 &parent, SLOT(filterPackets(QString, bool))qFlagLocation("1" "filterPackets(QString, bool)" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "348")
);349 connect(this, SIGNAL(rtpAnalysisDialogReplaceRtpStreams(QVector<rtpstream_id_t *>))qFlagLocation("2" "rtpAnalysisDialogReplaceRtpStreams(QVector<rtpstream_id_t *>)"
"\0" "ui/qt/rtp_player_dialog.cpp" ":" "349")
,350 &parent, SLOT(rtpAnalysisDialogReplaceRtpStreams(QVector<rtpstream_id_t *>))qFlagLocation("1" "rtpAnalysisDialogReplaceRtpStreams(QVector<rtpstream_id_t *>)"
"\0" "ui/qt/rtp_player_dialog.cpp" ":" "350")
);351 connect(this, SIGNAL(rtpAnalysisDialogAddRtpStreams(QVector<rtpstream_id_t *>))qFlagLocation("2" "rtpAnalysisDialogAddRtpStreams(QVector<rtpstream_id_t *>)"
"\0" "ui/qt/rtp_player_dialog.cpp" ":" "351")
,352 &parent, SLOT(rtpAnalysisDialogAddRtpStreams(QVector<rtpstream_id_t *>))qFlagLocation("1" "rtpAnalysisDialogAddRtpStreams(QVector<rtpstream_id_t *>)"
"\0" "ui/qt/rtp_player_dialog.cpp" ":" "352")
);353 connect(this, SIGNAL(rtpAnalysisDialogRemoveRtpStreams(QVector<rtpstream_id_t *>))qFlagLocation("2" "rtpAnalysisDialogRemoveRtpStreams(QVector<rtpstream_id_t *>)"
"\0" "ui/qt/rtp_player_dialog.cpp" ":" "353")
,354 &parent, SLOT(rtpAnalysisDialogRemoveRtpStreams(QVector<rtpstream_id_t *>))qFlagLocation("1" "rtpAnalysisDialogRemoveRtpStreams(QVector<rtpstream_id_t *>)"
"\0" "ui/qt/rtp_player_dialog.cpp" ":" "354")
);355#endif // QT_MULTIMEDIA_LIB356}357 358// _U_ is used when no QT_MULTIMEDIA_LIB is available359QToolButton *RtpPlayerDialog::addPlayerButton(QDialogButtonBox *button_box, QDialog *dialog _U___attribute__((unused)))360{361 if (!button_box) return NULL__null;362 363 QAction *ca;364 QToolButton *player_button = new QToolButton();365 button_box->addButton(player_button, QDialogButtonBox::ActionRole);366 player_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);367 player_button->setPopupMode(QToolButton::MenuButtonPopup);368 369 ca = new QAction(tr("&Play Streams"), player_button);370 ca->setToolTip(tr("Open RTP player dialog"));371 ca->setIcon(StockIcon("media-playback-start"));372 connect(ca, SIGNAL(triggered())qFlagLocation("2" "triggered()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "372")
, dialog, SLOT(rtpPlayerReplace())qFlagLocation("1" "rtpPlayerReplace()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "372")
);373 player_button->setDefaultAction(ca);374 // Overrides text striping of shortcut undercode in QAction375 player_button->setText(ca->text());376 377#if defined(QT_MULTIMEDIA_LIB1)378 QMenu *button_menu = new QMenu(player_button);379 button_menu->setToolTipsVisible(true);380 ca = button_menu->addAction(tr("&Set playlist"));381 ca->setToolTip(tr("Replace existing playlist in RTP Player with new one"));382 connect(ca, SIGNAL(triggered())qFlagLocation("2" "triggered()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "382")
, dialog, SLOT(rtpPlayerReplace())qFlagLocation("1" "rtpPlayerReplace()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "382")
);383 ca = button_menu->addAction(tr("&Add to playlist"));384 ca->setToolTip(tr("Add new set to existing playlist in RTP Player"));385 connect(ca, SIGNAL(triggered())qFlagLocation("2" "triggered()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "385")
, dialog, SLOT(rtpPlayerAdd())qFlagLocation("1" "rtpPlayerAdd()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "385")
);386 ca = button_menu->addAction(tr("&Remove from playlist"));387 ca->setToolTip(tr("Remove selected streams from playlist in RTP Player"));388 connect(ca, SIGNAL(triggered())qFlagLocation("2" "triggered()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "388")
, dialog, SLOT(rtpPlayerRemove())qFlagLocation("1" "rtpPlayerRemove()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "388")
);389 player_button->setMenu(button_menu);390#else391 player_button->setEnabled(false);392 player_button->setText(tr("No Audio"));393#endif394 395 return player_button;396}397 398#ifdef QT_MULTIMEDIA_LIB1399RtpPlayerDialog::~RtpPlayerDialog()400{401 std::lock_guard<std::mutex> lock(init_mutex_);402 if (pinstance_ != nullptr) {403 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {404 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);405 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();406 if (audio_stream)407 delete audio_stream;408 }409 cleanupMarkerStream();410 delete ui;411 pinstance_ = nullptr;412 }413}414 415void RtpPlayerDialog::accept()416{417 if (!listener_removed_) {418 remove_tap_listener(this);419 listener_removed_ = true;420 }421 422 int row_count = ui->streamTreeWidget->topLevelItemCount();423 // Stop all streams before the dialogs are closed.424 for (int row = 0; row < row_count; row++) {425 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);426 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();427 audio_stream->stopPlaying();428 }429 WiresharkDialog::accept();430}431 432void RtpPlayerDialog::reject()433{434 RtpPlayerDialog::accept();435}436 437void RtpPlayerDialog::retapPackets()438{439 if (!listener_removed_) {

1

Assuming field 'listener_removed_' is true

2

Taking false branch

440 // Retap is running, nothing better we can do441 return;442 }443 lockUI();444 ui->hintLabel->setText("<i><small>" + tr("Decoding streams...") + "</i></small>");445 mainApp->processEvents();446 447 // Clear packets from existing streams before retap448 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {

3

Assuming the condition is false

4

Loop condition is false. Execution continues on line 458

449 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);450 RtpAudioStream *row_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();451 452 row_stream->clearPackets();453 }454 455 // destroyCheck is protection againts destroying dialog during recap.456 // It stores dialog pointer in data() and if dialog destroyed, it457 // returns null458 QPointer<RtpPlayerDialog> destroyCheck=this;459 GString *error_string;460 461 listener_removed_ = false;462 error_string = register_tap_listener("rtp", this, NULL__null, 0, NULL__null, tapPacket, NULL__null, NULL__null);463 if (error_string) {

5

Assuming 'error_string' is non-null

6

Taking true branch

464 report_failure("RTP Player - tap registration failed: %s", error_string->str);465 g_string_free(error_string, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) (
(error_string), ((!(0)))) : g_string_free_and_steal (error_string
)) : (g_string_free) ((error_string), ((!(0)))))
;

7

'?' condition is true

466 unlockUI();467 return;

8

Calling implicit destructor for 'QPointer<RtpPlayerDialog>'

9

Calling '~QWeakPointer'

468 }469 cap_file_.retapPackets();470 471 // Check if dialog exists still472 if (destroyCheck.data()) {473 if (!listener_removed_) {474 remove_tap_listener(this);475 listener_removed_ = true;476 }477 fillTappedColumns();478 rescanPackets(true);479 }480 unlockUI();481}482 483void RtpPlayerDialog::rescanPackets(bool rescale_axes)484{485 lockUI();486 // Show information for a user - it can last long time...487 playback_error_.clear();488 ui->hintLabel->setText("<i><small>" + tr("Decoding streams...") + "</i></small>");489 mainApp->processEvents();490 491#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))492 QAudioDevice cur_out_device = getCurrentDeviceInfo();493#else494 QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo();495#endif496 int row_count = ui->streamTreeWidget->topLevelItemCount();497 498 // Reset stream values499 for (int row = 0; row < row_count; row++) {500 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);501 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();502 audio_stream->setStereoRequired(stereo_available_);503 audio_stream->reset(first_stream_rel_start_time_);504 505 audio_stream->setJitterBufferSize((int) ui->jitterSpinBox->value());506 507 RtpAudioStream::TimingMode timing_mode = RtpAudioStream::JitterBuffer;508 switch (ui->timingComboBox->currentIndex()) {509 case RtpAudioStream::RtpTimestamp:510 timing_mode = RtpAudioStream::RtpTimestamp;511 break;512 case RtpAudioStream::Uninterrupted:513 timing_mode = RtpAudioStream::Uninterrupted;514 break;515 default:516 break;517 }518 audio_stream->setTimingMode(timing_mode);519 520 //if (!cur_out_device.isNull()) {521 audio_stream->decode(cur_out_device);522 //}523 }524 525 for (int col = 0; col < ui->streamTreeWidget->columnCount() - 1; col++) {526 ui->streamTreeWidget->resizeColumnToContents(col);527 }528 529 createPlot(rescale_axes);530 531 updateWidgets();532 unlockUI();533}534 535void RtpPlayerDialog::createPlot(bool rescale_axes)536{537 bool legend_out_of_sequence = false;538 bool legend_jitter_dropped = false;539 bool legend_wrong_timestamps = false;540 bool legend_inserted_silences = false;541 bool relative_timestamps = !ui->todCheckBox->isChecked();542 int row_count = ui->streamTreeWidget->topLevelItemCount();543 int16_t total_max_sample_value = 1;544 545 ui->audioPlot->clearGraphs();546 547 if (relative_timestamps) {548 ui->audioPlot->xAxis->setTicker(number_ticker_);549 } else {550 ui->audioPlot->xAxis->setTicker(datetime_ticker_);551 }552 553 // Calculate common Y scale for graphs554 for (int row = 0; row < row_count; row++) {555 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);556 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();557 int16_t max_sample_value = audio_stream->getMaxSampleValue();558 559 if (max_sample_value > total_max_sample_value) {560 total_max_sample_value = max_sample_value;561 }562 }563 564 // Clear existing graphs565 for (int row = 0; row < row_count; row++) {566 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);567 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();568 int y_offset = row_count - row - 1;569 AudioRouting audio_routing = audio_stream->getAudioRouting();570 571 ti->setData(graph_audio_data_col_, Qt::UserRole, QVariant());572 ti->setData(graph_sequence_data_col_, Qt::UserRole, QVariant());573 ti->setData(graph_jitter_data_col_, Qt::UserRole, QVariant());574 ti->setData(graph_timestamp_data_col_, Qt::UserRole, QVariant());575 ti->setData(graph_silence_data_col_, Qt::UserRole, QVariant());576 577 // Set common scale578 audio_stream->setMaxSampleValue(total_max_sample_value);579 580 // Waveform581 RtpAudioGraph *audio_graph = new RtpAudioGraph(ui->audioPlot, audio_stream->color());582 audio_graph->setMuted(audio_routing.isMuted());583 audio_graph->setData(audio_stream->visualTimestamps(relative_timestamps), audio_stream->visualSamples(y_offset));584 ti->setData(graph_audio_data_col_, Qt::UserRole, QVariant::fromValue<RtpAudioGraph *>(audio_graph));585 //RTP_STREAM_DEBUG("Plotting %s, %d samples", ti->text(src_addr_col_).toUtf8().constData(), audio_graph->wave->data()->size());586 587 QString span_str;588 if (ui->todCheckBox->isChecked()) {589 QDateTime date_time1 = QDateTime::fromMSecsSinceEpoch((audio_stream->startRelTime() + first_stream_abs_start_time_ - audio_stream->startRelTime()) * 1000.0);590 QDateTime date_time2 = QDateTime::fromMSecsSinceEpoch((audio_stream->stopRelTime() + first_stream_abs_start_time_ - audio_stream->startRelTime()) * 1000.0);591 QString time_str1 = date_time1.toString("yyyy-MM-dd hh:mm:ss.zzz");592 QString time_str2 = date_time2.toString("yyyy-MM-dd hh:mm:ss.zzz");593 span_str = QString("%1 - %2 (%3)")594 .arg(time_str1)595 .arg(time_str2)596 .arg(QString::number(audio_stream->stopRelTime() - audio_stream->startRelTime(), 'f', prefs.gui_decimal_places1));597 } else {598 span_str = QString("%1 - %2 (%3)")599 .arg(QString::number(audio_stream->startRelTime(), 'f', prefs.gui_decimal_places1))600 .arg(QString::number(audio_stream->stopRelTime(), 'f', prefs.gui_decimal_places1))601 .arg(QString::number(audio_stream->stopRelTime() - audio_stream->startRelTime(), 'f', prefs.gui_decimal_places1));602 }603 ti->setText(time_span_col_, span_str);604 ti->setText(sample_rate_col_, QString::number(audio_stream->sampleRate()));605 ti->setText(play_rate_col_, QString::number(audio_stream->playRate()));606 ti->setText(payload_col_, audio_stream->payloadNames().join(", "));607 608 if (audio_stream->outOfSequence() > 0) {609 // Sequence numbers610 QCPGraph *seq_graph = ui->audioPlot->addGraph();611 seq_graph->setLineStyle(QCPGraph::lsNone);612 seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssSquare, tango_aluminium_6, Qt::white, mainApp->font().pointSize())); // Arbitrary613 seq_graph->setSelectable(QCP::stNone);614 seq_graph->setData(audio_stream->outOfSequenceTimestamps(relative_timestamps), audio_stream->outOfSequenceSamples(y_offset));615 ti->setData(graph_sequence_data_col_, Qt::UserRole, QVariant::fromValue<QCPGraph *>(seq_graph));616 if (legend_out_of_sequence) {617 seq_graph->removeFromLegend();618 } else {619 seq_graph->setName(tr("Out of Sequence"));620 legend_out_of_sequence = true;621 }622 }623 624 if (audio_stream->jitterDropped() > 0) {625 // Jitter drops626 QCPGraph *seq_graph = ui->audioPlot->addGraph();627 seq_graph->setLineStyle(QCPGraph::lsNone);628 seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, tango_scarlet_red_5, Qt::white, mainApp->font().pointSize())); // Arbitrary629 seq_graph->setSelectable(QCP::stNone);630 seq_graph->setData(audio_stream->jitterDroppedTimestamps(relative_timestamps), audio_stream->jitterDroppedSamples(y_offset));631 ti->setData(graph_jitter_data_col_, Qt::UserRole, QVariant::fromValue<QCPGraph *>(seq_graph));632 if (legend_jitter_dropped) {633 seq_graph->removeFromLegend();634 } else {635 seq_graph->setName(tr("Jitter Drops"));636 legend_jitter_dropped = true;637 }638 }639 640 if (audio_stream->wrongTimestamps() > 0) {641 // Wrong timestamps642 QCPGraph *seq_graph = ui->audioPlot->addGraph();643 seq_graph->setLineStyle(QCPGraph::lsNone);644 seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDiamond, tango_sky_blue_5, Qt::white, mainApp->font().pointSize())); // Arbitrary645 seq_graph->setSelectable(QCP::stNone);646 seq_graph->setData(audio_stream->wrongTimestampTimestamps(relative_timestamps), audio_stream->wrongTimestampSamples(y_offset));647 ti->setData(graph_timestamp_data_col_, Qt::UserRole, QVariant::fromValue<QCPGraph *>(seq_graph));648 if (legend_wrong_timestamps) {649 seq_graph->removeFromLegend();650 } else {651 seq_graph->setName(tr("Wrong Timestamps"));652 legend_wrong_timestamps = true;653 }654 }655 656 if (audio_stream->insertedSilences() > 0) {657 // Inserted silence658 QCPGraph *seq_graph = ui->audioPlot->addGraph();659 seq_graph->setLineStyle(QCPGraph::lsNone);660 seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssTriangle, tango_butter_5, Qt::white, mainApp->font().pointSize())); // Arbitrary661 seq_graph->setSelectable(QCP::stNone);662 seq_graph->setData(audio_stream->insertedSilenceTimestamps(relative_timestamps), audio_stream->insertedSilenceSamples(y_offset));663 ti->setData(graph_silence_data_col_, Qt::UserRole, QVariant::fromValue<QCPGraph *>(seq_graph));664 if (legend_inserted_silences) {665 seq_graph->removeFromLegend();666 } else {667 seq_graph->setName(tr("Inserted Silence"));668 legend_inserted_silences = true;669 }670 }671 }672 ui->audioPlot->legend->setVisible(legend_out_of_sequence || legend_jitter_dropped || legend_wrong_timestamps || legend_inserted_silences);673 674 ui->audioPlot->replot();675 if (rescale_axes) resetXAxis();676}677 678void RtpPlayerDialog::fillTappedColumns()679{680 // true just for first stream681 bool is_first = true;682 683 // Get all rows, immutable list. Later changes in rows might reorder them684 QList<QTreeWidgetItem *> items = ui->streamTreeWidget->findItems(685 QString("*"), Qt::MatchWrap | Qt::MatchWildcard | Qt::MatchRecursive);686 687 // Update rows by calculated values, it might reorder them in view...688 foreach(QTreeWidgetItem *ti, items)for (auto _container_688 = QtPrivate::qMakeForeachContainer(items
); _container_688.i != _container_688.e; ++_container_688.i) if
(QTreeWidgetItem *ti = *_container_688.i; false) {} else
{689 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();690 if (audio_stream) {691 rtpstream_info_t *rtpstream = audio_stream->getStreamInfo();692 693 // 0xFFFFFFFF mean no setup frame694 // first_packet_num == setup_frame_number happens, when695 // rtp_udp is active or Decode as was used696 if ((rtpstream->setup_frame_number == 0xFFFFFFFF) ||697 (rtpstream->rtp_stats.first_packet_num == rtpstream->setup_frame_number)698 ) {699 int packet = rtpstream->rtp_stats.first_packet_num;700 ti->setText(first_pkt_col_, QString("RTP %1").arg(packet));701 ti->setData(first_pkt_col_, Qt::UserRole, QVariant(packet));702 } else {703 int packet = rtpstream->setup_frame_number;704 ti->setText(first_pkt_col_, QString("SETUP %1").arg(rtpstream->setup_frame_number));705 ti->setData(first_pkt_col_, Qt::UserRole, QVariant(packet));706 }707 ti->setText(num_pkts_col_, QString::number(rtpstream->packet_count));708 updateStartStopTime(rtpstream, is_first);709 is_first = false;710 }711 }712 setMarkers();713}714 715void RtpPlayerDialog::addSingleRtpStream(rtpstream_id_t *id)716{717 bool found = false;718 719 AudioRouting audio_routing = AudioRouting(AUDIO_UNMUTEDfalse, channel_mono);720 721 if (!id) return;722 723 // Find the RTP streams associated with this conversation.724 // gtk/rtp_player.c:mark_rtp_stream_to_play does this differently.725 726 QList<RtpAudioStream *> streams = stream_hash_.values(rtpstream_id_to_hash(id));727 for (int i = 0; i < streams.size(); i++) {728 RtpAudioStream *row_stream = streams.at(i);729 if (row_stream->isMatch(id)) {730 found = true;731 break;732 }733 }734 735 736 if (found) {737 return;738 }739 740 try {741 int tli_count = ui->streamTreeWidget->topLevelItemCount();742 743 RtpAudioStream *audio_stream = new RtpAudioStream(this, id, stereo_available_);744 audio_stream->setColor(ColorUtils::graphColor(tli_count));745 746 QTreeWidgetItem *ti = new RtpPlayerTreeWidgetItem(ui->streamTreeWidget);747 stream_hash_.insert(rtpstream_id_to_hash(id), audio_stream);748 ti->setText(src_addr_col_, address_to_qstring(&(id->src_addr)));749 ti->setText(src_port_col_, QString::number(id->src_port));750 ti->setText(dst_addr_col_, address_to_qstring(&(id->dst_addr)));751 ti->setText(dst_port_col_, QString::number(id->dst_port));752 ti->setText(ssrc_col_, int_to_qstring(id->ssrc, 8, 16));753 754 // Calculated items are updated after every retapPackets()755 756 ti->setData(stream_data_col_, Qt::UserRole, QVariant::fromValue(audio_stream));757 if (stereo_available_) {758 if (tli_count%2) {759 audio_routing.setChannel(channel_stereo_right);760 } else {761 audio_routing.setChannel(channel_stereo_left);762 }763 } else {764 audio_routing.setChannel(channel_mono);765 }766 ti->setToolTip(channel_col_, QString(tr("Double click on cell to change audio routing")));767 formatAudioRouting(ti, audio_routing);768 audio_stream->setAudioRouting(audio_routing);769 770 for (int col = 0; col < ui->streamTreeWidget->columnCount(); col++) {771 QBrush fgBrush = ti->foreground(col);772 fgBrush.setColor(audio_stream->color());773 fgBrush.setStyle(Qt::SolidPattern);774 ti->setForeground(col, fgBrush);775 }776 777 connect(audio_stream, &RtpAudioStream::finishedPlaying, this, &RtpPlayerDialog::playFinished);778 connect(audio_stream, &RtpAudioStream::playbackError, this, &RtpPlayerDialog::setPlaybackError);779 } catch (...) {780 qWarningQMessageLogger(static_cast<const char *>("ui/qt/rtp_player_dialog.cpp"
), 780, static_cast<const char *>(__PRETTY_FUNCTION__))
.warning
() << "Stream ignored, try to add fewer streams to playlist";781 }782 783 RTP_STREAM_DEBUG("adding stream %d to layout",784 ui->streamTreeWidget->topLevelItemCount());785}786 787void RtpPlayerDialog::lockUI()788{789 if (0 == lock_ui_++) {790 if (playing_streams_.count() > 0) {791 on_stopButton_clicked();792 }793 setEnabled(false);794 }795}796 797void RtpPlayerDialog::unlockUI()798{799 if (--lock_ui_ == 0) {800 setEnabled(true);801 }802}803 804void RtpPlayerDialog::replaceRtpStreams(QVector<rtpstream_id_t *> stream_ids)805{806 std::unique_lock<std::mutex> lock(run_mutex_, std::try_to_lock);807 if (lock.owns_lock()) {808 lockUI();809 810 // Delete all existing rows811 if (last_ti_) {812 highlightItem(last_ti_, false);813 last_ti_ = NULL__null;814 }815 816 for (int row = ui->streamTreeWidget->topLevelItemCount() - 1; row >= 0; row--) {817 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);818 removeRow(ti);819 }820 821 // Add all new streams822 for (int i=0; i < stream_ids.size(); i++) {823 addSingleRtpStream(stream_ids[i]);824 }825 setMarkers();826 827 unlockUI();828#ifdef QT_MULTIMEDIA_LIB1829 QTimer::singleShot(0, this, SLOT(retapPackets())qFlagLocation("1" "retapPackets()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "829")
);830#endif831 } else {832 ws_warning("replaceRtpStreams was called while other thread locked it. Current call is ignored, try it later.")do { if (true) { ws_log_full("", LOG_LEVEL_WARNING, "ui/qt/rtp_player_dialog.cpp"
, 832, __func__, "replaceRtpStreams was called while other thread locked it. Current call is ignored, try it later."
); } } while (0)
;833 }834}835 836void RtpPlayerDialog::addRtpStreams(QVector<rtpstream_id_t *> stream_ids)837{838 std::unique_lock<std::mutex> lock(run_mutex_, std::try_to_lock);839 if (lock.owns_lock()) {840 lockUI();841 842 int tli_count = ui->streamTreeWidget->topLevelItemCount();843 844 // Add new streams845 for (int i=0; i < stream_ids.size(); i++) {846 addSingleRtpStream(stream_ids[i]);847 }848 849 if (tli_count == 0) {850 setMarkers();851 }852 853 unlockUI();854#ifdef QT_MULTIMEDIA_LIB1855 QTimer::singleShot(0, this, SLOT(retapPackets())qFlagLocation("1" "retapPackets()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "855")
);856#endif857 } else {858 ws_warning("addRtpStreams was called while other thread locked it. Current call is ignored, try it later.")do { if (true) { ws_log_full("", LOG_LEVEL_WARNING, "ui/qt/rtp_player_dialog.cpp"
, 858, __func__, "addRtpStreams was called while other thread locked it. Current call is ignored, try it later."
); } } while (0)
;859 }860}861 862void RtpPlayerDialog::removeRtpStreams(QVector<rtpstream_id_t *> stream_ids)863{864 std::unique_lock<std::mutex> lock(run_mutex_, std::try_to_lock);865 if (lock.owns_lock()) {866 lockUI();867 int tli_count = ui->streamTreeWidget->topLevelItemCount();868 869 for (int i=0; i < stream_ids.size(); i++) {870 for (int row = 0; row < tli_count; row++) {871 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);872 RtpAudioStream *row_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();873 if (row_stream->isMatch(stream_ids[i])) {874 removeRow(ti);875 tli_count--;876 break;877 }878 }879 }880 updateGraphs();881 882 updateWidgets();883 unlockUI();884 } else {885 ws_warning("removeRtpStreams was called while other thread locked it. Current call is ignored, try it later.")do { if (true) { ws_log_full("", LOG_LEVEL_WARNING, "ui/qt/rtp_player_dialog.cpp"
, 885, __func__, "removeRtpStreams was called while other thread locked it. Current call is ignored, try it later."
); } } while (0)
;886 }887}888 889void RtpPlayerDialog::setMarkers()890{891 setStartPlayMarker(0);892 drawStartPlayMarker();893}894 895void RtpPlayerDialog::showEvent(QShowEvent *)896{897 // We could use loadSplitterState(ui->splitter) instead of always898 // resetting the plot size to 75%899 QList<int> split_sizes = ui->splitter->sizes();900 int tot_size = split_sizes[0] + split_sizes[1];901 int plot_size = tot_size * 3 / 4;902 split_sizes.clear();903 split_sizes << plot_size << tot_size - plot_size;904 ui->splitter->setSizes(split_sizes);905}906 907bool RtpPlayerDialog::eventFilter(QObject *, QEvent *event)908{909 if (event->type() == QEvent::KeyPress) {910 QKeyEvent &keyEvent = static_cast<QKeyEvent&>(*event);911 int pan_secs = keyEvent.modifiers() & Qt::ShiftModifier ? 1 : 10;912 913 switch(keyEvent.key()) {914 case Qt::Key_Minus:915 case Qt::Key_Underscore: // Shifted minus on U.S. keyboards916 case Qt::Key_O: // GTK+917 case Qt::Key_R:918 on_actionZoomOut_triggered();919 return true;920 case Qt::Key_Plus:921 case Qt::Key_Equal: // Unshifted plus on U.S. keyboards922 case Qt::Key_I: // GTK+923 if (keyEvent.modifiers() == Qt::ControlModifier) {924 // Ctrl+I925 on_actionSelectInvert_triggered();926 return true;927 } else {928 // I929 on_actionZoomIn_triggered();930 return true;931 }932 break;933 case Qt::Key_Right:934 case Qt::Key_L:935 panXAxis(pan_secs);936 return true;937 case Qt::Key_Left:938 case Qt::Key_H:939 panXAxis(-1 * pan_secs);940 return true;941 case Qt::Key_0:942 case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards943 on_actionReset_triggered();944 return true;945 case Qt::Key_G:946 if (keyEvent.modifiers() == Qt::ShiftModifier) {947 // Goto SETUP frame, use correct call based on caller948 QPoint pos1 = ui->audioPlot->mapFromGlobal(QCursor::pos());949 QPoint pos2 = ui->streamTreeWidget->mapFromGlobal(QCursor::pos());950 if (ui->audioPlot->rect().contains(pos1)) {951 // audio plot, by mouse coords952 on_actionGoToSetupPacketPlot_triggered();953 } else if (ui->streamTreeWidget->rect().contains(pos2)) {954 // packet tree, by cursor955 on_actionGoToSetupPacketTree_triggered();956 }957 return true;958 } else {959 on_actionGoToPacket_triggered();960 return true;961 }962 case Qt::Key_A:963 if (keyEvent.modifiers() == Qt::ControlModifier) {964 // Ctrl+A965 on_actionSelectAll_triggered();966 return true;967 } else if (keyEvent.modifiers() == (Qt::ShiftModifier | Qt::ControlModifier)) {968 // Ctrl+Shift+A969 on_actionSelectNone_triggered();970 return true;971 }972 break;973 case Qt::Key_M:974 if (keyEvent.modifiers() == Qt::ShiftModifier) {975 on_actionAudioRoutingUnmute_triggered();976 return true;977 } else if (keyEvent.modifiers() == Qt::ControlModifier) {978 on_actionAudioRoutingMuteInvert_triggered();979 return true;980 } else {981 on_actionAudioRoutingMute_triggered();982 return true;983 }984 case Qt::Key_Delete:985 on_actionRemoveStream_triggered();986 return true;987 case Qt::Key_X:988 if (keyEvent.modifiers() == Qt::ControlModifier) {989 // Ctrl+X990 on_actionRemoveStream_triggered();991 return true;992 }993 break;994 case Qt::Key_Down:995 case Qt::Key_Up:996 case Qt::Key_PageUp:997 case Qt::Key_PageDown:998 case Qt::Key_Home:999 case Qt::Key_End:1000 // Route keys to QTreeWidget1001 ui->streamTreeWidget->setFocus();1002 break;1003 case Qt::Key_P:1004 if (keyEvent.modifiers() == Qt::NoModifier) {1005 on_actionPlay_triggered();1006 return true;1007 }1008 break;1009 case Qt::Key_S:1010 on_actionStop_triggered();1011 return true;1012 case Qt::Key_N:1013 if (keyEvent.modifiers() == Qt::ShiftModifier) {1014 // Shift+N1015 on_actionDeselectInaudible_triggered();1016 return true;1017 } else {1018 on_actionSelectInaudible_triggered();1019 return true;1020 }1021 break;1022 }1023 }1024 1025 return false;1026}1027 1028void RtpPlayerDialog::contextMenuEvent(QContextMenuEvent *event)1029{1030 list_ctx_menu_->popup(event->globalPos());1031}1032 1033void RtpPlayerDialog::updateWidgets()1034{1035 bool enable_play = true;1036 bool enable_pause = false;1037 bool enable_stop = false;1038 bool enable_timing = true;1039 int count = ui->streamTreeWidget->topLevelItemCount();1040 qsizetype selected = ui->streamTreeWidget->selectedItems().count();1041 1042 if (count < 1) {1043 enable_play = false;1044 ui->skipSilenceButton->setEnabled(false);1045 ui->minSilenceSpinBox->setEnabled(false);1046 } else {1047 ui->skipSilenceButton->setEnabled(true);1048 ui->minSilenceSpinBox->setEnabled(true);1049 }1050 1051 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {1052 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);1053 1054 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();1055 if (audio_stream->outputState() != QAudio::IdleState) {1056 enable_play = false;1057 enable_pause = true;1058 enable_stop = true;1059 enable_timing = false;1060 }1061 }1062 1063 ui->actionAudioRoutingP->setVisible(!stereo_available_);1064 ui->actionAudioRoutingL->setVisible(stereo_available_);1065 ui->actionAudioRoutingLR->setVisible(stereo_available_);1066 ui->actionAudioRoutingR->setVisible(stereo_available_);1067 1068 ui->playButton->setEnabled(enable_play);1069 if (enable_play) {1070 ui->playButton->setVisible(true);1071 ui->pauseButton->setVisible(false);1072 } else if (enable_pause) {1073 ui->playButton->setVisible(false);1074 ui->pauseButton->setVisible(true);1075 }1076 ui->outputDeviceComboBox->setEnabled(enable_play);1077 ui->outputAudioRate->setEnabled(enable_play);1078 ui->pauseButton->setEnabled(enable_pause);1079 ui->stopButton->setEnabled(enable_stop);1080 ui->actionStop->setEnabled(enable_stop);1081 cur_play_pos_->setVisible(enable_stop);1082 1083 ui->jitterSpinBox->setEnabled(enable_timing);1084 ui->timingComboBox->setEnabled(enable_timing);1085 ui->todCheckBox->setEnabled(enable_timing);1086 1087 read_btn_->setEnabled(read_capture_enabled_);1088 inaudible_btn_->setEnabled(count > 0);1089 analyze_btn_->setEnabled(selected > 0);1090 prepare_btn_->setEnabled(selected > 0);1091 1092 updateHintLabel();1093 ui->audioPlot->replot();1094}1095 1096void RtpPlayerDialog::handleItemHighlight(QTreeWidgetItem *ti, bool scroll)1097{1098 if (ti) {1099 if (ti != last_ti_) {1100 if (last_ti_) {1101 highlightItem(last_ti_, false);1102 }1103 highlightItem(ti, true);1104 1105 if (scroll)1106 ui->streamTreeWidget->scrollToItem(ti, QAbstractItemView::EnsureVisible);1107 ui->audioPlot->replot();1108 last_ti_ = ti;1109 }1110 } else {1111 if (last_ti_) {1112 highlightItem(last_ti_, false);1113 ui->audioPlot->replot();1114 last_ti_ = NULL__null;1115 }1116 }1117}1118 1119void RtpPlayerDialog::highlightItem(QTreeWidgetItem *ti, bool highlight)1120{1121 QFont font;1122 RtpAudioGraph *audio_graph;1123 1124 font.setBold(highlight);1125 for(int i=0; i<ui->streamTreeWidget->columnCount(); i++) {1126 ti->setFont(i, font);1127 }1128 1129 audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();1130 if (audio_graph) {1131 audio_graph->setHighlight(highlight);1132 }1133}1134 1135void RtpPlayerDialog::itemEntered(QTreeWidgetItem *item, int column _U___attribute__((unused)))1136{1137 handleItemHighlight(item, false);1138}1139 1140void RtpPlayerDialog::mouseMovePlot(QMouseEvent *event)1141{1142 // The calculations are expensive, so just store the position and1143 // calculate no more than once per some interval. (On Linux the1144 // QMouseEvents can be sent absurdly often, every 25 microseconds!)1145 mouse_pos_ = event->pos();1146 if (!mouse_update_timer_->isActive()) {1147 mouse_update_timer_->start();1148 }1149}1150 1151void RtpPlayerDialog::mouseMoveUpdate()1152{1153 // findItemByCoords is expensive (because of calling pointDistance),1154 // and updateHintLabel calls it as well via getHoveredPacket. Some1155 // way to only perform the distance calculations once would be better.1156 updateHintLabel();1157 1158 QTreeWidgetItem *ti = findItemByCoords(mouse_pos_);1159 handleItemHighlight(ti, true);1160}1161 1162void RtpPlayerDialog::showGraphContextMenu(const QPoint &pos)1163{1164 graph_ctx_menu_->popup(ui->audioPlot->mapToGlobal(pos));1165}1166 1167void RtpPlayerDialog::graphClicked(QMouseEvent*)1168{1169 updateWidgets();1170}1171 1172void RtpPlayerDialog::graphDoubleClicked(QMouseEvent *event)1173{1174 updateWidgets();1175 if (event->button() == Qt::LeftButton) {1176 // Move start play line1177 double ts = ui->audioPlot->xAxis->pixelToCoord(event->pos().x());1178 1179 setStartPlayMarker(ts);1180 drawStartPlayMarker();1181 1182 ui->audioPlot->replot();1183 }1184}1185 1186void RtpPlayerDialog::plotClicked(QCPAbstractPlottable *plottable _U___attribute__((unused)), int dataIndex _U___attribute__((unused)), QMouseEvent *event)1187{1188 // Delivered plottable very often points to different element than a mouse1189 // so we find right one by mouse coordinates1190 QTreeWidgetItem *ti = findItemByCoords(event->pos());1191 if (ti) {1192 if (event->modifiers() == Qt::NoModifier) {1193 ti->setSelected(true);1194 } else if (event->modifiers() == Qt::ControlModifier) {1195 ti->setSelected(!ti->isSelected());1196 }1197 }1198}1199 1200QTreeWidgetItem *RtpPlayerDialog::findItemByCoords(QPoint point)1201{1202 QCPAbstractPlottable *plottable=ui->audioPlot->plottableAt(point);1203 if (plottable) {1204 return findItem(plottable);1205 }1206 1207 return NULL__null;1208}1209 1210QTreeWidgetItem *RtpPlayerDialog::findItem(QCPAbstractPlottable *plottable)1211{1212 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {1213 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);1214 RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();1215 if (audio_graph && audio_graph->isMyPlottable(plottable)) {1216 return ti;1217 }1218 }1219 1220 return NULL__null;1221}1222 1223void RtpPlayerDialog::updateHintLabel()1224{1225 int packet_num = getHoveredPacket();1226 QString hint = "<small><i>";1227 double start_pos = getStartPlayMarker();1228 int row_count = ui->streamTreeWidget->topLevelItemCount();1229 qsizetype selected = ui->streamTreeWidget->selectedItems().count();1230 int not_muted = 0;1231 1232 hint += tr("%1 streams").arg(row_count);1233 1234 if (row_count > 0) {1235 if (selected > 0) {1236 hint += tr(", %1 selected").arg(selected);1237 }1238 1239 for (int row = 0; row < row_count; row++) {1240 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);1241 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();1242 if (audio_stream && (!audio_stream->getAudioRouting().isMuted())) {1243 not_muted++;1244 }1245 }1246 1247 hint += tr(", %1 not muted").arg(not_muted);1248 }1249 1250 if (packet_num == 0) {1251 hint += tr(", start: %1. Double click on graph to set start of playback.")1252 .arg(getFormatedTime(start_pos));1253 } else if (packet_num > 0) {1254 hint += tr(", start: %1, cursor: %2. Press \"G\" to go to packet %3. Double click on graph to set start of playback.")1255 .arg(getFormatedTime(start_pos))1256 .arg(getFormatedHoveredTime())1257 .arg(packet_num);1258 }1259 1260 if (!playback_error_.isEmpty()) {1261 hint += " <font color=\"red\">";1262 hint += playback_error_;1263 hint += " </font>";1264 }1265 1266 hint += "</i></small>";1267 ui->hintLabel->setText(hint);1268}1269 1270void RtpPlayerDialog::resetXAxis()1271{1272 QCustomPlot *ap = ui->audioPlot;1273 1274 double pixel_pad = 10.0; // per side1275 1276 ap->rescaleAxes(true);1277 1278 double axis_pixels = ap->xAxis->axisRect()->width();1279 ap->xAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, ap->xAxis->range().center());1280 1281 axis_pixels = ap->yAxis->axisRect()->height();1282 ap->yAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, ap->yAxis->range().center());1283 1284 ap->replot();1285}1286 1287void RtpPlayerDialog::updateGraphs()1288{1289 QCustomPlot *ap = ui->audioPlot;1290 1291 // Create new plots, just existing ones1292 createPlot(false);1293 1294 // Rescale Y axis1295 double pixel_pad = 10.0; // per side1296 double axis_pixels = ap->yAxis->axisRect()->height();1297 ap->yAxis->rescale(true);1298 ap->yAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, ap->yAxis->range().center());1299 1300 ap->replot();1301}1302 1303void RtpPlayerDialog::playFinished(RtpAudioStream *stream, QAudio::Error error)1304{1305 if ((error != QAudio::NoError) && (error != QAudio::UnderrunError)) {1306 setPlaybackError(tr("Playback of stream %1 failed!")1307 .arg(stream->getIDAsQString())1308 );1309 }1310 playing_streams_.removeOne(stream);1311 if (playing_streams_.isEmpty()) {1312 if (marker_stream_) {1313 marker_stream_->stop();1314 }1315 updateWidgets();1316 }1317}1318 1319void RtpPlayerDialog::setPlayPosition(double secs)1320{1321 double cur_secs = cur_play_pos_->point1->key();1322 1323 if (ui->todCheckBox->isChecked()) {1324 secs += first_stream_abs_start_time_;1325 } else {1326 secs += first_stream_rel_start_time_;1327 }1328 if (secs > cur_secs) {1329 cur_play_pos_->point1->setCoords(secs, 0.0);1330 cur_play_pos_->point2->setCoords(secs, 1.0);1331 ui->audioPlot->replot();1332 }1333}1334 1335void RtpPlayerDialog::setPlaybackError(const QString playback_error)1336{1337 playback_error_ = playback_error;1338 updateHintLabel();1339}1340 1341tap_packet_status RtpPlayerDialog::tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *rtpinfo_ptr, tap_flags_t)1342{1343 RtpPlayerDialog *rtp_player_dialog = dynamic_cast<RtpPlayerDialog *>((RtpPlayerDialog*)tapinfo_ptr);1344 if (!rtp_player_dialog) return TAP_PACKET_DONT_REDRAW;1345 1346 const struct _rtp_info *rtpinfo = (const struct _rtp_info *)rtpinfo_ptr;1347 if (!rtpinfo) return TAP_PACKET_DONT_REDRAW;1348 1349 /* ignore RTP Version != 2 */1350 if (rtpinfo->info_version != 2)1351 return TAP_PACKET_DONT_REDRAW;1352 1353 rtp_player_dialog->addPacket(pinfo, rtpinfo);1354 1355 return TAP_PACKET_DONT_REDRAW;1356}1357 1358void RtpPlayerDialog::addPacket(packet_info *pinfo, const _rtp_info *rtpinfo)1359{1360 // Search stream in hash key, if there are multiple streams with same hash1361 QList<RtpAudioStream *> streams = stream_hash_.values(pinfo_rtp_info_to_hash(pinfo, rtpinfo));1362 for (int i = 0; i < streams.size(); i++) {1363 RtpAudioStream *row_stream = streams.at(i);1364 if (row_stream->isMatch(pinfo, rtpinfo)) {1365 row_stream->addRtpPacket(pinfo, rtpinfo);1366 break;1367 }1368 }1369 1370// qDebug() << "=ap no match!" << address_to_qstring(&pinfo->src) << address_to_qstring(&pinfo->dst);1371}1372 1373void RtpPlayerDialog::zoomXAxis(bool in)1374{1375 QCustomPlot *ap = ui->audioPlot;1376 double h_factor = ap->axisRect()->rangeZoomFactor(Qt::Horizontal);1377 1378 if (!in) {1379 h_factor = pow(h_factor, -1);1380 }1381 1382 ap->xAxis->scaleRange(h_factor, ap->xAxis->range().center());1383 ap->replot();1384}1385 1386// XXX I tried using seconds but pixels make more sense at varying zoom1387// levels.1388void RtpPlayerDialog::panXAxis(int x_pixels)1389{1390 QCustomPlot *ap = ui->audioPlot;1391 double h_pan;1392 1393 h_pan = ap->xAxis->range().size() * x_pixels / ap->xAxis->axisRect()->width();1394 if (x_pixels) {1395 ap->xAxis->moveRange(h_pan);1396 ap->replot();1397 }1398}1399 1400void RtpPlayerDialog::on_playButton_clicked()1401{1402 double start_time;1403 QList<RtpAudioStream *> streams_to_start;1404 1405 ui->hintLabel->setText("<i><small>" + tr("Preparing to play...") + "</i></small>");1406 mainApp->processEvents();1407 ui->pauseButton->setChecked(false);1408 1409 // Protect start time against move of marker during the play1410 start_marker_time_play_ = start_marker_time_;1411 silence_skipped_time_ = 0.0;1412 cur_play_pos_->point1->setCoords(start_marker_time_play_, 0.0);1413 cur_play_pos_->point2->setCoords(start_marker_time_play_, 1.0);1414 cur_play_pos_->setVisible(true);1415 playback_error_.clear();1416 1417 if (ui->todCheckBox->isChecked()) {1418 start_time = start_marker_time_play_;1419 } else {1420 start_time = start_marker_time_play_ - first_stream_rel_start_time_;1421 }1422 1423#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))1424 QAudioDevice cur_out_device = getCurrentDeviceInfo();1425#else1426 QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo();1427#endif1428 playing_streams_.clear();1429 int row_count = ui->streamTreeWidget->topLevelItemCount();1430 for (int row = 0; row < row_count; row++) {1431 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);1432 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();1433 // All streams starts at first_stream_rel_start_time_1434 audio_stream->setStartPlayTime(start_time);1435 if (audio_stream->prepareForPlay(cur_out_device)) {1436 playing_streams_ << audio_stream;1437 }1438 }1439 1440 // Prepare silent stream for progress marker1441 if (!marker_stream_) {1442 marker_stream_ = getSilenceAudioOutput();1443 } else {1444 marker_stream_->stop();1445 }1446 1447 // Start progress marker and then audio streams1448#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))1449 notify_timer_start_diff_ = -1;1450#endif1451 marker_stream_->start(new AudioSilenceGenerator(marker_stream_));1452 // It may happen that stream play is finished before all others are started1453 // therefore we do not use playing_streams_ there, but separate temporarily1454 // list. It avoids access element/remove element race condition.1455 streams_to_start = playing_streams_;1456 for( int i = 0; i<streams_to_start.count(); ++i ) {1457 streams_to_start[i]->startPlaying();1458 }1459 1460 updateWidgets();1461}1462 1463#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))1464QAudioDevice RtpPlayerDialog::getCurrentDeviceInfo()1465{1466 QAudioDevice cur_out_device = QMediaDevices::defaultAudioOutput();1467 QString cur_out_name = currentOutputDeviceName();1468 foreach (QAudioDevice out_device, QMediaDevices::audioOutputs())for (auto _container_1468 = QtPrivate::qMakeForeachContainer(
QMediaDevices::audioOutputs()); _container_1468.i != _container_1468
.e; ++_container_1468.i) if (QAudioDevice out_device = *_container_1468
.i; false) {} else
{1469 if (cur_out_name == out_device.description()) {1470 cur_out_device = out_device;1471 }1472 }1473 1474 return cur_out_device;1475}1476 1477void RtpPlayerDialog::sinkStateChanged()1478{1479 if (marker_stream_->state() == QAudio::ActiveState) {1480 notify_timer_.start();1481 } else {1482 notify_timer_.stop();1483 }1484}1485#else1486QAudioDeviceInfo RtpPlayerDialog::getCurrentDeviceInfo()1487{1488 QAudioDeviceInfo cur_out_device = QAudioDeviceInfo::defaultOutputDevice();1489 QString cur_out_name = currentOutputDeviceName();1490 foreach (QAudioDeviceInfo out_device, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))for (auto _container_1490 = QtPrivate::qMakeForeachContainer(
QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)); _container_1490
.i != _container_1490.e; ++_container_1490.i) if (QAudioDeviceInfo
out_device = *_container_1490.i; false) {} else
{1491 if (cur_out_name == out_device.deviceName()) {1492 cur_out_device = out_device;1493 }1494 }1495 1496 return cur_out_device;1497}1498#endif1499 1500#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))1501QAudioSink *RtpPlayerDialog::getSilenceAudioOutput()1502{1503 QAudioDevice cur_out_device = getCurrentDeviceInfo();1504 1505 QAudioFormat format;1506 if (marker_stream_requested_out_rate_ > 0) {1507 format.setSampleRate(marker_stream_requested_out_rate_);1508 } else {1509 format.setSampleRate(8000);1510 }1511 // Must match rtp_media.h.1512 format.setSampleFormat(QAudioFormat::Int16);1513 format.setChannelCount(1);1514 if (!cur_out_device.isFormatSupported(format)) {1515 format = cur_out_device.preferredFormat();1516 }1517 1518 QAudioSink *sink = new QAudioSink(cur_out_device, format, this);1519 connect(sink, &QAudioSink::stateChanged, this, &RtpPlayerDialog::sinkStateChanged);1520 return sink;1521}1522#else1523QAudioOutput *RtpPlayerDialog::getSilenceAudioOutput()1524{1525 QAudioOutput *o;1526 QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo();1527 1528 QAudioFormat format;1529 if (marker_stream_requested_out_rate_ > 0) {1530 format.setSampleRate(marker_stream_requested_out_rate_);1531 } else {1532 format.setSampleRate(8000);1533 }1534 format.setSampleSize(SAMPLE_BYTES(sizeof(SAMPLE) / sizeof(char)) * 8); // bits1535 format.setSampleType(QAudioFormat::SignedInt);1536 format.setChannelCount(1);1537 format.setCodec("audio/pcm");1538 if (!cur_out_device.isFormatSupported(format)) {1539 format = cur_out_device.nearestFormat(format);1540 }1541 1542 o = new QAudioOutput(cur_out_device, format, this);1543 o->setNotifyInterval(100); // ~15 fps1544 connect(o, &QAudioOutput::notify, this, &RtpPlayerDialog::outputNotify);1545 1546 return o;1547}1548#endif1549 1550void RtpPlayerDialog::outputNotify()1551{1552 double new_current_pos = 0.0;1553 double current_pos = 0.0;1554 qint64 usecs = marker_stream_->processedUSecs();1555 1556#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))1557 // First notify can show end of buffer, not play point so we have1558 // remember the shift1559 if ( -1 == notify_timer_start_diff_ || 0 == notify_timer_start_diff_) {1560 notify_timer_start_diff_ = usecs;1561 }1562 usecs -= notify_timer_start_diff_;1563#endif1564 double secs = usecs / 1000000.0;1565 1566 if (ui->skipSilenceButton->isChecked()) {1567 // We should check whether we can skip some silence1568 // We must calculate in time domain as every stream can use different1569 // play rate1570 double min_silence = playing_streams_[0]->getEndOfSilenceTime();1571 for( int i = 1; i<playing_streams_.count(); ++i ) {1572 qint64 cur_silence = playing_streams_[i]->getEndOfSilenceTime();1573 if (cur_silence < min_silence) {1574 min_silence = cur_silence;1575 }1576 }1577 1578 if (min_silence > 0.0) {1579 double silence_duration;1580 1581 // Calculate silence duration we can skip1582 new_current_pos = first_stream_rel_start_time_ + min_silence;1583 if (ui->todCheckBox->isChecked()) {1584 current_pos = secs + start_marker_time_play_ + first_stream_rel_start_time_;1585 } else {1586 current_pos = secs + start_marker_time_play_;1587 }1588 silence_duration = new_current_pos - current_pos;1589 1590 if (silence_duration >= ui->minSilenceSpinBox->value()) {1591 // Skip silence gap and update cursor difference1592 for( int i = 0; i<playing_streams_.count(); ++i ) {1593 // Convert silence from time domain to samples1594 qint64 skip_samples = playing_streams_[i]->convertTimeToSamples(min_silence);1595 playing_streams_[i]->seekPlaying(skip_samples);1596 }1597 silence_skipped_time_ = silence_duration;1598 }1599 }1600 }1601 1602 // Calculate new cursor position1603 if (ui->todCheckBox->isChecked()) {1604 secs += start_marker_time_play_;1605 secs += silence_skipped_time_;1606 } else {1607 secs += start_marker_time_play_;1608 secs -= first_stream_rel_start_time_;1609 secs += silence_skipped_time_;1610 }1611 setPlayPosition(secs);1612}1613 1614void RtpPlayerDialog::on_pauseButton_clicked()1615{1616 for( int i = 0; i<playing_streams_.count(); ++i ) {1617 playing_streams_[i]->pausePlaying();1618 }1619 if (ui->pauseButton->isChecked()) {1620 marker_stream_->suspend();1621 } else {1622 marker_stream_->resume();1623 }1624 updateWidgets();1625}1626 1627void RtpPlayerDialog::on_stopButton_clicked()1628{1629 // We need copy of list because items will be removed during stopPlaying()1630 QList<RtpAudioStream *> ps=QList<RtpAudioStream *>(playing_streams_);1631 for( int i = 0; i<ps.count(); ++i ) {1632 ps[i]->stopPlaying();1633 }1634 marker_stream_->stop();1635 cur_play_pos_->setVisible(false);1636 updateWidgets();1637}1638 1639void RtpPlayerDialog::on_actionReset_triggered()1640{1641 resetXAxis();1642}1643 1644void RtpPlayerDialog::on_actionZoomIn_triggered()1645{1646 zoomXAxis(true);1647}1648 1649void RtpPlayerDialog::on_actionZoomOut_triggered()1650{1651 zoomXAxis(false);1652}1653 1654void RtpPlayerDialog::on_actionMoveLeft10_triggered()1655{1656 panXAxis(-10);1657}1658 1659void RtpPlayerDialog::on_actionMoveRight10_triggered()1660{1661 panXAxis(10);1662}1663 1664void RtpPlayerDialog::on_actionMoveLeft1_triggered()1665{1666 panXAxis(-1);1667}1668 1669void RtpPlayerDialog::on_actionMoveRight1_triggered()1670{1671 panXAxis(1);1672}1673 1674void RtpPlayerDialog::on_actionGoToPacket_triggered()1675{1676 int packet_num = getHoveredPacket();1677 if (packet_num > 0) emit goToPacket(packet_num);1678}1679 1680void RtpPlayerDialog::handleGoToSetupPacket(QTreeWidgetItem *ti)1681{1682 if (ti) {1683 bool ok;1684 1685 int packet_num = ti->data(first_pkt_col_, Qt::UserRole).toInt(&ok);1686 if (ok) {1687 emit goToPacket(packet_num);1688 }1689 }1690}1691 1692void RtpPlayerDialog::on_actionGoToSetupPacketPlot_triggered()1693{1694 QPoint pos = ui->audioPlot->mapFromGlobal(QCursor::pos());1695 handleGoToSetupPacket(findItemByCoords(pos));1696}1697 1698void RtpPlayerDialog::on_actionGoToSetupPacketTree_triggered()1699{1700 handleGoToSetupPacket(last_ti_);1701}1702 1703// Make waveform graphs selectable and update the treewidget selection accordingly.1704void RtpPlayerDialog::on_streamTreeWidget_itemSelectionChanged()1705{1706 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {1707 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);1708 RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();1709 if (audio_graph) {1710 audio_graph->setSelected(ti->isSelected());1711 }1712 }1713 1714 qsizetype selected = ui->streamTreeWidget->selectedItems().count();1715 if (selected == 0) {1716 analyze_btn_->setEnabled(false);1717 prepare_btn_->setEnabled(false);1718 export_btn_->setEnabled(false);1719 } else if (selected == 1) {1720 analyze_btn_->setEnabled(true);1721 prepare_btn_->setEnabled(true);1722 export_btn_->setEnabled(true);1723 ui->actionSavePayload->setEnabled(true);1724 } else {1725 analyze_btn_->setEnabled(true);1726 prepare_btn_->setEnabled(true);1727 export_btn_->setEnabled(true);1728 ui->actionSavePayload->setEnabled(false);1729 }1730 1731 if (!block_redraw_) {1732 ui->audioPlot->replot();1733 updateHintLabel();1734 }1735}1736 1737// Change channel audio routing if double clicked channel column1738void RtpPlayerDialog::on_streamTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, const int column)1739{1740 if (column == channel_col_) {1741 RtpAudioStream *audio_stream = item->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();1742 if (!audio_stream)1743 return;1744 1745 AudioRouting audio_routing = audio_stream->getAudioRouting();1746 audio_routing = audio_routing.getNextChannel(stereo_available_);1747 changeAudioRoutingOnItem(item, audio_routing);1748 }1749 updateHintLabel();1750}1751 1752void RtpPlayerDialog::removeRow(QTreeWidgetItem *ti)1753{1754 if (last_ti_ && (last_ti_ == ti)) {1755 highlightItem(last_ti_, false);1756 last_ti_ = NULL__null;1757 }1758 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();1759 if (audio_stream) {1760 stream_hash_.remove(audio_stream->getHash(), audio_stream);1761 ti->setData(stream_data_col_, Qt::UserRole, QVariant());1762 delete audio_stream;1763 }1764 1765 RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();1766 if (audio_graph) {1767 ti->setData(graph_audio_data_col_, Qt::UserRole, QVariant());1768 audio_graph->remove(ui->audioPlot);1769 }1770 1771 QCPGraph *graph;1772 graph = ti->data(graph_sequence_data_col_, Qt::UserRole).value<QCPGraph*>();1773 if (graph) {1774 ti->setData(graph_sequence_data_col_, Qt::UserRole, QVariant());1775 ui->audioPlot->removeGraph(graph);1776 }1777 1778 graph = ti->data(graph_jitter_data_col_, Qt::UserRole).value<QCPGraph*>();1779 if (graph) {1780 ti->setData(graph_jitter_data_col_, Qt::UserRole, QVariant());1781 ui->audioPlot->removeGraph(graph);1782 }1783 1784 graph = ti->data(graph_timestamp_data_col_, Qt::UserRole).value<QCPGraph*>();1785 if (graph) {1786 ti->setData(graph_timestamp_data_col_, Qt::UserRole, QVariant());1787 ui->audioPlot->removeGraph(graph);1788 }1789 1790 graph = ti->data(graph_silence_data_col_, Qt::UserRole).value<QCPGraph*>();1791 if (graph) {1792 ti->setData(graph_silence_data_col_, Qt::UserRole, QVariant());1793 ui->audioPlot->removeGraph(graph);1794 }1795 1796 delete ti;1797}1798 1799void RtpPlayerDialog::on_actionRemoveStream_triggered()1800{1801 lockUI();1802 QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();1803 1804 block_redraw_ = true;1805 for(int i = static_cast<int>(items.count()) - 1; i>=0; i-- ) {1806 removeRow(items[i]);1807 }1808 block_redraw_ = false;1809 // TODO: Recalculate legend1810 // - Graphs used for legend could be removed above and we must add new1811 // - If no legend is required, it should be removed1812 1813 // Redraw existing waveforms and rescale Y axis1814 updateGraphs();1815 1816 updateWidgets();1817 unlockUI();1818}1819 1820// If called with channel_any, just muted flag should be changed1821void RtpPlayerDialog::changeAudioRoutingOnItem(QTreeWidgetItem *ti, AudioRouting new_audio_routing)1822{1823 if (!ti)1824 return;1825 1826 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();1827 if (!audio_stream)1828 return;1829 1830 AudioRouting audio_routing = audio_stream->getAudioRouting();1831 audio_routing.mergeAudioRouting(new_audio_routing);1832 formatAudioRouting(ti, audio_routing);1833 1834 audio_stream->setAudioRouting(audio_routing);1835 1836 RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();1837 if (audio_graph) {1838 1839 audio_graph->setSelected(ti->isSelected());1840 audio_graph->setMuted(audio_routing.isMuted());1841 if (!block_redraw_) {1842 ui->audioPlot->replot();1843 }1844 }1845}1846 1847// Find current item and apply change on it1848void RtpPlayerDialog::changeAudioRouting(AudioRouting new_audio_routing)1849{1850 lockUI();1851 QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();1852 1853 block_redraw_ = true;1854 for(int i = 0; i<items.count(); i++ ) {1855 1856 QTreeWidgetItem *ti = items[i];1857 changeAudioRoutingOnItem(ti, new_audio_routing);1858 }1859 block_redraw_ = false;1860 ui->audioPlot->replot();1861 updateHintLabel();1862 unlockUI();1863}1864 1865// Invert mute/unmute on item1866void RtpPlayerDialog::invertAudioMutingOnItem(QTreeWidgetItem *ti)1867{1868 if (!ti)1869 return;1870 1871 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();1872 if (!audio_stream)1873 return;1874 1875 AudioRouting audio_routing = audio_stream->getAudioRouting();1876 // Invert muting1877 if (audio_routing.isMuted()) {1878 changeAudioRoutingOnItem(ti, AudioRouting(AUDIO_UNMUTEDfalse, channel_any));1879 } else {1880 changeAudioRoutingOnItem(ti, AudioRouting(AUDIO_MUTEDtrue, channel_any));1881 }1882}1883 1884void RtpPlayerDialog::on_actionAudioRoutingP_triggered()1885{1886 changeAudioRouting(AudioRouting(AUDIO_UNMUTEDfalse, channel_mono));1887}1888 1889void RtpPlayerDialog::on_actionAudioRoutingL_triggered()1890{1891 changeAudioRouting(AudioRouting(AUDIO_UNMUTEDfalse, channel_stereo_left));1892}1893 1894void RtpPlayerDialog::on_actionAudioRoutingLR_triggered()1895{1896 changeAudioRouting(AudioRouting(AUDIO_UNMUTEDfalse, channel_stereo_both));1897}1898 1899void RtpPlayerDialog::on_actionAudioRoutingR_triggered()1900{1901 changeAudioRouting(AudioRouting(AUDIO_UNMUTEDfalse, channel_stereo_right));1902}1903 1904void RtpPlayerDialog::on_actionAudioRoutingMute_triggered()1905{1906 changeAudioRouting(AudioRouting(AUDIO_MUTEDtrue, channel_any));1907}1908 1909void RtpPlayerDialog::on_actionAudioRoutingUnmute_triggered()1910{1911 changeAudioRouting(AudioRouting(AUDIO_UNMUTEDfalse, channel_any));1912}1913 1914void RtpPlayerDialog::on_actionAudioRoutingMuteInvert_triggered()1915{1916 lockUI();1917 QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();1918 1919 block_redraw_ = true;1920 for(int i = 0; i<items.count(); i++ ) {1921 1922 QTreeWidgetItem *ti = items[i];1923 invertAudioMutingOnItem(ti);1924 }1925 block_redraw_ = false;1926 ui->audioPlot->replot();1927 updateHintLabel();1928 unlockUI();1929}1930 1931const QString RtpPlayerDialog::getFormatedTime(double f_time)1932{1933 QString time_str;1934 1935 if (ui->todCheckBox->isChecked()) {1936 QDateTime date_time = QDateTime::fromMSecsSinceEpoch(f_time * 1000.0);1937 time_str = date_time.toString("yyyy-MM-dd hh:mm:ss.zzz");1938 } else {1939 time_str = QString::number(f_time, 'f', 6);1940 time_str += " s";1941 }1942 1943 return time_str;1944}1945 1946const QString RtpPlayerDialog::getFormatedHoveredTime()1947{1948 QPoint pos = ui->audioPlot->mapFromGlobal(QCursor::pos());1949 QTreeWidgetItem *ti = findItemByCoords(pos);1950 if (!ti) return tr("Unknown");1951 1952 double ts = ui->audioPlot->xAxis->pixelToCoord(pos.x());1953 1954 return getFormatedTime(ts);1955}1956 1957int RtpPlayerDialog::getHoveredPacket()1958{1959 QPoint pos = ui->audioPlot->mapFromGlobal(QCursor::pos());1960 QTreeWidgetItem *ti = findItemByCoords(pos);1961 if (!ti) return 0;1962 1963 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();1964 1965 double ts = ui->audioPlot->xAxis->pixelToCoord(pos.x());1966 1967 return audio_stream->nearestPacket(ts, !ui->todCheckBox->isChecked());1968}1969 1970// Used by RtpAudioStreams to initialize QAudioOutput. We could alternatively1971// pass the corresponding QAudioDeviceInfo directly.1972QString RtpPlayerDialog::currentOutputDeviceName()1973{1974 return ui->outputDeviceComboBox->currentText();1975}1976 1977void RtpPlayerDialog::fillAudioRateMenu()1978{1979 ui->outputAudioRate->blockSignals(true);1980 ui->outputAudioRate->clear();1981 ui->outputAudioRate->addItem(tr("Automatic"));1982#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))1983 // XXX QAudioDevice doesn't provide supportedSampleRates(). Fake it with1984 // what's available.1985 QAudioDevice cur_out_device = getCurrentDeviceInfo();1986 QSet<int>sample_rates;1987 if (!cur_out_device.isNull()) {1988 sample_rates.insert(cur_out_device.preferredFormat().sampleRate());1989 // Add 8000 if supported1990 if ((cur_out_device.minimumSampleRate() <= 8000) &&1991 (8000 <= cur_out_device.maximumSampleRate())1992 ) {1993 sample_rates.insert(8000);1994 }1995 // Add 16000 if supported1996 if ((cur_out_device.minimumSampleRate() <= 16000) &&1997 (16000 <= cur_out_device.maximumSampleRate())1998 ) {1999 sample_rates.insert(16000);2000 }2001 // Add 44100 if supported2002 if ((cur_out_device.minimumSampleRate() <= 44100) &&2003 (44100 <= cur_out_device.maximumSampleRate())2004 ) {2005 sample_rates.insert(44100);2006 }2007 }2008 2009 // Sort values2010 QList<int> sorter = sample_rates.values();2011 std::sort(sorter.begin(), sorter.end());2012 2013 // Insert rates to the list2014 for (auto rate : sorter) {2015 ui->outputAudioRate->addItem(QString::number(rate));2016 }2017#else2018 QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo();2019 2020 if (!cur_out_device.isNull()) {2021 foreach (int rate, cur_out_device.supportedSampleRates())for (auto _container_2021 = QtPrivate::qMakeForeachContainer(
cur_out_device.supportedSampleRates()); _container_2021.i != _container_2021
.e; ++_container_2021.i) if (int rate = *_container_2021.i; false
) {} else
{2022 ui->outputAudioRate->addItem(QString::number(rate));2023 }2024 }2025#endif2026 ui->outputAudioRate->blockSignals(false);2027}2028 2029void RtpPlayerDialog::cleanupMarkerStream()2030{2031 if (marker_stream_) {2032 marker_stream_->stop();2033 delete marker_stream_;2034 marker_stream_ = NULL__null;2035 }2036}2037 2038void RtpPlayerDialog::on_outputDeviceComboBox_currentTextChanged(const QString &)2039{2040 lockUI();2041 stereo_available_ = isStereoAvailable();2042 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {2043 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);2044 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();2045 if (!audio_stream)2046 continue;2047 2048 changeAudioRoutingOnItem(ti, audio_stream->getAudioRouting().convert(stereo_available_));2049 }2050 2051 marker_stream_requested_out_rate_ = 0;2052 cleanupMarkerStream();2053 fillAudioRateMenu();2054 rescanPackets();2055 unlockUI();2056}2057 2058void RtpPlayerDialog::on_outputAudioRate_currentTextChanged(const QString & rate_string)2059{2060 lockUI();2061 // Any unconvertable string is converted to 0 => used as Automatic rate2062 unsigned selected_rate = rate_string.toInt();2063 2064 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {2065 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);2066 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();2067 if (!audio_stream)2068 continue;2069 2070 audio_stream->setRequestedPlayRate(selected_rate);2071 }2072 marker_stream_requested_out_rate_ = selected_rate;2073 cleanupMarkerStream();2074 rescanPackets();2075 unlockUI();2076}2077 2078void RtpPlayerDialog::on_jitterSpinBox_valueChanged(double)2079{2080 rescanPackets();2081}2082 2083void RtpPlayerDialog::on_timingComboBox_currentIndexChanged(int)2084{2085 rescanPackets();2086}2087 2088void RtpPlayerDialog::on_todCheckBox_toggled(bool)2089{2090 QCPAxis *x_axis = ui->audioPlot->xAxis;2091 double move;2092 2093 // Create plot with new tod settings2094 createPlot();2095 2096 // Move view to same place as was shown before the change2097 if (ui->todCheckBox->isChecked()) {2098 // rel -> abs2099 // based on abs time of first sample2100 setStartPlayMarker(first_stream_abs_start_time_ + start_marker_time_ - first_stream_rel_start_time_);2101 move = first_stream_abs_start_time_ - first_stream_rel_start_time_;2102 } else {2103 // abs -> rel2104 // based on 0s2105 setStartPlayMarker(first_stream_rel_start_time_ + start_marker_time_);2106 move = - first_stream_abs_start_time_ + first_stream_rel_start_time_;2107 }2108 x_axis->moveRange(move);2109 drawStartPlayMarker();2110 ui->audioPlot->replot();2111}2112 2113void RtpPlayerDialog::on_buttonBox_helpRequested()2114{2115 mainApp->helpTopicAction(HELP_TELEPHONY_RTP_PLAYER_DIALOG);2116}2117 2118double RtpPlayerDialog::getStartPlayMarker()2119{2120 double start_pos;2121 2122 if (ui->todCheckBox->isChecked()) {2123 start_pos = start_marker_time_ + first_stream_abs_start_time_;2124 } else {2125 start_pos = start_marker_time_;2126 }2127 2128 return start_pos;2129}2130 2131void RtpPlayerDialog::drawStartPlayMarker()2132{2133 double pos = getStartPlayMarker();2134 2135 start_marker_pos_->point1->setCoords(pos, 0.0);2136 start_marker_pos_->point2->setCoords(pos, 1.0);2137 2138 updateHintLabel();2139}2140 2141void RtpPlayerDialog::setStartPlayMarker(double new_time)2142{2143 if (ui->todCheckBox->isChecked()) {2144 new_time = qBound(first_stream_abs_start_time_, new_time, first_stream_abs_start_time_ + streams_length_);2145 // start_play_time is relative, we must calculate it2146 start_marker_time_ = new_time - first_stream_abs_start_time_;2147 } else {2148 new_time = qBound(first_stream_rel_start_time_, new_time, first_stream_rel_start_time_ + streams_length_);2149 start_marker_time_ = new_time;2150 }2151}2152 2153void RtpPlayerDialog::updateStartStopTime(rtpstream_info_t *rtpstream, bool is_first)2154{2155 // Calculate start time of first last packet of last stream2156 double stream_rel_start_time = nstime_to_sec(&rtpstream->start_rel_time);2157 double stream_abs_start_time = nstime_to_sec(&rtpstream->start_abs_time);2158 double stream_rel_stop_time = nstime_to_sec(&rtpstream->stop_rel_time);2159 2160 if (is_first) {2161 // Take start/stop time for first stream2162 first_stream_rel_start_time_ = stream_rel_start_time;2163 first_stream_abs_start_time_ = stream_abs_start_time;2164 first_stream_rel_stop_time_ = stream_rel_stop_time;2165 } else {2166 // Calculate min/max for start/stop time for other streams2167 first_stream_rel_start_time_ = qMin(first_stream_rel_start_time_, stream_rel_start_time);2168 first_stream_abs_start_time_ = qMin(first_stream_abs_start_time_, stream_abs_start_time);2169 first_stream_rel_stop_time_ = qMax(first_stream_rel_stop_time_, stream_rel_stop_time);2170 }2171 streams_length_ = first_stream_rel_stop_time_ - first_stream_rel_start_time_;2172}2173 2174void RtpPlayerDialog::formatAudioRouting(QTreeWidgetItem *ti, AudioRouting audio_routing)2175{2176 ti->setText(channel_col_, tr(audio_routing.formatAudioRoutingToString()));2177}2178 2179bool RtpPlayerDialog::isStereoAvailable()2180{2181#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))2182 QAudioDevice cur_out_device = getCurrentDeviceInfo();2183 if (cur_out_device.maximumChannelCount() > 1) {2184 return true;2185 }2186#else2187 QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo();2188 foreach(int count, cur_out_device.supportedChannelCounts())for (auto _container_2188 = QtPrivate::qMakeForeachContainer(
cur_out_device.supportedChannelCounts()); _container_2188.i !=
_container_2188.e; ++_container_2188.i) if (int count = *_container_2188
.i; false) {} else
{2189 if (count > 1) {2190 return true;2191 }2192 }2193#endif2194 2195 return false;2196}2197 2198void RtpPlayerDialog::invertSelection()2199{2200 block_redraw_ = true;2201 ui->streamTreeWidget->blockSignals(true);2202 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {2203 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);2204 ti->setSelected(!ti->isSelected());2205 }2206 ui->streamTreeWidget->blockSignals(false);2207 block_redraw_ = false;2208 ui->audioPlot->replot();2209 updateHintLabel();2210}2211 2212void RtpPlayerDialog::on_actionSelectAll_triggered()2213{2214 ui->streamTreeWidget->selectAll();2215 updateHintLabel();2216}2217 2218void RtpPlayerDialog::on_actionSelectInvert_triggered()2219{2220 invertSelection();2221 updateHintLabel();2222}2223 2224void RtpPlayerDialog::on_actionSelectNone_triggered()2225{2226 ui->streamTreeWidget->clearSelection();2227 updateHintLabel();2228}2229 2230void RtpPlayerDialog::on_actionPlay_triggered()2231{2232 if (ui->playButton->isEnabled()) {2233 ui->playButton->animateClick();2234 } else if (ui->pauseButton->isEnabled()) {2235 ui->pauseButton->animateClick();2236 }2237}2238 2239void RtpPlayerDialog::on_actionStop_triggered()2240{2241 if (ui->stopButton->isEnabled()) {2242 ui->stopButton->animateClick();2243 }2244}2245 2246qint64 RtpPlayerDialog::saveAudioHeaderAU(QFile *save_file, quint32 channels, unsigned audio_rate)2247{2248 uint8_t pd[4];2249 int64_t nchars;2250 2251 /* https://pubs.opengroup.org/external/auformat.html */2252 /* First we write the .au header. All values in the header are2253 * 4-byte big-endian values, so we use pntoh32() to copy them2254 * to a 4-byte buffer, in big-endian order, and then write out2255 * the buffer. */2256 2257 /* the magic word 0x2e736e64 == .snd */2258 phton32(pd, 0x2e736e64);2259 nchars = save_file->write((const char *)pd, 4);2260 if (nchars != 4) {2261 return -1;2262 }2263 2264 /* header offset == 24 bytes */2265 phton32(pd, 24);2266 nchars = save_file->write((const char *)pd, 4);2267 if (nchars != 4) {2268 return -1;2269 }2270 2271 /* total length; it is permitted to set this to 0xffffffff */2272 phton32(pd, 0xffffffff);2273 nchars = save_file->write((const char *)pd, 4);2274 if (nchars != 4) {2275 return -1;2276 }2277 2278 /* encoding format == 16-bit linear PCM */2279 phton32(pd, 3);2280 nchars = save_file->write((const char *)pd, 4);2281 if (nchars != 4) {2282 return -1;2283 }2284 2285 /* sample rate [Hz] */2286 phton32(pd, audio_rate);2287 nchars = save_file->write((const char *)pd, 4);2288 if (nchars != 4) {2289 return -1;2290 }2291 2292 /* channels */2293 phton32(pd, channels);2294 nchars = save_file->write((const char *)pd, 4);2295 if (nchars != 4) {2296 return -1;2297 }2298 2299 return save_file->pos();2300}2301 2302qint64 RtpPlayerDialog::saveAudioHeaderWAV(QFile *save_file, quint32 channels, unsigned audio_rate, qint64 samples)2303{2304 uint8_t pd[4];2305 int64_t nchars;2306 int32_t subchunk2Size;2307 int32_t data32;2308 int16_t data16;2309 2310 subchunk2Size = sizeof(SAMPLE) * channels * (int32_t)samples;2311 2312 /* http://soundfile.sapp.org/doc/WaveFormat/ */2313 2314 /* RIFF header, ChunkID 0x52494646 == RIFF */2315 phton32(pd, 0x52494646);2316 nchars = save_file->write((const char *)pd, 4);2317 if (nchars != 4) {2318 return -1;2319 }2320 2321 /* RIFF header, ChunkSize */2322 data32 = 36 + subchunk2Size;2323 nchars = save_file->write((const char *)&data32, 4);2324 if (nchars != 4) {2325 return -1;2326 }2327 2328 /* RIFF header, Format 0x57415645 == WAVE */2329 phton32(pd, 0x57415645);2330 nchars = save_file->write((const char *)pd, 4);2331 if (nchars != 4) {2332 return -1;2333 }2334 2335 /* WAVE fmt header, Subchunk1ID 0x666d7420 == 'fmt ' */2336 phton32(pd, 0x666d7420);2337 nchars = save_file->write((const char *)pd, 4);2338 if (nchars != 4) {2339 return -1;2340 }2341 2342 /* WAVE fmt header, Subchunk1Size */2343 data32 = 16;2344 nchars = save_file->write((const char *)&data32, 4);2345 if (nchars != 4) {2346 return -1;2347 }2348 2349 /* WAVE fmt header, AudioFormat 1 == PCM */2350 data16 = 1;2351 nchars = save_file->write((const char *)&data16, 2);2352 if (nchars != 2) {2353 return -1;2354 }2355 2356 /* WAVE fmt header, NumChannels */2357 data16 = channels;2358 nchars = save_file->write((const char *)&data16, 2);2359 if (nchars != 2) {2360 return -1;2361 }2362 2363 /* WAVE fmt header, SampleRate */2364 data32 = audio_rate;2365 nchars = save_file->write((const char *)&data32, 4);2366 if (nchars != 4) {2367 return -1;2368 }2369 2370 /* WAVE fmt header, ByteRate */2371 data32 = audio_rate * channels * sizeof(SAMPLE);2372 nchars = save_file->write((const char *)&data32, 4);2373 if (nchars != 4) {2374 return -1;2375 }2376 2377 /* WAVE fmt header, BlockAlign */2378 data16 = channels * (int16_t)sizeof(SAMPLE);2379 nchars = save_file->write((const char *)&data16, 2);2380 if (nchars != 2) {2381 return -1;2382 }2383 2384 /* WAVE fmt header, BitsPerSample */2385 data16 = (int16_t)sizeof(SAMPLE) * 8;2386 nchars = save_file->write((const char *)&data16, 2);2387 if (nchars != 2) {2388 return -1;2389 }2390 2391 /* WAVE data header, Subchunk2ID 0x64617461 == 'data' */2392 phton32(pd, 0x64617461);2393 nchars = save_file->write((const char *)pd, 4);2394 if (nchars != 4) {2395 return -1;2396 }2397 2398 /* WAVE data header, Subchunk2Size */2399 data32 = subchunk2Size;2400 nchars = save_file->write((const char *)&data32, 4);2401 if (nchars != 4) {2402 return -1;2403 }2404 2405 /* Now we are ready for saving data */2406 2407 return save_file->pos();2408}2409 2410bool RtpPlayerDialog::writeAudioSilenceSamples(QFile *out_file, qint64 samples, int stream_count)2411{2412 uint8_t pd[2];2413 2414 phton16(pd, 0x0000);2415 for(int s=0; s < stream_count; s++) {2416 for(qint64 i=0; i < samples; i++) {2417 if (sizeof(SAMPLE) != out_file->write((char *)&pd, sizeof(SAMPLE))) {2418 return false;2419 }2420 }2421 }2422 2423 return true;2424}2425 2426bool RtpPlayerDialog::writeAudioStreamsSamples(QFile *out_file, QVector<RtpAudioStream *> streams, bool swap_bytes)2427{2428 SAMPLE sample;2429 uint8_t pd[2];2430 2431 // Did we read something in last cycle?2432 bool read = true;2433 2434 while (read) {2435 read = false;2436 // Loop over all streams, read one sample from each, write to output2437 foreach(RtpAudioStream *audio_stream, streams)for (auto _container_2437 = QtPrivate::qMakeForeachContainer(
streams); _container_2437.i != _container_2437.e; ++_container_2437
.i) if (RtpAudioStream *audio_stream = *_container_2437.i; false
) {} else
{2438 if (sizeof(sample) == audio_stream->readSample(&sample)) {2439 if (swap_bytes) {2440 // same as phton16(), but more clear in compare2441 // to else branch2442 pd[0] = (uint8_t)(sample >> 8);2443 pd[1] = (uint8_t)(sample >> 0);2444 } else {2445 // just copy2446 pd[1] = (uint8_t)(sample >> 8);2447 pd[0] = (uint8_t)(sample >> 0);2448 }2449 read = true;2450 } else {2451 // for 0x0000 doesn't matter on order2452 phton16(pd, 0x0000);2453 }2454 if (sizeof(sample) != out_file->write((char *)&pd, sizeof(sample))) {2455 return false;2456 }2457 }2458 }2459 2460 return true;2461}2462 2463save_audio_t RtpPlayerDialog::selectFileAudioFormatAndName(QString *file_path)2464{2465 QString ext_filter = "";2466 QString ext_filter_wav = tr("WAV (*.wav)");2467 QString ext_filter_au = tr("Sun Audio (*.au)");2468 ext_filter.append(ext_filter_wav);2469 ext_filter.append(";;");2470 ext_filter.append(ext_filter_au);2471 2472 QString sel_filter;2473 *file_path = WiresharkFileDialog::getSaveFileName(2474 this, tr("Save audio"), mainApp->openDialogInitialDir().absoluteFilePath(""),2475 ext_filter, &sel_filter);2476 2477 if (file_path->isEmpty()) return save_audio_none;2478 2479 save_audio_t save_format = save_audio_none;2480 if (0 == QString::compare(sel_filter, ext_filter_au)) {2481 save_format = save_audio_au;2482 } else if (0 == QString::compare(sel_filter, ext_filter_wav)) {2483 save_format = save_audio_wav;2484 }2485 2486 return save_format;2487}2488 2489save_payload_t RtpPlayerDialog::selectFilePayloadFormatAndName(QString *file_path)2490{2491 QString ext_filter = "";2492 QString ext_filter_raw = tr("Raw (*.raw)");2493 ext_filter.append(ext_filter_raw);2494 2495 QString sel_filter;2496 *file_path = WiresharkFileDialog::getSaveFileName(2497 this, tr("Save payload"), mainApp->openDialogInitialDir().absoluteFilePath(""),2498 ext_filter, &sel_filter);2499 2500 if (file_path->isEmpty()) return save_payload_none;2501 2502 save_payload_t save_format = save_payload_none;2503 if (0 == QString::compare(sel_filter, ext_filter_raw)) {2504 save_format = save_payload_data;2505 }2506 2507 return save_format;2508}2509 2510QVector<rtpstream_id_t *>RtpPlayerDialog::getSelectedRtpStreamIDs()2511{2512 QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();2513 QVector<rtpstream_id_t *> ids;2514 2515 if (items.count() > 0) {2516 foreach(QTreeWidgetItem *ti, items)for (auto _container_2516 = QtPrivate::qMakeForeachContainer(
items); _container_2516.i != _container_2516.e; ++_container_2516
.i) if (QTreeWidgetItem *ti = *_container_2516.i; false) {} else
{2517 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();2518 if (audio_stream) {2519 ids << audio_stream->getID();2520 }2521 }2522 }2523 2524 return ids;2525}2526 2527QVector<RtpAudioStream *>RtpPlayerDialog::getSelectedAudibleNonmutedAudioStreams()2528{2529 QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();2530 QVector<RtpAudioStream *> streams;2531 2532 if (items.count() > 0) {2533 foreach(QTreeWidgetItem *ti, items)for (auto _container_2533 = QtPrivate::qMakeForeachContainer(
items); _container_2533.i != _container_2533.e; ++_container_2533
.i) if (QTreeWidgetItem *ti = *_container_2533.i; false) {} else
{2534 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();2535 // Ignore muted streams and streams with no audio2536 if (audio_stream &&2537 !audio_stream->getAudioRouting().isMuted() &&2538 (audio_stream->sampleRate()>0)2539 ) {2540 streams << audio_stream;2541 }2542 }2543 }2544 2545 return streams;2546}2547 2548void RtpPlayerDialog::saveAudio(save_mode_t save_mode)2549{2550 qint64 minSilenceSamples;2551 qint64 startSample;2552 qint64 lead_silence_samples;2553 qint64 maxSample;2554 QString path;2555 QVector<RtpAudioStream *>streams;2556 2557 streams = getSelectedAudibleNonmutedAudioStreams();2558 if (streams.count() < 1) {2559 QMessageBox::warning(this, tr("Warning"), tr("No stream selected or none of selected streams provide audio"));2560 return;2561 }2562 2563 unsigned save_audio_rate = streams[0]->playRate();2564 // Check whether all streams use same audio rate2565 foreach(RtpAudioStream *audio_stream, streams)for (auto _container_2565 = QtPrivate::qMakeForeachContainer(
streams); _container_2565.i != _container_2565.e; ++_container_2565
.i) if (RtpAudioStream *audio_stream = *_container_2565.i; false
) {} else
{2566 if (save_audio_rate != audio_stream->playRate()) {2567 QMessageBox::warning(this, tr("Error"), tr("All selected streams must use same play rate. Manual set of Output Audio Rate might help."));2568 return;2569 }2570 }2571 2572 save_audio_t format = selectFileAudioFormatAndName(&path);2573 if (format == save_audio_none) return;2574 2575 // Use start silence and length of first stream2576 minSilenceSamples = streams[0]->getLeadSilenceSamples();2577 maxSample = streams[0]->getTotalSamples();2578 // Find shortest start silence and longest stream2579 foreach(RtpAudioStream *audio_stream, streams)for (auto _container_2579 = QtPrivate::qMakeForeachContainer(
streams); _container_2579.i != _container_2579.e; ++_container_2579
.i) if (RtpAudioStream *audio_stream = *_container_2579.i; false
) {} else
{2580 if (minSilenceSamples > audio_stream->getLeadSilenceSamples()) {2581 minSilenceSamples = audio_stream->getLeadSilenceSamples();2582 }2583 if (maxSample < audio_stream->getTotalSamples()) {2584 maxSample = audio_stream->getTotalSamples();2585 }2586 }2587 2588 switch (save_mode) {2589 case save_mode_from_cursor:2590 if (ui->todCheckBox->isChecked()) {2591 startSample = start_marker_time_ * save_audio_rate;2592 } else {2593 startSample = (start_marker_time_ - first_stream_rel_start_time_) * save_audio_rate;2594 }2595 lead_silence_samples = 0;2596 break;2597 case save_mode_sync_stream:2598 // Skip start of first stream, no lead silence2599 startSample = minSilenceSamples;2600 lead_silence_samples = 0;2601 break;2602 case save_mode_sync_file:2603 default:2604 // Full first stream, lead silence2605 startSample = 0;2606 lead_silence_samples = first_stream_rel_start_time_ * save_audio_rate;2607 break;2608 }2609 2610 QVector<RtpAudioStream *>temp = QVector<RtpAudioStream *>(streams);2611 2612 // Remove streams shorter than startSample and2613 // seek to correct start for longer ones2614 foreach(RtpAudioStream *audio_stream, temp)for (auto _container_2614 = QtPrivate::qMakeForeachContainer(
temp); _container_2614.i != _container_2614.e; ++_container_2614
.i) if (RtpAudioStream *audio_stream = *_container_2614.i; false
) {} else
{2615 if (startSample > audio_stream->getTotalSamples()) {2616 streams.removeAll(audio_stream);2617 } else {2618 audio_stream->seekSample(startSample);2619 }2620 }2621 2622 if (streams.count() < 1) {2623 QMessageBox::warning(this, tr("Warning"), tr("No streams are suitable for save"));2624 return;2625 }2626 2627 QFile file(path);2628 file.open(QIODevice::WriteOnly);2629 2630 if (!file.isOpen() || (file.error() != QFile::NoError)) {2631 QMessageBox::warning(this, tr("Warning"), tr("Save failed!"));2632 } else {2633 switch (format) {2634 case save_audio_au:2635 if (-1 == saveAudioHeaderAU(&file, static_cast<quint32>(streams.count()), save_audio_rate)) {2636 QMessageBox::warning(this, tr("Error"), tr("Can't write header of AU file"));2637 return;2638 }2639 if (lead_silence_samples > 0) {2640 if (!writeAudioSilenceSamples(&file, lead_silence_samples, static_cast<int>(streams.count()))) {2641 QMessageBox::warning(this, tr("Warning"), tr("Save failed!"));2642 }2643 }2644 if (!writeAudioStreamsSamples(&file, streams, true)) {2645 QMessageBox::warning(this, tr("Warning"), tr("Save failed!"));2646 }2647 break;2648 case save_audio_wav:2649 if (-1 == saveAudioHeaderWAV(&file, static_cast<quint32>(streams.count()), save_audio_rate, (maxSample - startSample) + lead_silence_samples)) {2650 QMessageBox::warning(this, tr("Error"), tr("Can't write header of WAV file"));2651 return;2652 }2653 if (lead_silence_samples > 0) {2654 if (!writeAudioSilenceSamples(&file, lead_silence_samples, static_cast<int>(streams.count()))) {2655 QMessageBox::warning(this, tr("Warning"), tr("Save failed!"));2656 }2657 }2658 if (!writeAudioStreamsSamples(&file, streams, false)) {2659 QMessageBox::warning(this, tr("Warning"), tr("Save failed!"));2660 }2661 break;2662 case save_audio_none:2663 break;2664 }2665 }2666 2667 file.close();2668}2669 2670void RtpPlayerDialog::savePayload()2671{2672 QString path;2673 QList<QTreeWidgetItem *> items;2674 RtpAudioStream *audio_stream = NULL__null;2675 2676 items = ui->streamTreeWidget->selectedItems();2677 foreach(QTreeWidgetItem *ti, items)for (auto _container_2677 = QtPrivate::qMakeForeachContainer(
items); _container_2677.i != _container_2677.e; ++_container_2677
.i) if (QTreeWidgetItem *ti = *_container_2677.i; false) {} else
{2678 audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();2679 if (audio_stream)2680 break;2681 }2682 if (items.count() != 1 || !audio_stream) {2683 QMessageBox::warning(this, tr("Warning"), tr("Payload save works with just one audio stream."));2684 return;2685 }2686 2687 save_payload_t format = selectFilePayloadFormatAndName(&path);2688 if (format == save_payload_none) return;2689 2690 QFile file(path);2691 file.open(QIODevice::WriteOnly);2692 2693 if (!file.isOpen() || (file.error() != QFile::NoError)) {2694 QMessageBox::warning(this, tr("Warning"), tr("Save failed!"));2695 } else if (!audio_stream->savePayload(&file)) {2696 QMessageBox::warning(this, tr("Warning"), tr("Save failed!"));2697 }2698 2699 file.close();2700}2701 2702void RtpPlayerDialog::on_actionSaveAudioFromCursor_triggered()2703{2704 saveAudio(save_mode_from_cursor);2705}2706 2707void RtpPlayerDialog::on_actionSaveAudioSyncStream_triggered()2708{2709 saveAudio(save_mode_sync_stream);2710}2711 2712void RtpPlayerDialog::on_actionSaveAudioSyncFile_triggered()2713{2714 saveAudio(save_mode_sync_file);2715}2716 2717void RtpPlayerDialog::on_actionSavePayload_triggered()2718{2719 savePayload();2720}2721 2722void RtpPlayerDialog::selectInaudible(bool select)2723{2724 block_redraw_ = true;2725 ui->streamTreeWidget->blockSignals(true);2726 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {2727 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);2728 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();2729 // Streams with no audio2730 if (audio_stream && (audio_stream->sampleRate()==0)) {2731 ti->setSelected(select);2732 }2733 }2734 ui->streamTreeWidget->blockSignals(false);2735 block_redraw_ = false;2736 ui->audioPlot->replot();2737 updateHintLabel();2738}2739 2740void RtpPlayerDialog::on_actionSelectInaudible_triggered()2741{2742 selectInaudible(true);2743}2744 2745void RtpPlayerDialog::on_actionDeselectInaudible_triggered()2746{2747 selectInaudible(false);2748}2749 2750void RtpPlayerDialog::on_actionPrepareFilter_triggered()2751{2752 QVector<rtpstream_id_t *> ids = getSelectedRtpStreamIDs();2753 QString filter = make_filter_based_on_rtpstream_id(ids);2754 if (filter.length() > 0) {2755 emit updateFilter(filter);2756 }2757}2758 2759void RtpPlayerDialog::rtpAnalysisReplace()2760{2761 if (ui->streamTreeWidget->selectedItems().count() < 1) return;2762 2763 emit rtpAnalysisDialogReplaceRtpStreams(getSelectedRtpStreamIDs());2764}2765 2766void RtpPlayerDialog::rtpAnalysisAdd()2767{2768 if (ui->streamTreeWidget->selectedItems().count() < 1) return;2769 2770 emit rtpAnalysisDialogAddRtpStreams(getSelectedRtpStreamIDs());2771}2772 2773void RtpPlayerDialog::rtpAnalysisRemove()2774{2775 if (ui->streamTreeWidget->selectedItems().count() < 1) return;2776 2777 emit rtpAnalysisDialogRemoveRtpStreams(getSelectedRtpStreamIDs());2778}2779 2780void RtpPlayerDialog::on_actionReadCapture_triggered()2781{2782#ifdef QT_MULTIMEDIA_LIB12783 QTimer::singleShot(0, this, SLOT(retapPackets())qFlagLocation("1" "retapPackets()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "2783")
);2784#endif2785}2786 2787// _U_ is used for case w have no LIBPCAP2788void RtpPlayerDialog::captureEvent(CaptureEvent e _U___attribute__((unused)))2789{2790#ifdef HAVE_LIBPCAP12791 bool new_read_capture_enabled = false;2792 bool found = false;2793 2794 if ((e.captureContext() & CaptureEvent::Capture) &&2795 (e.eventType() == CaptureEvent::Prepared)2796 ) {2797 new_read_capture_enabled = true;2798 found = true;2799 } else if ((e.captureContext() & CaptureEvent::Capture) &&2800 (e.eventType() == CaptureEvent::Finished)2801 ) {2802 new_read_capture_enabled = false;2803 found = true;2804 }2805 2806 if (found) {2807 bool retap = false;2808 if (read_capture_enabled_ && !new_read_capture_enabled) {2809 // Capturing ended, automatically refresh data2810 retap = true;2811 }2812 read_capture_enabled_ = new_read_capture_enabled;2813 updateWidgets();2814 if (retap) {2815 QTimer::singleShot(0, this, SLOT(retapPackets())qFlagLocation("1" "retapPackets()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "2815")
);2816 }2817 }2818#endif2819}2820 2821#endif // QT_MULTIMEDIA_LIB
1// Copyright (C) 2016 The Qt Company Ltd.2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only3 4#ifndef QPOINTER_H5#define QPOINTER_H6 7#include <QtCore/qsharedpointer.h>8#include <QtCore/qtypeinfo.h>9 10#ifndef QT_NO_QOBJECT11 12QT_BEGIN_NAMESPACE13 14class QVariant;15 16template <class T>17class QPointer18{19 static_assert(!std::is_pointer<T>::value, "QPointer's template type must not be a pointer type");20 21 using QObjectType =22 typename std::conditional<std::is_const<T>::value, const QObject, QObject>::type;23 QWeakPointer<QObjectType> wp;24public:25 QPointer() = default;26 inline QPointer(T *p) : wp(p, true) { }27 // compiler-generated copy/move ctor/assignment operators are fine!28 // compiler-generated dtor is fine!29 30#ifdef Q_QDOC31 // Stop qdoc from complaining about missing function32 ~QPointer();33#endif34 35 inline void swap(QPointer &other) noexcept { wp.swap(other.wp); }36 37 inline QPointer<T> &operator=(T* p)38 { wp.assign(static_cast<QObjectType*>(p)); return *this; }39 40 inline T* data() const41 { return static_cast<T*>(wp.internalData()); }42 inline T* get() const43 { return data(); }44 inline T* operator->() const45 { return data(); }46 inline T& operator*() const47 { return *data(); }48 inline operator T*() const49 { return data(); }50 51 inline bool isNull() const52 { return wp.isNull(); }53 54 inline void clear()55 { wp.clear(); }56 57#define DECLARE_COMPARE_SET(T1, A1, T2, A2) \58 friend bool operator==(T1, T2) \59 { return A1 == A2; } \60 friend bool operator!=(T1, T2) \61 { return A1 != A2; }62 63#define DECLARE_TEMPLATE_COMPARE_SET(T1, A1, T2, A2) \64 template <typename X> \65 friend bool operator==(T1, T2) noexcept \66 { return A1 == A2; } \67 template <typename X> \68 friend bool operator!=(T1, T2) noexcept \69 { return A1 != A2; }70 71 DECLARE_TEMPLATE_COMPARE_SET(const QPointer &p1, p1.data(), const QPointer<X> &p2, p2.data())72 DECLARE_TEMPLATE_COMPARE_SET(const QPointer &p1, p1.data(), X *ptr, ptr)73 DECLARE_TEMPLATE_COMPARE_SET(X *ptr, ptr, const QPointer &p2, p2.data())74 DECLARE_COMPARE_SET(const QPointer &p1, p1.data(), std::nullptr_t, nullptr)75 DECLARE_COMPARE_SET(std::nullptr_t, nullptr, const QPointer &p2, p2.data())76#undef DECLARE_COMPARE_SET77#undef DECLARE_TEMPLATE_COMPARE_SET78};79template <class T> Q_DECLARE_TYPEINFO_BODY(QPointer<T>, Q_RELOCATABLE_TYPE)class QTypeInfo<QPointer<T> > { public: enum { isComplex
= (((Q_RELOCATABLE_TYPE) & Q_PRIMITIVE_TYPE) == 0) &&
!std::is_trivial_v<QPointer<T> >, isRelocatable =
!isComplex || ((Q_RELOCATABLE_TYPE) & Q_RELOCATABLE_TYPE
) || qIsRelocatable<QPointer<T> >, isPointer = false
, isIntegral = std::is_integral< QPointer<T> >::value
, }; }
;80 81template<typename T>82QPointer<T>83qPointerFromVariant(const QVariant &variant)84{85 const auto wp = QtSharedPointer::weakPointerFromVariant_internal(variant);86 return QPointer<T>{qobject_cast<T*>(QtPrivate::EnableInternalData::internalData(wp))};87}88 89template <class T>90inline void swap(QPointer<T> &p1, QPointer<T> &p2) noexcept91{ p1.swap(p2); }92 93QT_END_NAMESPACE94 95#endif // QT_NO_QOBJECT96 97#endif // QPOINTER_H
1// Copyright (C) 2021 The Qt Company Ltd.2// Copyright (C) 2022 Intel Corporation.3// Copyright (C) 2019 Klarälvdalens Datakonsult AB.4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only5 6#ifndef Q_QDOC7 8#ifndef QSHAREDPOINTER_H9#error Do not include qsharedpointer_impl.h directly10#endif11 12#if 013#pragma qt_sync_skip_header_check14#pragma qt_sync_stop_processing15#endif16 17#if 018// These macros are duplicated here to make syncqt not complain a about19// this header, as we have a "qt_sync_stop_processing" below, which in turn20// is here because this file contains a template mess and duplicates the21// classes found in qsharedpointer.h22QT_BEGIN_NAMESPACE23QT_END_NAMESPACE24#pragma qt_sync_stop_processing25#endif26 27#include <new>28#include <QtCore/qatomic.h>29#include <QtCore/qhashfunctions.h>30#include <QtCore/qmetatype.h> // for IsPointerToTypeDerivedFromQObject31 32#include <memory>33 34QT_BEGIN_NAMESPACE35 36class QObject;37template <class T>38T qobject_cast(const QObject *object);39 40//41// forward declarations42//43template <class T> class QWeakPointer;44template <class T> class QSharedPointer;45template <class T> class QEnableSharedFromThis;46 47class QVariant;48 49template <class X, class T>50QSharedPointer<X> qSharedPointerCast(const QSharedPointer<T> &ptr);51template <class X, class T>52QSharedPointer<X> qSharedPointerDynamicCast(const QSharedPointer<T> &ptr);53template <class X, class T>54QSharedPointer<X> qSharedPointerConstCast(const QSharedPointer<T> &ptr);55 56#ifndef QT_NO_QOBJECT57template <class X, class T>58QSharedPointer<X> qSharedPointerObjectCast(const QSharedPointer<T> &ptr);59#endif60 61namespace QtPrivate {62struct EnableInternalData;63}64 65namespace QtSharedPointer {66 template <class T> class ExternalRefCount;67 68 template <class X, class Y> QSharedPointer<X> copyAndSetPointer(X * ptr, const QSharedPointer<Y> &src);69 70 // used in debug mode to verify the reuse of pointers71 Q_CORE_EXPORT__attribute__((visibility("default"))) void internalSafetyCheckAdd(const void *, const volatile void *);72 Q_CORE_EXPORT__attribute__((visibility("default"))) void internalSafetyCheckRemove(const void *);73 74 template <class T, typename Klass, typename RetVal>75 inline void executeDeleter(T *t, RetVal (Klass:: *memberDeleter)())76 { if (t) (t->*memberDeleter)(); }77 template <class T, typename Deleter>78 inline void executeDeleter(T *t, Deleter d)79 { d(t); }80 struct NormalDeleter {};81 82 // this uses partial template specialization83 template <class T> struct RemovePointer;84 template <class T> struct RemovePointer<T *> { typedef T Type; };85 template <class T> struct RemovePointer<QSharedPointer<T> > { typedef T Type; };86 template <class T> struct RemovePointer<QWeakPointer<T> > { typedef T Type; };87 88 // This class is the d-pointer of QSharedPointer and QWeakPointer.89 //90 // It is a reference-counted reference counter. "strongref" is the inner91 // reference counter, and it tracks the lifetime of the pointer itself.92 // "weakref" is the outer reference counter and it tracks the lifetime of93 // the ExternalRefCountData object.94 //95 // The deleter is stored in the destroyer member and is always a pointer to96 // a static function in ExternalRefCountWithCustomDeleter or in97 // ExternalRefCountWithContiguousData98 struct ExternalRefCountData99 {100 typedef void (*DestroyerFn)(ExternalRefCountData *);101 QBasicAtomicInt weakref;102 QBasicAtomicInt strongref;103 DestroyerFn destroyer;104 105 inline ExternalRefCountData(DestroyerFn d)106 : destroyer(d)107 {108 strongref.storeRelaxed(1);109 weakref.storeRelaxed(1);110 }111 inline ExternalRefCountData(Qt::Initialization) { }112 ~ExternalRefCountData() { Q_ASSERT(!weakref.loadRelaxed())((!weakref.loadRelaxed()) ? static_cast<void>(0) : qt_assert
("!weakref.loadRelaxed()", "/usr/include/x86_64-linux-gnu/qt6/QtCore/qsharedpointer_impl.h"
, 112))
; Q_ASSERT(strongref.loadRelaxed() <= 0)((strongref.loadRelaxed() <= 0) ? static_cast<void>(
0) : qt_assert("strongref.loadRelaxed() <= 0", "/usr/include/x86_64-linux-gnu/qt6/QtCore/qsharedpointer_impl.h"
, 112))
; }113 114 void destroy() { destroyer(this); }115 116#ifndef QT_NO_QOBJECT117 Q_CORE_EXPORT__attribute__((visibility("default"))) static ExternalRefCountData *getAndRef(const QObject *);118 Q_CORE_EXPORT__attribute__((visibility("default"))) void setQObjectShared(const QObject *, bool enable);119 Q_CORE_EXPORT__attribute__((visibility("default"))) void checkQObjectShared(const QObject *);120#endif121 inline void checkQObjectShared(...) { }122 inline void setQObjectShared(...) { }123 124 // Normally, only subclasses of ExternalRefCountData are allocated125 // One exception exists in getAndRef; that uses the global operator new126 // to prevent a mismatch with the custom operator delete127 inline void *operator new(std::size_t) = delete;128 // placement new129 inline void *operator new(std::size_t, void *ptr) noexcept { return ptr; }130 inline void operator delete(void *ptr) { ::operator delete(ptr); }

15

Use of memory after it is freed
131 inline void operator delete(void *, void *) { }132 };133 // sizeof(ExternalRefCountData) = 12 (32-bit) / 16 (64-bit)134 135 template <class T, typename Deleter>136 struct CustomDeleter137 {138 Deleter deleter;139 T *ptr;140 141 CustomDeleter(T *p, Deleter d) : deleter(d), ptr(p) {}142 void execute() { executeDeleter(ptr, deleter); }143 };144 // sizeof(CustomDeleter) = sizeof(Deleter) + sizeof(void*) + padding145 // for Deleter = stateless functor: 8 (32-bit) / 16 (64-bit) due to padding146 // for Deleter = function pointer: 8 (32-bit) / 16 (64-bit)147 // for Deleter = PMF: 12 (32-bit) / 24 (64-bit) (GCC)148 149 // This specialization of CustomDeleter for a deleter of type NormalDeleter150 // is an optimization: instead of storing a pointer to a function that does151 // the deleting, we simply delete the pointer ourselves.152 template <class T>153 struct CustomDeleter<T, NormalDeleter>154 {155 T *ptr;156 157 CustomDeleter(T *p, NormalDeleter) : ptr(p) {}158 void execute() { delete ptr; }159 };160 // sizeof(CustomDeleter specialization) = sizeof(void*)161 162 // This class extends ExternalRefCountData and implements163 // the static function that deletes the object. The pointer and the164 // custom deleter are kept in the "extra" member so we can construct165 // and destruct it independently of the full structure.166 template <class T, typename Deleter>167 struct ExternalRefCountWithCustomDeleter: public ExternalRefCountData168 {169 typedef ExternalRefCountWithCustomDeleter Self;170 typedef ExternalRefCountData BaseClass;171 CustomDeleter<T, Deleter> extra;172 173 static inline void deleter(ExternalRefCountData *self)174 {175 Self *realself = static_cast<Self *>(self);176 realself->extra.execute();177 178 // delete the deleter too179 realself->extra.~CustomDeleter<T, Deleter>();180 }181 static void safetyCheckDeleter(ExternalRefCountData *self)182 {183 internalSafetyCheckRemove(self);184 deleter(self);185 }186 187 static inline Self *create(T *ptr, Deleter userDeleter, DestroyerFn actualDeleter)188 {189 Self *d = static_cast<Self *>(::operator new(sizeof(Self)));190 191 // initialize the two sub-objects192 new (&d->extra) CustomDeleter<T, Deleter>(ptr, userDeleter);193 new (d) BaseClass(actualDeleter); // can't throw194 195 return d;196 }197 private:198 // prevent construction199 ExternalRefCountWithCustomDeleter() = delete;200 ~ExternalRefCountWithCustomDeleter() = delete;201 Q_DISABLE_COPY(ExternalRefCountWithCustomDeleter)ExternalRefCountWithCustomDeleter(const ExternalRefCountWithCustomDeleter
&) = delete; ExternalRefCountWithCustomDeleter &operator
=(const ExternalRefCountWithCustomDeleter &) = delete;
202 };203 204 // This class extends ExternalRefCountData and adds a "T"205 // member. That way, when the create() function is called, we allocate206 // memory for both QSharedPointer's d-pointer and the actual object being207 // tracked.208 template <class T>209 struct ExternalRefCountWithContiguousData: public ExternalRefCountData210 {211 typedef ExternalRefCountData Parent;212 typedef typename std::remove_cv<T>::type NoCVType;213 NoCVType data;214 215 static void deleter(ExternalRefCountData *self)216 {217 ExternalRefCountWithContiguousData *that =218 static_cast<ExternalRefCountWithContiguousData *>(self);219 that->data.~T();220 Q_UNUSED(that)(void)that;; // MSVC warns if T has a trivial destructor221 }222 static void safetyCheckDeleter(ExternalRefCountData *self)223 {224 internalSafetyCheckRemove(self);225 deleter(self);226 }227 static void noDeleter(ExternalRefCountData *) { }228 229 static inline ExternalRefCountData *create(NoCVType **ptr, DestroyerFn destroy)230 {231 ExternalRefCountWithContiguousData *d =232 static_cast<ExternalRefCountWithContiguousData *>(::operator new(sizeof(ExternalRefCountWithContiguousData)));233 234 // initialize the d-pointer sub-object235 // leave d->data uninitialized236 new (d) Parent(destroy); // can't throw237 238 *ptr = &d->data;239 return d;240 }241 242 private:243 // prevent construction244 ExternalRefCountWithContiguousData() = delete;245 ~ExternalRefCountWithContiguousData() = delete;246 Q_DISABLE_COPY(ExternalRefCountWithContiguousData)ExternalRefCountWithContiguousData(const ExternalRefCountWithContiguousData
&) = delete; ExternalRefCountWithContiguousData &operator
=(const ExternalRefCountWithContiguousData &) = delete;
247 };248 249#ifndef QT_NO_QOBJECT250 Q_CORE_EXPORT__attribute__((visibility("default"))) QWeakPointer<QObject> weakPointerFromVariant_internal(const QVariant &variant);251 Q_CORE_EXPORT__attribute__((visibility("default"))) QSharedPointer<QObject> sharedPointerFromVariant_internal(const QVariant &variant);252#endif253} // namespace QtSharedPointer254 255template <class T> class QSharedPointer256{257 typedef QtSharedPointer::ExternalRefCountData Data;258 template <typename X>259 using IfCompatible = typename std::enable_if<std::is_convertible<X*, T*>::value, bool>::type;260 261public:262 typedef T Type;263 typedef T element_type;264 typedef T value_type;265 typedef value_type *pointer;266 typedef const value_type *const_pointer;267 typedef value_type &reference;268 typedef const value_type &const_reference;269 typedef qptrdiff difference_type;270 271 T *data() const noexcept { return value; }272 T *get() const noexcept { return value; }273 bool isNull() const noexcept { return !data(); }274 explicit operator bool() const noexcept { return !isNull(); }275 bool operator !() const noexcept { return isNull(); }276 T &operator*() const { return *data(); }277 T *operator->() const noexcept { return data(); }278 279 constexpr QSharedPointer() noexcept : value(nullptr), d(nullptr) { }280 ~QSharedPointer() { deref(); }281 282 constexpr QSharedPointer(std::nullptr_t) noexcept : value(nullptr), d(nullptr) { }283 284 template <class X, IfCompatible<X> = true>285 inline explicit QSharedPointer(X *ptr) : value(ptr) // noexcept286 { internalConstruct(ptr, QtSharedPointer::NormalDeleter()); }287 288 template <class X, typename Deleter, IfCompatible<X> = true>289 inline QSharedPointer(X *ptr, Deleter deleter) : value(ptr) // throws290 { internalConstruct(ptr, deleter); }291 292 template <typename Deleter>293 QSharedPointer(std::nullptr_t, Deleter deleter) : value(nullptr)294 { internalConstruct(static_cast<T *>(nullptr), deleter); }295 296 QSharedPointer(const QSharedPointer &other) noexcept : value(other.value), d(other.d)297 { if (d) ref(); }298 QSharedPointer &operator=(const QSharedPointer &other) noexcept299 {300 QSharedPointer copy(other);301 swap(copy);302 return *this;303 }304 QSharedPointer(QSharedPointer &&other) noexcept305 : value(other.value), d(other.d)306 {307 other.d = nullptr;308 other.value = nullptr;309 }310 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QSharedPointer)QSharedPointer &operator=(QSharedPointer &&other)
noexcept { QSharedPointer moved(std::move(other)); swap(moved
); return *this; }
311 312 template <class X, IfCompatible<X> = true>313 QSharedPointer(QSharedPointer<X> &&other) noexcept314 : value(other.value), d(other.d)315 {316 other.d = nullptr;317 other.value = nullptr;318 }319 320 template <class X, IfCompatible<X> = true>321 QSharedPointer &operator=(QSharedPointer<X> &&other) noexcept322 {323 QSharedPointer moved(std::move(other));324 swap(moved);325 return *this;326 }327 328 template <class X, IfCompatible<X> = true>329 QSharedPointer(const QSharedPointer<X> &other) noexcept : value(other.value), d(other.d)330 { if (d) ref(); }331 332 template <class X, IfCompatible<X> = true>333 inline QSharedPointer &operator=(const QSharedPointer<X> &other)334 {335 QSharedPointer copy(other);336 swap(copy);337 return *this;338 }339 340 template <class X, IfCompatible<X> = true>341 inline QSharedPointer(const QWeakPointer<X> &other) : value(nullptr), d(nullptr)342 { *this = other; }343 344 template <class X, IfCompatible<X> = true>345 inline QSharedPointer<T> &operator=(const QWeakPointer<X> &other)346 { internalSet(other.d, other.value); return *this; }347 348 inline void swap(QSharedPointer &other) noexcept349 { this->internalSwap(other); }350 351 inline void reset() { clear(); }352 inline void reset(T *t)353 { QSharedPointer copy(t); swap(copy); }354 template <typename Deleter>355 inline void reset(T *t, Deleter deleter)356 { QSharedPointer copy(t, deleter); swap(copy); }357 358 template <class X>359 QSharedPointer<X> staticCast() const360 {361 return qSharedPointerCast<X, T>(*this);362 }363 364 template <class X>365 QSharedPointer<X> dynamicCast() const366 {367 return qSharedPointerDynamicCast<X, T>(*this);368 }369 370 template <class X>371 QSharedPointer<X> constCast() const372 {373 return qSharedPointerConstCast<X, T>(*this);374 }375 376#ifndef QT_NO_QOBJECT377 template <class X>378 QSharedPointer<X> objectCast() const379 {380 return qSharedPointerObjectCast<X, T>(*this);381 }382#endif383 384 inline void clear() { QSharedPointer copy; swap(copy); }385 386 QWeakPointer<T> toWeakRef() const;387 388 template <typename... Args>389 static QSharedPointer create(Args && ...arguments)390 {391 typedef QtSharedPointer::ExternalRefCountWithContiguousData<T> Private;392# ifdef QT_SHAREDPOINTER_TRACK_POINTERS393 typename Private::DestroyerFn destroy = &Private::safetyCheckDeleter;394# else395 typename Private::DestroyerFn destroy = &Private::deleter;396# endif397 typename Private::DestroyerFn noDestroy = &Private::noDeleter;398 QSharedPointer result(Qt::Uninitialized);399 typename std::remove_cv<T>::type *ptr;400 result.d = Private::create(&ptr, noDestroy);401 402 // now initialize the data403 new (ptr) T(std::forward<Args>(arguments)...);404 result.value = ptr;405 result.d->destroyer = destroy;406 result.d->setQObjectShared(result.value, true);407# ifdef QT_SHAREDPOINTER_TRACK_POINTERS408 internalSafetyCheckAdd(result.d, result.value);409# endif410 result.enableSharedFromThis(result.data());411 return result;412 }413 414#define DECLARE_COMPARE_SET(T1, A1, T2, A2) \415 friend bool operator==(T1, T2) noexcept \416 { return A1 == A2; } \417 friend bool operator!=(T1, T2) noexcept \418 { return A1 != A2; }419 420#define DECLARE_TEMPLATE_COMPARE_SET(T1, A1, T2, A2) \421 template <typename X> \422 friend bool operator==(T1, T2) noexcept \423 { return A1 == A2; } \424 template <typename X> \425 friend bool operator!=(T1, T2) noexcept \426 { return A1 != A2; }427 428 DECLARE_TEMPLATE_COMPARE_SET(const QSharedPointer &p1, p1.data(), const QSharedPointer<X> &p2, p2.data())429 DECLARE_TEMPLATE_COMPARE_SET(const QSharedPointer &p1, p1.data(), X *ptr, ptr)430 DECLARE_TEMPLATE_COMPARE_SET(X *ptr, ptr, const QSharedPointer &p2, p2.data())431 DECLARE_COMPARE_SET(const QSharedPointer &p1, p1.data(), std::nullptr_t, nullptr)432 DECLARE_COMPARE_SET(std::nullptr_t, nullptr, const QSharedPointer &p2, p2.data())433#undef DECLARE_TEMPLATE_COMPARE_SET434#undef DECLARE_COMPARE_SET435 436private:437 explicit QSharedPointer(Qt::Initialization) {}438 439 void deref() noexcept440 { deref(d); }441 static void deref(Data *dd) noexcept442 {443 if (!dd) return;444 if (!dd->strongref.deref()) {445 dd->destroy();446 }447 if (!dd->weakref.deref())448 delete dd;449 }450 451 template <class X>452 inline void enableSharedFromThis(const QEnableSharedFromThis<X> *ptr)453 {454 ptr->initializeFromSharedPointer(constCast<typename std::remove_cv<T>::type>());455 }456 457 inline void enableSharedFromThis(...) {}458 459 template <typename X, typename Deleter>460 inline void internalConstruct(X *ptr, Deleter deleter)461 {462 typedef QtSharedPointer::ExternalRefCountWithCustomDeleter<X, Deleter> Private;463# ifdef QT_SHAREDPOINTER_TRACK_POINTERS464 typename Private::DestroyerFn actualDeleter = &Private::safetyCheckDeleter;465# else466 typename Private::DestroyerFn actualDeleter = &Private::deleter;467# endif468 d = Private::create(ptr, deleter, actualDeleter);469 470#ifdef QT_SHAREDPOINTER_TRACK_POINTERS471 internalSafetyCheckAdd(d, ptr);472#endif473 d->setQObjectShared(ptr, true);474 enableSharedFromThis(ptr);475 }476 477 void internalSwap(QSharedPointer &other) noexcept478 {479 qt_ptr_swap(d, other.d);480 qt_ptr_swap(this->value, other.value);481 }482 483 template <class X> friend class QSharedPointer;484 template <class X> friend class QWeakPointer;485 template <class X, class Y> friend QSharedPointer<X> QtSharedPointer::copyAndSetPointer(X * ptr, const QSharedPointer<Y> &src);486 void ref() const noexcept { d->weakref.ref(); d->strongref.ref(); }487 488 inline void internalSet(Data *o, T *actual)489 {490 if (o) {491 // increase the strongref, but never up from zero492 // or less (-1 is used by QWeakPointer on untracked QObject)493 int tmp = o->strongref.loadRelaxed();494 while (tmp > 0) {495 // try to increment from "tmp" to "tmp + 1"496 if (o->strongref.testAndSetRelaxed(tmp, tmp + 1))497 break; // succeeded498 tmp = o->strongref.loadRelaxed(); // failed, try again499 }500 501 if (tmp > 0) {502 o->weakref.ref();503 } else {504 o->checkQObjectShared(actual);505 o = nullptr;506 }507 }508 509 qt_ptr_swap(d, o);510 qt_ptr_swap(this->value, actual);511 if (!d || d->strongref.loadRelaxed() == 0)512 this->value = nullptr;513 514 // dereference saved data515 deref(o);516 }517 518 Type *value;519 Data *d;520};521 522template <class T>523class QWeakPointer524{525 typedef QtSharedPointer::ExternalRefCountData Data;526 template <typename X>527 using IfCompatible = typename std::enable_if<std::is_convertible<X*, T*>::value, bool>::type;528 529public:530 typedef T element_type;531 typedef T value_type;532 typedef value_type *pointer;533 typedef const value_type *const_pointer;534 typedef value_type &reference;535 typedef const value_type &const_reference;536 typedef qptrdiff difference_type;537 538 bool isNull() const noexcept { return d == nullptr || d->strongref.loadRelaxed() == 0 || value == nullptr; }539 explicit operator bool() const noexcept { return !isNull(); }540 bool operator !() const noexcept { return isNull(); }541 542 constexpr QWeakPointer() noexcept : d(nullptr), value(nullptr) { }543 inline ~QWeakPointer() { if (d && !d->weakref.deref()) delete d; }

10

Assuming field 'd' is non-null

11

Assuming the condition is true

12

Taking true branch

13

Memory is released

14

Calling 'ExternalRefCountData::operator delete'

544 545 QWeakPointer(const QWeakPointer &other) noexcept : d(other.d), value(other.value)546 { if (d) d->weakref.ref(); }547 QWeakPointer(QWeakPointer &&other) noexcept548 : d(other.d), value(other.value)549 {550 other.d = nullptr;551 other.value = nullptr;552 }553 QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_MOVE_AND_SWAP(QWeakPointer)QWeakPointer &operator=(QWeakPointer &&other) noexcept
{ QWeakPointer moved(std::move(other)); swap(moved); return *
this; }
554 555 template <class X, IfCompatible<X> = true>556 QWeakPointer(QWeakPointer<X> &&other) noexcept557 : d(other.d), value(other.value)558 {559 other.d = nullptr;560 other.value = nullptr;561 }562 563 template <class X, IfCompatible<X> = true>564 QWeakPointer &operator=(QWeakPointer<X> &&other) noexcept565 {566 QWeakPointer moved(std::move(other));567 swap(moved);568 return *this;569 }570 571 QWeakPointer &operator=(const QWeakPointer &other) noexcept572 {573 QWeakPointer copy(other);574 swap(copy);575 return *this;576 }577 578 void swap(QWeakPointer &other) noexcept579 {580 qt_ptr_swap(this->d, other.d);581 qt_ptr_swap(this->value, other.value);582 }583 584 inline QWeakPointer(const QSharedPointer<T> &o) : d(o.d), value(o.data())585 { if (d) d->weakref.ref();}586 inline QWeakPointer &operator=(const QSharedPointer<T> &o)587 {588 internalSet(o.d, o.value);589 return *this;590 }591 592 template <class X, IfCompatible<X> = true>593 inline QWeakPointer(const QWeakPointer<X> &o) : d(nullptr), value(nullptr)594 { *this = o; }595 596 template <class X, IfCompatible<X> = true>597 inline QWeakPointer &operator=(const QWeakPointer<X> &o)598 {599 // conversion between X and T could require access to the virtual table600 // so force the operation to go through QSharedPointer601 *this = o.toStrongRef();602 return *this;603 }604 605 template <class X, IfCompatible<X> = true>606 inline QWeakPointer(const QSharedPointer<X> &o) : d(nullptr), value(nullptr)607 { *this = o; }608 609 template <class X, IfCompatible<X> = true>610 inline QWeakPointer &operator=(const QSharedPointer<X> &o)611 {612 internalSet(o.d, o.data());613 return *this;614 }615 616 inline void clear() { *this = QWeakPointer(); }617 618 inline QSharedPointer<T> toStrongRef() const { return QSharedPointer<T>(*this); }619 // std::weak_ptr compatibility:620 inline QSharedPointer<T> lock() const { return toStrongRef(); }621 622 template <class X>623 bool operator==(const QWeakPointer<X> &o) const noexcept624 { return d == o.d && value == static_cast<const T *>(o.value); }625 626 template <class X>627 bool operator!=(const QWeakPointer<X> &o) const noexcept628 { return !(*this == o); }629 630 template <class X>631 bool operator==(const QSharedPointer<X> &o) const noexcept632 { return d == o.d; }633 634 template <class X>635 bool operator!=(const QSharedPointer<X> &o) const noexcept636 { return !(*this == o); }637 638 template <typename X>639 friend bool operator==(const QSharedPointer<X> &p1, const QWeakPointer &p2) noexcept640 { return p2 == p1; }641 template <typename X>642 friend bool operator!=(const QSharedPointer<X> &p1, const QWeakPointer &p2) noexcept643 { return p2 != p1; }644 645 friend bool operator==(const QWeakPointer &p, std::nullptr_t)646 { return p.isNull(); }647 friend bool operator==(std::nullptr_t, const QWeakPointer &p)648 { return p.isNull(); }649 friend bool operator!=(const QWeakPointer &p, std::nullptr_t)650 { return !p.isNull(); }651 friend bool operator!=(std::nullptr_t, const QWeakPointer &p)652 { return !p.isNull(); }653 654private:655 friend struct QtPrivate::EnableInternalData;656 template <class X> friend class QSharedPointer;657 template <class X> friend class QWeakPointer;658 template <class X> friend class QPointer;659 660 template <class X>661 inline QWeakPointer &assign(X *ptr)662 { return *this = QWeakPointer<X>(ptr, true); }663 664#ifndef QT_NO_QOBJECT665 template <class X, IfCompatible<X> = true>666 inline QWeakPointer(X *ptr, bool) : d(ptr ? Data::getAndRef(ptr) : nullptr), value(ptr)667 { }668#endif669 670 inline void internalSet(Data *o, T *actual)671 {672 if (d == o) return;673 if (o)674 o->weakref.ref();675 if (d && !d->weakref.deref())676 delete d;677 d = o;678 value = actual;679 }680 681 // ### TODO - QTBUG-88102: remove all users of this API; no one should ever682 // access a weak pointer's data but the weak pointer itself683 inline T *internalData() const noexcept684 {685 return d == nullptr || d->strongref.loadRelaxed() == 0 ? nullptr : value;686 }687 688 Data *d;689 T *value;690};691 692namespace QtPrivate {693struct EnableInternalData {694 template <typename T>695 static T *internalData(const QWeakPointer<T> &p) noexcept { return p.internalData(); }696};697// hack to delay name lookup to instantiation time by making698// EnableInternalData a dependent name:699template <typename T>700struct EnableInternalDataWrap : EnableInternalData {};701}702 703template <class T>704class QEnableSharedFromThis705{706protected:707 QEnableSharedFromThis() = default;708 QEnableSharedFromThis(const QEnableSharedFromThis &) {}709 QEnableSharedFromThis &operator=(const QEnableSharedFromThis &) { return *this; }710 711public:712 inline QSharedPointer<T> sharedFromThis() { return QSharedPointer<T>(weakPointer); }713 inline QSharedPointer<const T> sharedFromThis() const { return QSharedPointer<const T>(weakPointer); }714 715private:716 template <class X> friend class QSharedPointer;717 template <class X>718 inline void initializeFromSharedPointer(const QSharedPointer<X> &ptr) const719 {720 weakPointer = ptr;721 }722 723 mutable QWeakPointer<T> weakPointer;724};725 726//727// operator-728//729template <class T, class X>730Q_INLINE_TEMPLATEinline typename QSharedPointer<T>::difference_type operator-(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2)731{732 return ptr1.data() - ptr2.data();733}734template <class T, class X>735Q_INLINE_TEMPLATEinline typename QSharedPointer<T>::difference_type operator-(const QSharedPointer<T> &ptr1, X *ptr2)736{737 return ptr1.data() - ptr2;738}739template <class T, class X>740Q_INLINE_TEMPLATEinline typename QSharedPointer<X>::difference_type operator-(T *ptr1, const QSharedPointer<X> &ptr2)741{742 return ptr1 - ptr2.data();743}744 745//746// operator<747//748template <class T, class X>749Q_INLINE_TEMPLATEinline bool operator<(const QSharedPointer<T> &ptr1, const QSharedPointer<X> &ptr2)750{751 using CT = typename std::common_type<T *, X *>::type;752 return std::less<CT>()(ptr1.data(), ptr2.data());753}754template <class T, class X>755Q_INLINE_TEMPLATEinline bool operator<(const QSharedPointer<T> &ptr1, X *ptr2)756{757 using CT = typename std::common_type<T *, X *>::type;758 return std::less<CT>()(ptr1.data(), ptr2);759}760template <class T, class X>761Q_INLINE_TEMPLATEinline bool operator<(T *ptr1, const QSharedPointer<X> &ptr2)762{763 using CT = typename std::common_type<T *, X *>::type;764 return std::less<CT>()(ptr1, ptr2.data());765}766 767//768// qHash769//770template <class T>771Q_INLINE_TEMPLATEinline size_t qHash(const QSharedPointer<T> &ptr, size_t seed = 0)772{773 return qHash(ptr.data(), seed);774}775 776 777template <class T>778Q_INLINE_TEMPLATEinline QWeakPointer<T> QSharedPointer<T>::toWeakRef() const779{780 return QWeakPointer<T>(*this);781}782 783template <class T>784inline void swap(QSharedPointer<T> &p1, QSharedPointer<T> &p2) noexcept785{ p1.swap(p2); }786 787template <class T>788inline void swap(QWeakPointer<T> &p1, QWeakPointer<T> &p2) noexcept789{ p1.swap(p2); }790 791namespace QtSharedPointer {792// helper functions:793 template <class X, class T>794 Q_INLINE_TEMPLATEinline QSharedPointer<X> copyAndSetPointer(X *ptr, const QSharedPointer<T> &src)795 {796 QSharedPointer<X> result;797 result.internalSet(src.d, ptr);798 return result;799 }800}801 802// cast operators803template <class X, class T>804Q_INLINE_TEMPLATEinline QSharedPointer<X> qSharedPointerCast(const QSharedPointer<T> &src)805{806 X *ptr = static_cast<X *>(src.data()); // if you get an error in this line, the cast is invalid807 return QtSharedPointer::copyAndSetPointer(ptr, src);808}809template <class X, class T>810Q_INLINE_TEMPLATEinline QSharedPointer<X> qSharedPointerCast(const QWeakPointer<T> &src)811{812 return qSharedPointerCast<X, T>(src.toStrongRef());813}814 815template <class X, class T>816Q_INLINE_TEMPLATEinline QSharedPointer<X> qSharedPointerDynamicCast(const QSharedPointer<T> &src)817{818 X *ptr = dynamic_cast<X *>(src.data()); // if you get an error in this line, the cast is invalid819 if (!ptr)820 return QSharedPointer<X>();821 return QtSharedPointer::copyAndSetPointer(ptr, src);822}823template <class X, class T>824Q_INLINE_TEMPLATEinline QSharedPointer<X> qSharedPointerDynamicCast(const QWeakPointer<T> &src)825{826 return qSharedPointerDynamicCast<X, T>(src.toStrongRef());827}828 829template <class X, class T>830Q_INLINE_TEMPLATEinline QSharedPointer<X> qSharedPointerConstCast(const QSharedPointer<T> &src)831{832 X *ptr = const_cast<X *>(src.data()); // if you get an error in this line, the cast is invalid833 return QtSharedPointer::copyAndSetPointer(ptr, src);834}835template <class X, class T>836Q_INLINE_TEMPLATEinline QSharedPointer<X> qSharedPointerConstCast(const QWeakPointer<T> &src)837{838 return qSharedPointerConstCast<X, T>(src.toStrongRef());839}840 841template <class X, class T>842Q_INLINE_TEMPLATEinline843QWeakPointer<X> qWeakPointerCast(const QSharedPointer<T> &src)844{845 return qSharedPointerCast<X, T>(src).toWeakRef();846}847 848#ifndef QT_NO_QOBJECT849template <class X, class T>850Q_INLINE_TEMPLATEinline QSharedPointer<X> qSharedPointerObjectCast(const QSharedPointer<T> &src)851{852 X *ptr = qobject_cast<X *>(src.data());853 return QtSharedPointer::copyAndSetPointer(ptr, src);854}855template <class X, class T>856Q_INLINE_TEMPLATEinline QSharedPointer<X> qSharedPointerObjectCast(const QWeakPointer<T> &src)857{858 return qSharedPointerObjectCast<X>(src.toStrongRef());859}860 861template <class X, class T>862inline QSharedPointer<typename QtSharedPointer::RemovePointer<X>::Type>863qobject_cast(const QSharedPointer<T> &src)864{865 return qSharedPointerObjectCast<typename QtSharedPointer::RemovePointer<X>::Type, T>(src);866}867template <class X, class T>868inline QSharedPointer<typename QtSharedPointer::RemovePointer<X>::Type>869qobject_cast(const QWeakPointer<T> &src)870{871 return qSharedPointerObjectCast<typename QtSharedPointer::RemovePointer<X>::Type, T>(src);872}873 874/// ### TODO - QTBUG-88102: make this use toStrongRef() (once support for875/// storing non-managed QObjects in QWeakPointer is removed)876template<typename T>877QWeakPointer<typename std::enable_if<QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value, T>::type>878qWeakPointerFromVariant(const QVariant &variant)879{880 return QWeakPointer<T>(qobject_cast<T*>(QtPrivate::EnableInternalData::internalData(QtSharedPointer::weakPointerFromVariant_internal(variant))));881}882template<typename T>883QSharedPointer<typename std::enable_if<QtPrivate::IsPointerToTypeDerivedFromQObject<T*>::Value, T>::type>884qSharedPointerFromVariant(const QVariant &variant)885{886 return qSharedPointerObjectCast<T>(QtSharedPointer::sharedPointerFromVariant_internal(variant));887}888 889// std::shared_ptr helpers890 891template <typename X, class T>892std::shared_ptr<X> qobject_pointer_cast(const std::shared_ptr<T> &src)893{894 using element_type = typename std::shared_ptr<X>::element_type;895 return std::shared_ptr<X>(src, qobject_cast<element_type *>(src.get()));896}897 898template <typename X, class T>899std::shared_ptr<X> qobject_pointer_cast(std::shared_ptr<T> &&src)900{901 using element_type = typename std::shared_ptr<X>::element_type;902 auto castResult = qobject_cast<element_type *>(src.get());903 if (castResult) {904 // C++2a's move aliasing constructor will leave src empty.905 // Before C++2a we don't really know if the compiler has support for it.906 // The move aliasing constructor is the resolution for LWG2996,907 // which does not impose a feature-testing macro. So: clear src.908 return std::shared_ptr<X>(std::exchange(src, nullptr), castResult);909 }910 return std::shared_ptr<X>();911}912 913template <typename X, class T>914std::shared_ptr<X> qSharedPointerObjectCast(const std::shared_ptr<T> &src)915{916 return qobject_pointer_cast<X>(src);917}918 919template <typename X, class T>920std::shared_ptr<X> qSharedPointerObjectCast(std::shared_ptr<T> &&src)921{922 return qobject_pointer_cast<X>(std::move(src));923}924 925#endif926 927template<typename T> Q_DECLARE_TYPEINFO_BODY(QWeakPointer<T>, Q_RELOCATABLE_TYPE)class QTypeInfo<QWeakPointer<T> > { public: enum {
isComplex = (((Q_RELOCATABLE_TYPE) & Q_PRIMITIVE_TYPE) ==
0) && !std::is_trivial_v<QWeakPointer<T> >
, isRelocatable = !isComplex || ((Q_RELOCATABLE_TYPE) & Q_RELOCATABLE_TYPE
) || qIsRelocatable<QWeakPointer<T> >, isPointer =
false, isIntegral = std::is_integral< QWeakPointer<T>
>::value, }; }
;928template<typename T> Q_DECLARE_TYPEINFO_BODY(QSharedPointer<T>, Q_RELOCATABLE_TYPE)class QTypeInfo<QSharedPointer<T> > { public: enum
{ isComplex = (((Q_RELOCATABLE_TYPE) & Q_PRIMITIVE_TYPE)
== 0) && !std::is_trivial_v<QSharedPointer<T>
>, isRelocatable = !isComplex || ((Q_RELOCATABLE_TYPE) &
Q_RELOCATABLE_TYPE) || qIsRelocatable<QSharedPointer<T
> >, isPointer = false, isIntegral = std::is_integral<
QSharedPointer<T> >::value, }; }
;929 930 931QT_END_NAMESPACE932 933#endif
/usr/include/x86_64-linux-gnu/qt6/QtCore/qsharedpointer_impl.h (2024)
Top Articles
Latest Posts
Article information

Author: Prof. An Powlowski

Last Updated:

Views: 5665

Rating: 4.3 / 5 (44 voted)

Reviews: 91% of readers found this page helpful

Author information

Name: Prof. An Powlowski

Birthday: 1992-09-29

Address: Apt. 994 8891 Orval Hill, Brittnyburgh, AZ 41023-0398

Phone: +26417467956738

Job: District Marketing Strategist

Hobby: Embroidery, Bodybuilding, Motor sports, Amateur radio, Wood carving, Whittling, Air sports

Introduction: My name is Prof. An Powlowski, I am a charming, helpful, attractive, good, graceful, thoughtful, vast person who loves writing and wants to share my knowledge and understanding with you.