Bug Summary

File:usr/include/x86_64-linux-gnu/qt6/QtCore/qsharedpointer_impl.h
Warning:line 130, column 50
Use of memory after it is freed

Annotated Source Code

Press '?' to see keyboard shortcuts

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-12-02-100256-3913-1 -Xclang -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /builds/wireshark/wireshark/sbout/2024-12-02-100256-3913-1 -x c++ /builds/wireshark/wireshark/ui/qt/rtp_player_dialog.cpp

/builds/wireshark/wireshark/ui/qt/rtp_player_dialog.cpp

1/* rtp_player_dialog.cpp
2 *
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <[email protected]>
5 * Copyright 1998 Gerald Combs
6 *
7 * SPDX-License-Identifier: GPL-2.0-or-later
8 */
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
20#include "rtp_analysis_dialog.h"
21
22#ifdef QT_MULTIMEDIA_LIB1
23
24#include <epan/dissectors/packet-rtp.h>
25#include <epan/to_str.h>
26
27#include <wsutil/report_message.h>
28#include <wsutil/utf8_entities.h>
29#include <wsutil/pint.h>
30
31#include <ui/qt/utils/color_utils.h>
32#include <ui/qt/widgets/qcustomplot.h>
33#include <ui/qt/utils/qt_ui_utils.h>
34#include "rtp_audio_stream.h"
35#include <ui/qt/utils/tango_colors.h>
36#include <widgets/rtp_audio_graph.h>
37#include "main_application.h"
38#include "ui/qt/widgets/wireshark_file_dialog.h"
39
40#include <QAudio>
41#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))
42#include <algorithm>
43#include <QAudioDevice>
44#include <QAudioSink>
45#include <QMediaDevices>
46#else
47#include <QAudioDeviceInfo>
48#endif
49#include <QFrame>
50#include <QMenu>
51#include <QVBoxLayout>
52#include <QTimer>
53
54#include <QAudioFormat>
55#include <QAudioOutput>
56#include <ui/qt/utils/rtp_audio_silence_generator.h>
57
58#endif // QT_MULTIMEDIA_LIB
59
60#include <QPushButton>
61#include <QToolButton>
62
63#include <ui/qt/utils/stock_icon.h>
64#include "main_application.h"
65
66// To do:
67// - Threaded decoding?
68
69// Current and former RTP player bugs. Many have attachments that can be usef for testing.
70// Bug 3368 - The timestamp line in a RTP or RTCP packet display's "Not Representable"
71// Bug 3952 - VoIP Call RTP Player: audio played is corrupted when RFC2833 packets are present
72// Bug 4960 - RTP Player: Audio and visual feedback get rapidly out of sync
73// Bug 5527 - Adding arbitrary value to x-axis RTP player
74// Bug 7935 - Wrong Timestamps in RTP Player-Decode
75// Bug 8007 - UI gets confused on playing decoded audio in rtp_player
76// Bug 9007 - Switching SSRC values in RTP stream
77// Bug 10613 - RTP audio player crashes
78// Bug 11125 - RTP Player does not show progress in selected stream in Window 7
79// Bug 11409 - Wireshark crashes when using RTP player
80// Bug 12166 - RTP audio player crashes
81
82// In some places we match by conv/call number, in others we match by first frame.
83
84enum {
85 channel_col_,
86 src_addr_col_,
87 src_port_col_,
88 dst_addr_col_,
89 dst_port_col_,
90 ssrc_col_,
91 first_pkt_col_,
92 num_pkts_col_,
93 time_span_col_,
94 sample_rate_col_,
95 play_rate_col_,
96 payload_col_,
97
98 stream_data_col_ = src_addr_col_, // RtpAudioStream
99 graph_audio_data_col_ = src_port_col_, // QCPGraph (wave)
100 graph_sequence_data_col_ = dst_addr_col_, // QCPGraph (sequence)
101 graph_jitter_data_col_ = dst_port_col_, // QCPGraph (jitter)
102 graph_timestamp_data_col_ = ssrc_col_, // QCPGraph (timestamp)
103 // first_pkt_col_ is skipped, it is used for real data
104 graph_silence_data_col_ = num_pkts_col_, // QCPGraph (silence)
105};
106
107class RtpPlayerTreeWidgetItem : public QTreeWidgetItem
108{
109public:
110 RtpPlayerTreeWidgetItem(QTreeWidget *tree) :
111 QTreeWidgetItem(tree)
112 {
113 }
114
115 bool operator< (const QTreeWidgetItem &other) const
116 {
117 // Handle numeric sorting
118 switch (treeWidget()->sortColumn()) {
119 case src_port_col_:
120 case dst_port_col_:
121 case num_pkts_col_:
122 case sample_rate_col_:
123 return text(treeWidget()->sortColumn()).toInt() < other.text(treeWidget()->sortColumn()).toInt();
124 case play_rate_col_:
125 return text(treeWidget()->sortColumn()).toInt() < other.text(treeWidget()->sortColumn()).toInt();
126 case first_pkt_col_:
127 int v1;
128 int v2;
129
130 v1 = data(first_pkt_col_, Qt::UserRole).toInt();
131 v2 = other.data(first_pkt_col_, Qt::UserRole).toInt();
132
133 return v1 < v2;
134 default:
135 // Fall back to string comparison
136 return QTreeWidgetItem::operator <(other);
137 }
138 }
139};
140
141RtpPlayerDialog *RtpPlayerDialog::pinstance_{nullptr};
142std::mutex RtpPlayerDialog::init_mutex_;
143std::mutex RtpPlayerDialog::run_mutex_;
144
145RtpPlayerDialog *RtpPlayerDialog::openRtpPlayerDialog(QWidget &parent, CaptureFile &cf, QObject *packet_list, bool capture_running)
146{
147 std::lock_guard<std::mutex> lock(init_mutex_);
148 if (pinstance_ == nullptr)
149 {
150 pinstance_ = new RtpPlayerDialog(parent, cf, capture_running);
151 connect(pinstance_, SIGNAL(goToPacket(int))qFlagLocation("2" "goToPacket(int)" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "151")
,
152 packet_list, SLOT(goToPacket(int))qFlagLocation("1" "goToPacket(int)" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "152")
);
153 }
154 return pinstance_;
155}
156
157RtpPlayerDialog::RtpPlayerDialog(QWidget &parent, CaptureFile &cf, bool capture_running _U___attribute__((unused))) :
158 WiresharkDialog(parent, cf)
159#ifdef QT_MULTIMEDIA_LIB1
160 , ui(new Ui::RtpPlayerDialog)
161 , first_stream_rel_start_time_(0.0)
162 , first_stream_abs_start_time_(0.0)
163 , first_stream_rel_stop_time_(0.0)
164 , streams_length_(0.0)
165 , start_marker_time_(0.0)
166 , number_ticker_(new QCPAxisTicker)
167 , datetime_ticker_(new QCPAxisTickerDateTime)
168 , stereo_available_(false)
169 , marker_stream_(0)
170 , marker_stream_requested_out_rate_(0)
171 , last_ti_(0)
172 , listener_removed_(true)
173 , block_redraw_(false)
174 , lock_ui_(0)
175 , read_capture_enabled_(capture_running)
176 , silence_skipped_time_(0.0)
177#endif // QT_MULTIMEDIA_LIB
178{
179 ui->setupUi(this);
180 loadGeometry(parent.width(), parent.height());
181 setWindowTitle(mainApp->windowTitleString(tr("RTP Player")));
182 ui->streamTreeWidget->installEventFilter(this);
183 ui->audioPlot->installEventFilter(this);
184 installEventFilter(this);
185
186#ifdef QT_MULTIMEDIA_LIB1
187 ui->splitter->setStretchFactor(0, 3);
188 ui->splitter->setStretchFactor(1, 1);
189
190 ui->streamTreeWidget->sortByColumn(first_pkt_col_, Qt::AscendingOrder);
191
192 graph_ctx_menu_ = new QMenu(this);
193
194 graph_ctx_menu_->addAction(ui->actionZoomIn);
195 graph_ctx_menu_->addAction(ui->actionZoomOut);
196 graph_ctx_menu_->addAction(ui->actionReset);
197 graph_ctx_menu_->addSeparator();
198 graph_ctx_menu_->addAction(ui->actionMoveRight10);
199 graph_ctx_menu_->addAction(ui->actionMoveLeft10);
200 graph_ctx_menu_->addAction(ui->actionMoveRight1);
201 graph_ctx_menu_->addAction(ui->actionMoveLeft1);
202 graph_ctx_menu_->addSeparator();
203 graph_ctx_menu_->addAction(ui->actionGoToPacket);
204 graph_ctx_menu_->addAction(ui->actionGoToSetupPacketPlot);
205 set_action_shortcuts_visible_in_context_menu(graph_ctx_menu_->actions());
206
207 ui->audioPlot->setContextMenuPolicy(Qt::CustomContextMenu);
208 connect(ui->audioPlot, &QCustomPlot::customContextMenuRequested, this, &RtpPlayerDialog::showGraphContextMenu);
209
210 ui->streamTreeWidget->setMouseTracking(true);
211 mouse_update_timer_ = new QTimer(this);
212 mouse_update_timer_->setSingleShot(true);
213 mouse_update_timer_->setInterval(10);
214 connect(mouse_update_timer_, &QTimer::timeout, this, &RtpPlayerDialog::mouseMoveUpdate);
215
216 connect(ui->streamTreeWidget, &QTreeWidget::itemEntered, this, &RtpPlayerDialog::itemEntered);
217
218 connect(ui->audioPlot, &QCustomPlot::mouseMove, this, &RtpPlayerDialog::mouseMovePlot);
219 connect(ui->audioPlot, &QCustomPlot::mousePress, this, &RtpPlayerDialog::graphClicked);
220 connect(ui->audioPlot, &QCustomPlot::mouseDoubleClick, this, &RtpPlayerDialog::graphDoubleClicked);
221 connect(ui->audioPlot, &QCustomPlot::plottableClick, this, &RtpPlayerDialog::plotClicked);
222
223 cur_play_pos_ = new QCPItemStraightLine(ui->audioPlot);
224 cur_play_pos_->setVisible(false);
225
226 start_marker_pos_ = new QCPItemStraightLine(ui->audioPlot);
227 start_marker_pos_->setPen(QPen(Qt::green,4));
228 setStartPlayMarker(0);
229 drawStartPlayMarker();
230 start_marker_pos_->setVisible(true);
231
232#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))
233 notify_timer_.setInterval(100); // ~15 fps
234 connect(&notify_timer_, &QTimer::timeout, this, &RtpPlayerDialog::outputNotify);
235#endif
236
237 datetime_ticker_->setDateTimeFormat("yyyy-MM-dd\nhh:mm:ss.zzz");
238
239 ui->audioPlot->xAxis->setNumberFormat("gb");
240 ui->audioPlot->xAxis->setNumberPrecision(3);
241 ui->audioPlot->xAxis->setTicker(datetime_ticker_);
242 ui->audioPlot->yAxis->setVisible(false);
243
244 ui->playButton->setIcon(StockIcon("media-playback-start"));
245 ui->playButton->setEnabled(false);
246 ui->pauseButton->setIcon(StockIcon("media-playback-pause"));
247 ui->pauseButton->setCheckable(true);
248 ui->pauseButton->setVisible(false);
249 ui->stopButton->setIcon(StockIcon("media-playback-stop"));
250 ui->stopButton->setEnabled(false);
251 ui->skipSilenceButton->setIcon(StockIcon("media-seek-forward"));
252 ui->skipSilenceButton->setCheckable(true);
253 ui->skipSilenceButton->setEnabled(false);
254
255 read_btn_ = ui->buttonBox->addButton(ui->actionReadCapture->text(), QDialogButtonBox::ActionRole);
256 read_btn_->setToolTip(ui->actionReadCapture->toolTip());
257 read_btn_->setEnabled(false);
258 connect(read_btn_, &QPushButton::pressed, this, &RtpPlayerDialog::on_actionReadCapture_triggered);
259
260 inaudible_btn_ = new QToolButton();
261 ui->buttonBox->addButton(inaudible_btn_, QDialogButtonBox::ActionRole);
262 inaudible_btn_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
263 inaudible_btn_->setPopupMode(QToolButton::MenuButtonPopup);
264
265 connect(ui->actionInaudibleButton, &QAction::triggered, this, &RtpPlayerDialog::on_actionSelectInaudible_triggered);
266 inaudible_btn_->setDefaultAction(ui->actionInaudibleButton);
267 // Overrides text striping of shortcut undercode in QAction
268 inaudible_btn_->setText(ui->actionInaudibleButton->text());
269 inaudible_btn_->setEnabled(false);
270 inaudible_btn_->setMenu(ui->menuInaudible);
271
272 analyze_btn_ = RtpAnalysisDialog::addAnalyzeButton(ui->buttonBox, this);
273
274 prepare_btn_ = ui->buttonBox->addButton(ui->actionPrepareFilter->text(), QDialogButtonBox::ActionRole);
275 prepare_btn_->setToolTip(ui->actionPrepareFilter->toolTip());
276 connect(prepare_btn_, &QPushButton::pressed, this, &RtpPlayerDialog::on_actionPrepareFilter_triggered);
277
278 export_btn_ = ui->buttonBox->addButton(ui->actionExportButton->text(), QDialogButtonBox::ActionRole);
279 export_btn_->setToolTip(ui->actionExportButton->toolTip());
280 export_btn_->setEnabled(false);
281 export_btn_->setMenu(ui->menuExport);
282
283 // Ordered, unique device names starting with the system default
284 QMap<QString, bool> out_device_map; // true == default device
285#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))
286 out_device_map.insert(QMediaDevices::defaultAudioOutput().description(), true);
287 foreach (QAudioDevice out_device, QMediaDevices::audioOutputs())for (auto _container_287 = QtPrivate::qMakeForeachContainer(QMediaDevices
::audioOutputs()); _container_287.i != _container_287.e; ++_container_287
.i) if (QAudioDevice out_device = *_container_287.i; false) {
} else
{
288 if (!out_device_map.contains(out_device.description())) {
289 out_device_map.insert(out_device.description(), false);
290 }
291 }
292#else
293 out_device_map.insert(QAudioDeviceInfo::defaultOutputDevice().deviceName(), true);
294 foreach (QAudioDeviceInfo out_device, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))for (auto _container_294 = QtPrivate::qMakeForeachContainer(QAudioDeviceInfo
::availableDevices(QAudio::AudioOutput)); _container_294.i !=
_container_294.e; ++_container_294.i) if (QAudioDeviceInfo out_device
= *_container_294.i; false) {} else
{
295 if (!out_device_map.contains(out_device.deviceName())) {
296 out_device_map.insert(out_device.deviceName(), false);
297 }
298 }
299#endif
300
301 ui->outputDeviceComboBox->blockSignals(true);
302 foreach (QString out_name, out_device_map.keys())for (auto _container_302 = QtPrivate::qMakeForeachContainer(out_device_map
.keys()); _container_302.i != _container_302.e; ++_container_302
.i) if (QString out_name = *_container_302.i; false) {} else
{
303 ui->outputDeviceComboBox->addItem(out_name);
304 if (out_device_map.value(out_name)) {
305 ui->outputDeviceComboBox->setCurrentIndex(ui->outputDeviceComboBox->count() - 1);
306 }
307 }
308 if (ui->outputDeviceComboBox->count() < 1) {
309 ui->outputDeviceComboBox->setEnabled(false);
310 ui->playButton->setEnabled(false);
311 ui->pauseButton->setEnabled(false);
312 ui->stopButton->setEnabled(false);
313 ui->skipSilenceButton->setEnabled(false);
314 ui->minSilenceSpinBox->setEnabled(false);
315 ui->outputDeviceComboBox->addItem(tr("No devices available"));
316 ui->outputAudioRate->setEnabled(false);
317 } else {
318 stereo_available_ = isStereoAvailable();
319 fillAudioRateMenu();
320 }
321 ui->outputDeviceComboBox->blockSignals(false);
322
323 ui->audioPlot->setMouseTracking(true);
324 ui->audioPlot->setEnabled(true);
325 ui->audioPlot->setInteractions(
326 QCP::iRangeDrag |
327 QCP::iRangeZoom
328 );
329
330 graph_ctx_menu_->addSeparator();
331 list_ctx_menu_ = new QMenu(this);
332 list_ctx_menu_->addAction(ui->actionPlay);
333 graph_ctx_menu_->addAction(ui->actionPlay);
334 list_ctx_menu_->addAction(ui->actionStop);
335 graph_ctx_menu_->addAction(ui->actionStop);
336 list_ctx_menu_->addMenu(ui->menuSelect);
337 graph_ctx_menu_->addMenu(ui->menuSelect);
338 list_ctx_menu_->addMenu(ui->menuAudioRouting);
339 graph_ctx_menu_->addMenu(ui->menuAudioRouting);
340 list_ctx_menu_->addAction(ui->actionRemoveStream);
341 graph_ctx_menu_->addAction(ui->actionRemoveStream);
342 list_ctx_menu_->addAction(ui->actionGoToSetupPacketTree);
343 set_action_shortcuts_visible_in_context_menu(list_ctx_menu_->actions());
344
345 connect(&cap_file_, &CaptureFile::captureEvent, this, &RtpPlayerDialog::captureEvent);
346 connect(this, SIGNAL(updateFilter(QString, bool))qFlagLocation("2" "updateFilter(QString, bool)" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "346")
,
347 &parent, SLOT(filterPackets(QString, bool))qFlagLocation("1" "filterPackets(QString, bool)" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "347")
);
348 connect(this, SIGNAL(rtpAnalysisDialogReplaceRtpStreams(QVector<rtpstream_id_t *>))qFlagLocation("2" "rtpAnalysisDialogReplaceRtpStreams(QVector<rtpstream_id_t *>)"
"\0" "ui/qt/rtp_player_dialog.cpp" ":" "348")
,
349 &parent, SLOT(rtpAnalysisDialogReplaceRtpStreams(QVector<rtpstream_id_t *>))qFlagLocation("1" "rtpAnalysisDialogReplaceRtpStreams(QVector<rtpstream_id_t *>)"
"\0" "ui/qt/rtp_player_dialog.cpp" ":" "349")
);
350 connect(this, SIGNAL(rtpAnalysisDialogAddRtpStreams(QVector<rtpstream_id_t *>))qFlagLocation("2" "rtpAnalysisDialogAddRtpStreams(QVector<rtpstream_id_t *>)"
"\0" "ui/qt/rtp_player_dialog.cpp" ":" "350")
,
351 &parent, SLOT(rtpAnalysisDialogAddRtpStreams(QVector<rtpstream_id_t *>))qFlagLocation("1" "rtpAnalysisDialogAddRtpStreams(QVector<rtpstream_id_t *>)"
"\0" "ui/qt/rtp_player_dialog.cpp" ":" "351")
);
352 connect(this, SIGNAL(rtpAnalysisDialogRemoveRtpStreams(QVector<rtpstream_id_t *>))qFlagLocation("2" "rtpAnalysisDialogRemoveRtpStreams(QVector<rtpstream_id_t *>)"
"\0" "ui/qt/rtp_player_dialog.cpp" ":" "352")
,
353 &parent, SLOT(rtpAnalysisDialogRemoveRtpStreams(QVector<rtpstream_id_t *>))qFlagLocation("1" "rtpAnalysisDialogRemoveRtpStreams(QVector<rtpstream_id_t *>)"
"\0" "ui/qt/rtp_player_dialog.cpp" ":" "353")
);
354#endif // QT_MULTIMEDIA_LIB
355}
356
357// _U_ is used when no QT_MULTIMEDIA_LIB is available
358QToolButton *RtpPlayerDialog::addPlayerButton(QDialogButtonBox *button_box, QDialog *dialog _U___attribute__((unused)))
359{
360 if (!button_box) return NULL__null;
361
362 QAction *ca;
363 QToolButton *player_button = new QToolButton();
364 button_box->addButton(player_button, QDialogButtonBox::ActionRole);
365 player_button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
366 player_button->setPopupMode(QToolButton::MenuButtonPopup);
367
368 ca = new QAction(tr("&Play Streams"), player_button);
369 ca->setToolTip(tr("Open RTP player dialog"));
370 ca->setIcon(StockIcon("media-playback-start"));
371 connect(ca, SIGNAL(triggered())qFlagLocation("2" "triggered()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "371")
, dialog, SLOT(rtpPlayerReplace())qFlagLocation("1" "rtpPlayerReplace()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "371")
);
372 player_button->setDefaultAction(ca);
373 // Overrides text striping of shortcut undercode in QAction
374 player_button->setText(ca->text());
375
376#if defined(QT_MULTIMEDIA_LIB1)
377 QMenu *button_menu = new QMenu(player_button);
378 button_menu->setToolTipsVisible(true);
379 ca = button_menu->addAction(tr("&Set playlist"));
380 ca->setToolTip(tr("Replace existing playlist in RTP Player with new one"));
381 connect(ca, SIGNAL(triggered())qFlagLocation("2" "triggered()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "381")
, dialog, SLOT(rtpPlayerReplace())qFlagLocation("1" "rtpPlayerReplace()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "381")
);
382 ca = button_menu->addAction(tr("&Add to playlist"));
383 ca->setToolTip(tr("Add new set to existing playlist in RTP Player"));
384 connect(ca, SIGNAL(triggered())qFlagLocation("2" "triggered()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "384")
, dialog, SLOT(rtpPlayerAdd())qFlagLocation("1" "rtpPlayerAdd()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "384")
);
385 ca = button_menu->addAction(tr("&Remove from playlist"));
386 ca->setToolTip(tr("Remove selected streams from playlist in RTP Player"));
387 connect(ca, SIGNAL(triggered())qFlagLocation("2" "triggered()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "387")
, dialog, SLOT(rtpPlayerRemove())qFlagLocation("1" "rtpPlayerRemove()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "387")
);
388 player_button->setMenu(button_menu);
389#else
390 player_button->setEnabled(false);
391 player_button->setText(tr("No Audio"));
392#endif
393
394 return player_button;
395}
396
397#ifdef QT_MULTIMEDIA_LIB1
398RtpPlayerDialog::~RtpPlayerDialog()
399{
400 std::lock_guard<std::mutex> lock(init_mutex_);
401 if (pinstance_ != nullptr) {
402 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
403 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
404 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
405 if (audio_stream)
406 delete audio_stream;
407 }
408 cleanupMarkerStream();
409 delete ui;
410 pinstance_ = nullptr;
411 }
412}
413
414void RtpPlayerDialog::accept()
415{
416 if (!listener_removed_) {
417 remove_tap_listener(this);
418 listener_removed_ = true;
419 }
420
421 int row_count = ui->streamTreeWidget->topLevelItemCount();
422 // Stop all streams before the dialogs are closed.
423 for (int row = 0; row < row_count; row++) {
424 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
425 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
426 audio_stream->stopPlaying();
427 }
428 WiresharkDialog::accept();
429}
430
431void RtpPlayerDialog::reject()
432{
433 RtpPlayerDialog::accept();
434}
435
436void RtpPlayerDialog::retapPackets()
437{
438 if (!listener_removed_) {
1
Assuming field 'listener_removed_' is true
2
Taking false branch
439 // Retap is running, nothing better we can do
440 return;
441 }
442 lockUI();
443 ui->hintLabel->setText("<i><small>" + tr("Decoding streams...") + "</i></small>");
444 mainApp->processEvents();
445
446 // Clear packets from existing streams before retap
447 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
3
Assuming the condition is false
4
Loop condition is false. Execution continues on line 457
448 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
449 RtpAudioStream *row_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
450
451 row_stream->clearPackets();
452 }
453
454 // destroyCheck is protection against destroying dialog during recap.
455 // It stores dialog pointer in data() and if dialog destroyed, it
456 // returns null
457 QPointer<RtpPlayerDialog> destroyCheck=this;
458 GString *error_string;
459
460 listener_removed_ = false;
461 error_string = register_tap_listener("rtp", this, NULL__null, 0, NULL__null, tapPacket, NULL__null, NULL__null);
462 if (error_string) {
5
Assuming 'error_string' is non-null
6
Taking true branch
463 report_failure("RTP Player - tap registration failed: %s", error_string->str);
464 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
465 unlockUI();
466 return;
8
Calling implicit destructor for 'QPointer<RtpPlayerDialog>'
9
Calling '~QWeakPointer'
467 }
468 cap_file_.retapPackets();
469
470 // Check if dialog exists still
471 if (destroyCheck.data()) {
472 if (!listener_removed_) {
473 remove_tap_listener(this);
474 listener_removed_ = true;
475 }
476 fillTappedColumns();
477 rescanPackets(true);
478 }
479 unlockUI();
480}
481
482void RtpPlayerDialog::rescanPackets(bool rescale_axes)
483{
484 lockUI();
485 // Show information for a user - it can last long time...
486 playback_error_.clear();
487 ui->hintLabel->setText("<i><small>" + tr("Decoding streams...") + "</i></small>");
488 mainApp->processEvents();
489
490#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))
491 QAudioDevice cur_out_device = getCurrentDeviceInfo();
492#else
493 QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo();
494#endif
495 int row_count = ui->streamTreeWidget->topLevelItemCount();
496
497 // Reset stream values
498 for (int row = 0; row < row_count; row++) {
499 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
500 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
501 audio_stream->setStereoRequired(stereo_available_);
502 audio_stream->reset(first_stream_rel_start_time_);
503
504 audio_stream->setJitterBufferSize((int) ui->jitterSpinBox->value());
505
506 RtpAudioStream::TimingMode timing_mode = RtpAudioStream::JitterBuffer;
507 switch (ui->timingComboBox->currentIndex()) {
508 case RtpAudioStream::RtpTimestamp:
509 timing_mode = RtpAudioStream::RtpTimestamp;
510 break;
511 case RtpAudioStream::Uninterrupted:
512 timing_mode = RtpAudioStream::Uninterrupted;
513 break;
514 default:
515 break;
516 }
517 audio_stream->setTimingMode(timing_mode);
518
519 //if (!cur_out_device.isNull()) {
520 audio_stream->decode(cur_out_device);
521 //}
522 }
523
524 for (int col = 0; col < ui->streamTreeWidget->columnCount() - 1; col++) {
525 ui->streamTreeWidget->resizeColumnToContents(col);
526 }
527
528 createPlot(rescale_axes);
529
530 updateWidgets();
531 unlockUI();
532}
533
534void RtpPlayerDialog::createPlot(bool rescale_axes)
535{
536 bool legend_out_of_sequence = false;
537 bool legend_jitter_dropped = false;
538 bool legend_wrong_timestamps = false;
539 bool legend_inserted_silences = false;
540 bool relative_timestamps = !ui->todCheckBox->isChecked();
541 int row_count = ui->streamTreeWidget->topLevelItemCount();
542 int16_t total_max_sample_value = 1;
543
544 ui->audioPlot->clearGraphs();
545
546 if (relative_timestamps) {
547 ui->audioPlot->xAxis->setTicker(number_ticker_);
548 } else {
549 ui->audioPlot->xAxis->setTicker(datetime_ticker_);
550 }
551
552 // Calculate common Y scale for graphs
553 for (int row = 0; row < row_count; row++) {
554 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
555 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
556 int16_t max_sample_value = audio_stream->getMaxSampleValue();
557
558 if (max_sample_value > total_max_sample_value) {
559 total_max_sample_value = max_sample_value;
560 }
561 }
562
563 // Clear existing graphs
564 for (int row = 0; row < row_count; row++) {
565 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
566 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
567 int y_offset = row_count - row - 1;
568 AudioRouting audio_routing = audio_stream->getAudioRouting();
569
570 ti->setData(graph_audio_data_col_, Qt::UserRole, QVariant());
571 ti->setData(graph_sequence_data_col_, Qt::UserRole, QVariant());
572 ti->setData(graph_jitter_data_col_, Qt::UserRole, QVariant());
573 ti->setData(graph_timestamp_data_col_, Qt::UserRole, QVariant());
574 ti->setData(graph_silence_data_col_, Qt::UserRole, QVariant());
575
576 // Set common scale
577 audio_stream->setMaxSampleValue(total_max_sample_value);
578
579 // Waveform
580 RtpAudioGraph *audio_graph = new RtpAudioGraph(ui->audioPlot, audio_stream->color());
581 audio_graph->setMuted(audio_routing.isMuted());
582 audio_graph->setData(audio_stream->visualTimestamps(relative_timestamps), audio_stream->visualSamples(y_offset));
583 ti->setData(graph_audio_data_col_, Qt::UserRole, QVariant::fromValue<RtpAudioGraph *>(audio_graph));
584 //RTP_STREAM_DEBUG("Plotting %s, %d samples", ti->text(src_addr_col_).toUtf8().constData(), audio_graph->wave->data()->size());
585
586 QString span_str;
587 if (ui->todCheckBox->isChecked()) {
588 QDateTime date_time1 = QDateTime::fromMSecsSinceEpoch((audio_stream->startRelTime() + first_stream_abs_start_time_ - audio_stream->startRelTime()) * 1000.0);
589 QDateTime date_time2 = QDateTime::fromMSecsSinceEpoch((audio_stream->stopRelTime() + first_stream_abs_start_time_ - audio_stream->startRelTime()) * 1000.0);
590 QString time_str1 = date_time1.toString("yyyy-MM-dd hh:mm:ss.zzz");
591 QString time_str2 = date_time2.toString("yyyy-MM-dd hh:mm:ss.zzz");
592 span_str = QStringLiteral("%1 - %2 (%3)")(QString(QtPrivate::qMakeStringPrivate(u"" "%1 - %2 (%3)")))
593 .arg(time_str1)
594 .arg(time_str2)
595 .arg(QString::number(audio_stream->stopRelTime() - audio_stream->startRelTime(), 'f', prefs.gui_decimal_places1));
596 } else {
597 span_str = QStringLiteral("%1 - %2 (%3)")(QString(QtPrivate::qMakeStringPrivate(u"" "%1 - %2 (%3)")))
598 .arg(QString::number(audio_stream->startRelTime(), 'f', prefs.gui_decimal_places1))
599 .arg(QString::number(audio_stream->stopRelTime(), 'f', prefs.gui_decimal_places1))
600 .arg(QString::number(audio_stream->stopRelTime() - audio_stream->startRelTime(), 'f', prefs.gui_decimal_places1));
601 }
602 ti->setText(time_span_col_, span_str);
603 ti->setText(sample_rate_col_, QString::number(audio_stream->sampleRate()));
604 ti->setText(play_rate_col_, QString::number(audio_stream->playRate()));
605 ti->setText(payload_col_, audio_stream->payloadNames().join(", "));
606
607 if (audio_stream->outOfSequence() > 0) {
608 // Sequence numbers
609 QCPGraph *seq_graph = ui->audioPlot->addGraph();
610 seq_graph->setLineStyle(QCPGraph::lsNone);
611 seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssSquare, tango_aluminium_6, Qt::white, mainApp->font().pointSize())); // Arbitrary
612 seq_graph->setSelectable(QCP::stNone);
613 seq_graph->setData(audio_stream->outOfSequenceTimestamps(relative_timestamps), audio_stream->outOfSequenceSamples(y_offset));
614 ti->setData(graph_sequence_data_col_, Qt::UserRole, QVariant::fromValue<QCPGraph *>(seq_graph));
615 if (legend_out_of_sequence) {
616 seq_graph->removeFromLegend();
617 } else {
618 seq_graph->setName(tr("Out of Sequence"));
619 legend_out_of_sequence = true;
620 }
621 }
622
623 if (audio_stream->jitterDropped() > 0) {
624 // Jitter drops
625 QCPGraph *seq_graph = ui->audioPlot->addGraph();
626 seq_graph->setLineStyle(QCPGraph::lsNone);
627 seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCircle, tango_scarlet_red_5, Qt::white, mainApp->font().pointSize())); // Arbitrary
628 seq_graph->setSelectable(QCP::stNone);
629 seq_graph->setData(audio_stream->jitterDroppedTimestamps(relative_timestamps), audio_stream->jitterDroppedSamples(y_offset));
630 ti->setData(graph_jitter_data_col_, Qt::UserRole, QVariant::fromValue<QCPGraph *>(seq_graph));
631 if (legend_jitter_dropped) {
632 seq_graph->removeFromLegend();
633 } else {
634 seq_graph->setName(tr("Jitter Drops"));
635 legend_jitter_dropped = true;
636 }
637 }
638
639 if (audio_stream->wrongTimestamps() > 0) {
640 // Wrong timestamps
641 QCPGraph *seq_graph = ui->audioPlot->addGraph();
642 seq_graph->setLineStyle(QCPGraph::lsNone);
643 seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDiamond, tango_sky_blue_5, Qt::white, mainApp->font().pointSize())); // Arbitrary
644 seq_graph->setSelectable(QCP::stNone);
645 seq_graph->setData(audio_stream->wrongTimestampTimestamps(relative_timestamps), audio_stream->wrongTimestampSamples(y_offset));
646 ti->setData(graph_timestamp_data_col_, Qt::UserRole, QVariant::fromValue<QCPGraph *>(seq_graph));
647 if (legend_wrong_timestamps) {
648 seq_graph->removeFromLegend();
649 } else {
650 seq_graph->setName(tr("Wrong Timestamps"));
651 legend_wrong_timestamps = true;
652 }
653 }
654
655 if (audio_stream->insertedSilences() > 0) {
656 // Inserted silence
657 QCPGraph *seq_graph = ui->audioPlot->addGraph();
658 seq_graph->setLineStyle(QCPGraph::lsNone);
659 seq_graph->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssTriangle, tango_butter_5, Qt::white, mainApp->font().pointSize())); // Arbitrary
660 seq_graph->setSelectable(QCP::stNone);
661 seq_graph->setData(audio_stream->insertedSilenceTimestamps(relative_timestamps), audio_stream->insertedSilenceSamples(y_offset));
662 ti->setData(graph_silence_data_col_, Qt::UserRole, QVariant::fromValue<QCPGraph *>(seq_graph));
663 if (legend_inserted_silences) {
664 seq_graph->removeFromLegend();
665 } else {
666 seq_graph->setName(tr("Inserted Silence"));
667 legend_inserted_silences = true;
668 }
669 }
670 }
671 ui->audioPlot->legend->setVisible(legend_out_of_sequence || legend_jitter_dropped || legend_wrong_timestamps || legend_inserted_silences);
672
673 ui->audioPlot->replot();
674 if (rescale_axes) resetXAxis();
675}
676
677void RtpPlayerDialog::fillTappedColumns()
678{
679 // true just for first stream
680 bool is_first = true;
681
682 // Get all rows, immutable list. Later changes in rows might reorder them
683 QList<QTreeWidgetItem *> items = ui->streamTreeWidget->findItems(
684 QStringLiteral("*")(QString(QtPrivate::qMakeStringPrivate(u"" "*"))), Qt::MatchWrap | Qt::MatchWildcard | Qt::MatchRecursive);
685
686 // Update rows by calculated values, it might reorder them in view...
687 foreach(QTreeWidgetItem *ti, items)for (auto _container_687 = QtPrivate::qMakeForeachContainer(items
); _container_687.i != _container_687.e; ++_container_687.i) if
(QTreeWidgetItem *ti = *_container_687.i; false) {} else
{
688 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
689 if (audio_stream) {
690 rtpstream_info_t *rtpstream = audio_stream->getStreamInfo();
691
692 // 0xFFFFFFFF mean no setup frame
693 // first_packet_num == setup_frame_number happens, when
694 // rtp_udp is active or Decode as was used
695 if ((rtpstream->setup_frame_number == 0xFFFFFFFF) ||
696 (rtpstream->rtp_stats.first_packet_num == rtpstream->setup_frame_number)
697 ) {
698 int packet = rtpstream->rtp_stats.first_packet_num;
699 ti->setText(first_pkt_col_, QStringLiteral("RTP %1")(QString(QtPrivate::qMakeStringPrivate(u"" "RTP %1"))).arg(packet));
700 ti->setData(first_pkt_col_, Qt::UserRole, QVariant(packet));
701 } else {
702 int packet = rtpstream->setup_frame_number;
703 ti->setText(first_pkt_col_, QStringLiteral("SETUP %1")(QString(QtPrivate::qMakeStringPrivate(u"" "SETUP %1"))).arg(rtpstream->setup_frame_number));
704 ti->setData(first_pkt_col_, Qt::UserRole, QVariant(packet));
705 }
706 ti->setText(num_pkts_col_, QString::number(rtpstream->packet_count));
707 updateStartStopTime(rtpstream, is_first);
708 is_first = false;
709 }
710 }
711 setMarkers();
712}
713
714void RtpPlayerDialog::addSingleRtpStream(rtpstream_id_t *id)
715{
716 bool found = false;
717
718 AudioRouting audio_routing = AudioRouting(AUDIO_UNMUTEDfalse, channel_mono);
719
720 if (!id) return;
721
722 // Find the RTP streams associated with this conversation.
723 // gtk/rtp_player.c:mark_rtp_stream_to_play does this differently.
724
725 QList<RtpAudioStream *> streams = stream_hash_.values(rtpstream_id_to_hash(id));
726 for (int i = 0; i < streams.size(); i++) {
727 RtpAudioStream *row_stream = streams.at(i);
728 if (row_stream->isMatch(id)) {
729 found = true;
730 break;
731 }
732 }
733
734
735 if (found) {
736 return;
737 }
738
739 try {
740 int tli_count = ui->streamTreeWidget->topLevelItemCount();
741
742 RtpAudioStream *audio_stream = new RtpAudioStream(this, id, stereo_available_);
743 audio_stream->setColor(ColorUtils::graphColor(tli_count));
744
745 QTreeWidgetItem *ti = new RtpPlayerTreeWidgetItem(ui->streamTreeWidget);
746 stream_hash_.insert(rtpstream_id_to_hash(id), audio_stream);
747 ti->setText(src_addr_col_, address_to_qstring(&(id->src_addr)));
748 ti->setText(src_port_col_, QString::number(id->src_port));
749 ti->setText(dst_addr_col_, address_to_qstring(&(id->dst_addr)));
750 ti->setText(dst_port_col_, QString::number(id->dst_port));
751 ti->setText(ssrc_col_, int_to_qstring(id->ssrc, 8, 16));
752
753 // Calculated items are updated after every retapPackets()
754
755 ti->setData(stream_data_col_, Qt::UserRole, QVariant::fromValue(audio_stream));
756 if (stereo_available_) {
757 if (tli_count%2) {
758 audio_routing.setChannel(channel_stereo_right);
759 } else {
760 audio_routing.setChannel(channel_stereo_left);
761 }
762 } else {
763 audio_routing.setChannel(channel_mono);
764 }
765 ti->setToolTip(channel_col_, tr("Double click on cell to change audio routing"));
766 formatAudioRouting(ti, audio_routing);
767 audio_stream->setAudioRouting(audio_routing);
768
769 for (int col = 0; col < ui->streamTreeWidget->columnCount(); col++) {
770 QBrush fgBrush = ti->foreground(col);
771 fgBrush.setColor(audio_stream->color());
772 fgBrush.setStyle(Qt::SolidPattern);
773 ti->setForeground(col, fgBrush);
774 }
775
776 connect(audio_stream, &RtpAudioStream::finishedPlaying, this, &RtpPlayerDialog::playFinished);
777 connect(audio_stream, &RtpAudioStream::playbackError, this, &RtpPlayerDialog::setPlaybackError);
778 } catch (...) {
779 qWarningQMessageLogger(static_cast<const char *>("ui/qt/rtp_player_dialog.cpp"
), 779, static_cast<const char *>(__PRETTY_FUNCTION__))
.warning
() << "Stream ignored, try to add fewer streams to playlist";
780 }
781
782 RTP_STREAM_DEBUG("adding stream %d to layout",
783 ui->streamTreeWidget->topLevelItemCount());
784}
785
786void RtpPlayerDialog::lockUI()
787{
788 if (0 == lock_ui_++) {
789 if (playing_streams_.count() > 0) {
790 on_stopButton_clicked();
791 }
792 setEnabled(false);
793 }
794}
795
796void RtpPlayerDialog::unlockUI()
797{
798 if (--lock_ui_ == 0) {
799 setEnabled(true);
800 }
801}
802
803void RtpPlayerDialog::replaceRtpStreams(QVector<rtpstream_id_t *> stream_ids)
804{
805 std::unique_lock<std::mutex> lock(run_mutex_, std::try_to_lock);
806 if (lock.owns_lock()) {
807 lockUI();
808
809 // Delete all existing rows
810 if (last_ti_) {
811 highlightItem(last_ti_, false);
812 last_ti_ = NULL__null;
813 }
814
815 for (int row = ui->streamTreeWidget->topLevelItemCount() - 1; row >= 0; row--) {
816 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
817 removeRow(ti);
818 }
819
820 // Add all new streams
821 for (int i=0; i < stream_ids.size(); i++) {
822 addSingleRtpStream(stream_ids[i]);
823 }
824 setMarkers();
825
826 unlockUI();
827#ifdef QT_MULTIMEDIA_LIB1
828 QTimer::singleShot(0, this, SLOT(retapPackets())qFlagLocation("1" "retapPackets()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "828")
);
829#endif
830 } else {
831 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"
, 831, __func__, "replaceRtpStreams was called while other thread locked it. Current call is ignored, try it later."
); } } while (0)
;
832 }
833}
834
835void RtpPlayerDialog::addRtpStreams(QVector<rtpstream_id_t *> stream_ids)
836{
837 std::unique_lock<std::mutex> lock(run_mutex_, std::try_to_lock);
838 if (lock.owns_lock()) {
839 lockUI();
840
841 int tli_count = ui->streamTreeWidget->topLevelItemCount();
842
843 // Add new streams
844 for (int i=0; i < stream_ids.size(); i++) {
845 addSingleRtpStream(stream_ids[i]);
846 }
847
848 if (tli_count == 0) {
849 setMarkers();
850 }
851
852 unlockUI();
853#ifdef QT_MULTIMEDIA_LIB1
854 QTimer::singleShot(0, this, SLOT(retapPackets())qFlagLocation("1" "retapPackets()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "854")
);
855#endif
856 } else {
857 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"
, 857, __func__, "addRtpStreams was called while other thread locked it. Current call is ignored, try it later."
); } } while (0)
;
858 }
859}
860
861void RtpPlayerDialog::removeRtpStreams(QVector<rtpstream_id_t *> stream_ids)
862{
863 std::unique_lock<std::mutex> lock(run_mutex_, std::try_to_lock);
864 if (lock.owns_lock()) {
865 lockUI();
866 int tli_count = ui->streamTreeWidget->topLevelItemCount();
867
868 for (int i=0; i < stream_ids.size(); i++) {
869 for (int row = 0; row < tli_count; row++) {
870 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
871 RtpAudioStream *row_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
872 if (row_stream->isMatch(stream_ids[i])) {
873 removeRow(ti);
874 tli_count--;
875 break;
876 }
877 }
878 }
879 updateGraphs();
880
881 updateWidgets();
882 unlockUI();
883 } else {
884 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"
, 884, __func__, "removeRtpStreams was called while other thread locked it. Current call is ignored, try it later."
); } } while (0)
;
885 }
886}
887
888void RtpPlayerDialog::setMarkers()
889{
890 setStartPlayMarker(0);
891 drawStartPlayMarker();
892}
893
894void RtpPlayerDialog::showEvent(QShowEvent *)
895{
896 // We could use loadSplitterState(ui->splitter) instead of always
897 // resetting the plot size to 75%
898 QList<int> split_sizes = ui->splitter->sizes();
899 int tot_size = split_sizes[0] + split_sizes[1];
900 int plot_size = tot_size * 3 / 4;
901 split_sizes.clear();
902 split_sizes << plot_size << tot_size - plot_size;
903 ui->splitter->setSizes(split_sizes);
904}
905
906bool RtpPlayerDialog::eventFilter(QObject *, QEvent *event)
907{
908 if (event->type() == QEvent::KeyPress) {
909 QKeyEvent &keyEvent = static_cast<QKeyEvent&>(*event);
910 int pan_secs = keyEvent.modifiers() & Qt::ShiftModifier ? 1 : 10;
911
912 switch(keyEvent.key()) {
913 case Qt::Key_Minus:
914 case Qt::Key_Underscore: // Shifted minus on U.S. keyboards
915 case Qt::Key_O: // GTK+
916 case Qt::Key_R:
917 on_actionZoomOut_triggered();
918 return true;
919 case Qt::Key_Plus:
920 case Qt::Key_Equal: // Unshifted plus on U.S. keyboards
921 case Qt::Key_I: // GTK+
922 if (keyEvent.modifiers() == Qt::ControlModifier) {
923 // Ctrl+I
924 on_actionSelectInvert_triggered();
925 return true;
926 } else {
927 // I
928 on_actionZoomIn_triggered();
929 return true;
930 }
931 break;
932 case Qt::Key_Right:
933 case Qt::Key_L:
934 panXAxis(pan_secs);
935 return true;
936 case Qt::Key_Left:
937 case Qt::Key_H:
938 panXAxis(-1 * pan_secs);
939 return true;
940 case Qt::Key_0:
941 case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards
942 on_actionReset_triggered();
943 return true;
944 case Qt::Key_G:
945 if (keyEvent.modifiers() == Qt::ShiftModifier) {
946 // Goto SETUP frame, use correct call based on caller
947 QPoint pos1 = ui->audioPlot->mapFromGlobal(QCursor::pos());
948 QPoint pos2 = ui->streamTreeWidget->mapFromGlobal(QCursor::pos());
949 if (ui->audioPlot->rect().contains(pos1)) {
950 // audio plot, by mouse coords
951 on_actionGoToSetupPacketPlot_triggered();
952 } else if (ui->streamTreeWidget->rect().contains(pos2)) {
953 // packet tree, by cursor
954 on_actionGoToSetupPacketTree_triggered();
955 }
956 return true;
957 } else {
958 on_actionGoToPacket_triggered();
959 return true;
960 }
961 case Qt::Key_A:
962 if (keyEvent.modifiers() == Qt::ControlModifier) {
963 // Ctrl+A
964 on_actionSelectAll_triggered();
965 return true;
966 } else if (keyEvent.modifiers() == (Qt::ShiftModifier | Qt::ControlModifier)) {
967 // Ctrl+Shift+A
968 on_actionSelectNone_triggered();
969 return true;
970 }
971 break;
972 case Qt::Key_M:
973 if (keyEvent.modifiers() == Qt::ShiftModifier) {
974 on_actionAudioRoutingUnmute_triggered();
975 return true;
976 } else if (keyEvent.modifiers() == Qt::ControlModifier) {
977 on_actionAudioRoutingMuteInvert_triggered();
978 return true;
979 } else {
980 on_actionAudioRoutingMute_triggered();
981 return true;
982 }
983 case Qt::Key_Delete:
984 on_actionRemoveStream_triggered();
985 return true;
986 case Qt::Key_X:
987 if (keyEvent.modifiers() == Qt::ControlModifier) {
988 // Ctrl+X
989 on_actionRemoveStream_triggered();
990 return true;
991 }
992 break;
993 case Qt::Key_Down:
994 case Qt::Key_Up:
995 case Qt::Key_PageUp:
996 case Qt::Key_PageDown:
997 case Qt::Key_Home:
998 case Qt::Key_End:
999 // Route keys to QTreeWidget
1000 ui->streamTreeWidget->setFocus();
1001 break;
1002 case Qt::Key_P:
1003 if (keyEvent.modifiers() == Qt::NoModifier) {
1004 on_actionPlay_triggered();
1005 return true;
1006 }
1007 break;
1008 case Qt::Key_S:
1009 on_actionStop_triggered();
1010 return true;
1011 case Qt::Key_N:
1012 if (keyEvent.modifiers() == Qt::ShiftModifier) {
1013 // Shift+N
1014 on_actionDeselectInaudible_triggered();
1015 return true;
1016 } else {
1017 on_actionSelectInaudible_triggered();
1018 return true;
1019 }
1020 break;
1021 }
1022 }
1023
1024 return false;
1025}
1026
1027void RtpPlayerDialog::contextMenuEvent(QContextMenuEvent *event)
1028{
1029 list_ctx_menu_->popup(event->globalPos());
1030}
1031
1032void RtpPlayerDialog::updateWidgets()
1033{
1034 bool enable_play = true;
1035 bool enable_pause = false;
1036 bool enable_stop = false;
1037 bool enable_timing = true;
1038 int count = ui->streamTreeWidget->topLevelItemCount();
1039 qsizetype selected = ui->streamTreeWidget->selectedItems().count();
1040
1041 if (count < 1) {
1042 enable_play = false;
1043 ui->skipSilenceButton->setEnabled(false);
1044 ui->minSilenceSpinBox->setEnabled(false);
1045 } else {
1046 ui->skipSilenceButton->setEnabled(true);
1047 ui->minSilenceSpinBox->setEnabled(true);
1048 }
1049
1050 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
1051 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
1052
1053 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
1054 if (audio_stream->outputState() != QAudio::IdleState) {
1055 enable_play = false;
1056 enable_pause = true;
1057 enable_stop = true;
1058 enable_timing = false;
1059 }
1060 }
1061
1062 ui->actionAudioRoutingP->setVisible(!stereo_available_);
1063 ui->actionAudioRoutingL->setVisible(stereo_available_);
1064 ui->actionAudioRoutingLR->setVisible(stereo_available_);
1065 ui->actionAudioRoutingR->setVisible(stereo_available_);
1066
1067 ui->playButton->setEnabled(enable_play);
1068 if (enable_play) {
1069 ui->playButton->setVisible(true);
1070 ui->pauseButton->setVisible(false);
1071 } else if (enable_pause) {
1072 ui->playButton->setVisible(false);
1073 ui->pauseButton->setVisible(true);
1074 }
1075 ui->outputDeviceComboBox->setEnabled(enable_play);
1076 ui->outputAudioRate->setEnabled(enable_play);
1077 ui->pauseButton->setEnabled(enable_pause);
1078 ui->stopButton->setEnabled(enable_stop);
1079 ui->actionStop->setEnabled(enable_stop);
1080 cur_play_pos_->setVisible(enable_stop);
1081
1082 ui->jitterSpinBox->setEnabled(enable_timing);
1083 ui->timingComboBox->setEnabled(enable_timing);
1084 ui->todCheckBox->setEnabled(enable_timing);
1085
1086 read_btn_->setEnabled(read_capture_enabled_);
1087 inaudible_btn_->setEnabled(count > 0);
1088 analyze_btn_->setEnabled(selected > 0);
1089 prepare_btn_->setEnabled(selected > 0);
1090
1091 updateHintLabel();
1092 ui->audioPlot->replot();
1093}
1094
1095void RtpPlayerDialog::handleItemHighlight(QTreeWidgetItem *ti, bool scroll)
1096{
1097 if (ti) {
1098 if (ti != last_ti_) {
1099 if (last_ti_) {
1100 highlightItem(last_ti_, false);
1101 }
1102 highlightItem(ti, true);
1103
1104 if (scroll)
1105 ui->streamTreeWidget->scrollToItem(ti, QAbstractItemView::EnsureVisible);
1106 ui->audioPlot->replot();
1107 last_ti_ = ti;
1108 }
1109 } else {
1110 if (last_ti_) {
1111 highlightItem(last_ti_, false);
1112 ui->audioPlot->replot();
1113 last_ti_ = NULL__null;
1114 }
1115 }
1116}
1117
1118void RtpPlayerDialog::highlightItem(QTreeWidgetItem *ti, bool highlight)
1119{
1120 QFont font;
1121 RtpAudioGraph *audio_graph;
1122
1123 font.setBold(highlight);
1124 for(int i=0; i<ui->streamTreeWidget->columnCount(); i++) {
1125 ti->setFont(i, font);
1126 }
1127
1128 audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();
1129 if (audio_graph) {
1130 audio_graph->setHighlight(highlight);
1131 }
1132}
1133
1134void RtpPlayerDialog::itemEntered(QTreeWidgetItem *item, int column _U___attribute__((unused)))
1135{
1136 handleItemHighlight(item, false);
1137}
1138
1139void RtpPlayerDialog::mouseMovePlot(QMouseEvent *event)
1140{
1141 // The calculations are expensive, so just store the position and
1142 // calculate no more than once per some interval. (On Linux the
1143 // QMouseEvents can be sent absurdly often, every 25 microseconds!)
1144 mouse_pos_ = event->pos();
1145 if (!mouse_update_timer_->isActive()) {
1146 mouse_update_timer_->start();
1147 }
1148}
1149
1150void RtpPlayerDialog::mouseMoveUpdate()
1151{
1152 // findItemByCoords is expensive (because of calling pointDistance),
1153 // and updateHintLabel calls it as well via getHoveredPacket. Some
1154 // way to only perform the distance calculations once would be better.
1155 updateHintLabel();
1156
1157 QTreeWidgetItem *ti = findItemByCoords(mouse_pos_);
1158 handleItemHighlight(ti, true);
1159}
1160
1161void RtpPlayerDialog::showGraphContextMenu(const QPoint &pos)
1162{
1163 graph_ctx_menu_->popup(ui->audioPlot->mapToGlobal(pos));
1164}
1165
1166void RtpPlayerDialog::graphClicked(QMouseEvent*)
1167{
1168 updateWidgets();
1169}
1170
1171void RtpPlayerDialog::graphDoubleClicked(QMouseEvent *event)
1172{
1173 updateWidgets();
1174 if (event->button() == Qt::LeftButton) {
1175 // Move start play line
1176 double ts = ui->audioPlot->xAxis->pixelToCoord(event->pos().x());
1177
1178 setStartPlayMarker(ts);
1179 drawStartPlayMarker();
1180
1181 ui->audioPlot->replot();
1182 }
1183}
1184
1185void RtpPlayerDialog::plotClicked(QCPAbstractPlottable *plottable _U___attribute__((unused)), int dataIndex _U___attribute__((unused)), QMouseEvent *event)
1186{
1187 // Delivered plottable very often points to different element than a mouse
1188 // so we find right one by mouse coordinates
1189 QTreeWidgetItem *ti = findItemByCoords(event->pos());
1190 if (ti) {
1191 if (event->modifiers() == Qt::NoModifier) {
1192 ti->setSelected(true);
1193 } else if (event->modifiers() == Qt::ControlModifier) {
1194 ti->setSelected(!ti->isSelected());
1195 }
1196 }
1197}
1198
1199QTreeWidgetItem *RtpPlayerDialog::findItemByCoords(QPoint point)
1200{
1201 QCPAbstractPlottable *plottable=ui->audioPlot->plottableAt(point);
1202 if (plottable) {
1203 return findItem(plottable);
1204 }
1205
1206 return NULL__null;
1207}
1208
1209QTreeWidgetItem *RtpPlayerDialog::findItem(QCPAbstractPlottable *plottable)
1210{
1211 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
1212 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
1213 RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();
1214 if (audio_graph && audio_graph->isMyPlottable(plottable)) {
1215 return ti;
1216 }
1217 }
1218
1219 return NULL__null;
1220}
1221
1222void RtpPlayerDialog::updateHintLabel()
1223{
1224 int packet_num = getHoveredPacket();
1225 QString hint = "<small><i>";
1226 double start_pos = getStartPlayMarker();
1227 int row_count = ui->streamTreeWidget->topLevelItemCount();
1228 qsizetype selected = ui->streamTreeWidget->selectedItems().count();
1229 int not_muted = 0;
1230
1231 hint += tr("%1 streams").arg(row_count);
1232
1233 if (row_count > 0) {
1234 if (selected > 0) {
1235 hint += tr(", %1 selected").arg(selected);
1236 }
1237
1238 for (int row = 0; row < row_count; row++) {
1239 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
1240 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
1241 if (audio_stream && (!audio_stream->getAudioRouting().isMuted())) {
1242 not_muted++;
1243 }
1244 }
1245
1246 hint += tr(", %1 not muted").arg(not_muted);
1247 }
1248
1249 if (packet_num == 0) {
1250 hint += tr(", start: %1. Double click on graph to set start of playback.")
1251 .arg(getFormatedTime(start_pos));
1252 } else if (packet_num > 0) {
1253 hint += tr(", start: %1, cursor: %2. Press \"G\" to go to packet %3. Double click on graph to set start of playback.")
1254 .arg(getFormatedTime(start_pos))
1255 .arg(getFormatedHoveredTime())
1256 .arg(packet_num);
1257 }
1258
1259 if (!playback_error_.isEmpty()) {
1260 hint += " <font color=\"red\">";
1261 hint += playback_error_;
1262 hint += " </font>";
1263 }
1264
1265 hint += "</i></small>";
1266 ui->hintLabel->setText(hint);
1267}
1268
1269void RtpPlayerDialog::resetXAxis()
1270{
1271 QCustomPlot *ap = ui->audioPlot;
1272
1273 double pixel_pad = 10.0; // per side
1274
1275 ap->rescaleAxes(true);
1276
1277 double axis_pixels = ap->xAxis->axisRect()->width();
1278 ap->xAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, ap->xAxis->range().center());
1279
1280 axis_pixels = ap->yAxis->axisRect()->height();
1281 ap->yAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, ap->yAxis->range().center());
1282
1283 ap->replot();
1284}
1285
1286void RtpPlayerDialog::updateGraphs()
1287{
1288 QCustomPlot *ap = ui->audioPlot;
1289
1290 // Create new plots, just existing ones
1291 createPlot(false);
1292
1293 // Rescale Y axis
1294 double pixel_pad = 10.0; // per side
1295 double axis_pixels = ap->yAxis->axisRect()->height();
1296 ap->yAxis->rescale(true);
1297 ap->yAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, ap->yAxis->range().center());
1298
1299 ap->replot();
1300}
1301
1302void RtpPlayerDialog::playFinished(RtpAudioStream *stream, QAudio::Error error)
1303{
1304 if ((error != QAudio::NoError) && (error != QAudio::UnderrunError)) {
1305 setPlaybackError(tr("Playback of stream %1 failed!")
1306 .arg(stream->getIDAsQString())
1307 );
1308 }
1309 playing_streams_.removeOne(stream);
1310 if (playing_streams_.isEmpty()) {
1311 if (marker_stream_) {
1312 marker_stream_->stop();
1313 }
1314 updateWidgets();
1315 }
1316}
1317
1318void RtpPlayerDialog::setPlayPosition(double secs)
1319{
1320 double cur_secs = cur_play_pos_->point1->key();
1321
1322 if (ui->todCheckBox->isChecked()) {
1323 secs += first_stream_abs_start_time_;
1324 } else {
1325 secs += first_stream_rel_start_time_;
1326 }
1327 if (secs > cur_secs) {
1328 cur_play_pos_->point1->setCoords(secs, 0.0);
1329 cur_play_pos_->point2->setCoords(secs, 1.0);
1330 ui->audioPlot->replot();
1331 }
1332}
1333
1334void RtpPlayerDialog::setPlaybackError(const QString playback_error)
1335{
1336 playback_error_ = playback_error;
1337 updateHintLabel();
1338}
1339
1340tap_packet_status RtpPlayerDialog::tapPacket(void *tapinfo_ptr, packet_info *pinfo, epan_dissect_t *, const void *rtpinfo_ptr, tap_flags_t)
1341{
1342 RtpPlayerDialog *rtp_player_dialog = dynamic_cast<RtpPlayerDialog *>((RtpPlayerDialog*)tapinfo_ptr);
1343 if (!rtp_player_dialog) return TAP_PACKET_DONT_REDRAW;
1344
1345 const struct _rtp_info *rtpinfo = (const struct _rtp_info *)rtpinfo_ptr;
1346 if (!rtpinfo) return TAP_PACKET_DONT_REDRAW;
1347
1348 /* ignore RTP Version != 2 */
1349 if (rtpinfo->info_version != 2)
1350 return TAP_PACKET_DONT_REDRAW;
1351
1352 rtp_player_dialog->addPacket(pinfo, rtpinfo);
1353
1354 return TAP_PACKET_DONT_REDRAW;
1355}
1356
1357void RtpPlayerDialog::addPacket(packet_info *pinfo, const _rtp_info *rtpinfo)
1358{
1359 // Search stream in hash key, if there are multiple streams with same hash
1360 QList<RtpAudioStream *> streams = stream_hash_.values(pinfo_rtp_info_to_hash(pinfo, rtpinfo));
1361 for (int i = 0; i < streams.size(); i++) {
1362 RtpAudioStream *row_stream = streams.at(i);
1363 if (row_stream->isMatch(pinfo, rtpinfo)) {
1364 row_stream->addRtpPacket(pinfo, rtpinfo);
1365 break;
1366 }
1367 }
1368
1369// qDebug() << "=ap no match!" << address_to_qstring(&pinfo->src) << address_to_qstring(&pinfo->dst);
1370}
1371
1372void RtpPlayerDialog::zoomXAxis(bool in)
1373{
1374 QCustomPlot *ap = ui->audioPlot;
1375 double h_factor = ap->axisRect()->rangeZoomFactor(Qt::Horizontal);
1376
1377 if (!in) {
1378 h_factor = pow(h_factor, -1);
1379 }
1380
1381 ap->xAxis->scaleRange(h_factor, ap->xAxis->range().center());
1382 ap->replot();
1383}
1384
1385// XXX I tried using seconds but pixels make more sense at varying zoom
1386// levels.
1387void RtpPlayerDialog::panXAxis(int x_pixels)
1388{
1389 QCustomPlot *ap = ui->audioPlot;
1390 double h_pan;
1391
1392 h_pan = ap->xAxis->range().size() * x_pixels / ap->xAxis->axisRect()->width();
1393 if (x_pixels) {
1394 ap->xAxis->moveRange(h_pan);
1395 ap->replot();
1396 }
1397}
1398
1399void RtpPlayerDialog::on_playButton_clicked()
1400{
1401 double start_time;
1402 QList<RtpAudioStream *> streams_to_start;
1403
1404 ui->hintLabel->setText("<i><small>" + tr("Preparing to play...") + "</i></small>");
1405 mainApp->processEvents();
1406 ui->pauseButton->setChecked(false);
1407
1408 // Protect start time against move of marker during the play
1409 start_marker_time_play_ = start_marker_time_;
1410 silence_skipped_time_ = 0.0;
1411 cur_play_pos_->point1->setCoords(start_marker_time_play_, 0.0);
1412 cur_play_pos_->point2->setCoords(start_marker_time_play_, 1.0);
1413 cur_play_pos_->setVisible(true);
1414 playback_error_.clear();
1415
1416 if (ui->todCheckBox->isChecked()) {
1417 start_time = start_marker_time_play_;
1418 } else {
1419 start_time = start_marker_time_play_ - first_stream_rel_start_time_;
1420 }
1421
1422#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))
1423 QAudioDevice cur_out_device = getCurrentDeviceInfo();
1424#else
1425 QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo();
1426#endif
1427 playing_streams_.clear();
1428 int row_count = ui->streamTreeWidget->topLevelItemCount();
1429 for (int row = 0; row < row_count; row++) {
1430 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
1431 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
1432 // All streams starts at first_stream_rel_start_time_
1433 audio_stream->setStartPlayTime(start_time);
1434 if (audio_stream->prepareForPlay(cur_out_device)) {
1435 playing_streams_ << audio_stream;
1436 }
1437 }
1438
1439 // Prepare silent stream for progress marker
1440 if (!marker_stream_) {
1441 marker_stream_ = getSilenceAudioOutput();
1442 } else {
1443 marker_stream_->stop();
1444 }
1445
1446 // Start progress marker and then audio streams
1447#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))
1448 notify_timer_start_diff_ = -1;
1449#endif
1450 marker_stream_->start(new AudioSilenceGenerator(marker_stream_));
1451 // It may happen that stream play is finished before all others are started
1452 // therefore we do not use playing_streams_ there, but separate temporarily
1453 // list. It avoids access element/remove element race condition.
1454 streams_to_start = playing_streams_;
1455 for( int i = 0; i<streams_to_start.count(); ++i ) {
1456 streams_to_start[i]->startPlaying();
1457 }
1458
1459 updateWidgets();
1460}
1461
1462#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))
1463QAudioDevice RtpPlayerDialog::getCurrentDeviceInfo()
1464{
1465 QAudioDevice cur_out_device = QMediaDevices::defaultAudioOutput();
1466 QString cur_out_name = currentOutputDeviceName();
1467 foreach (QAudioDevice out_device, QMediaDevices::audioOutputs())for (auto _container_1467 = QtPrivate::qMakeForeachContainer(
QMediaDevices::audioOutputs()); _container_1467.i != _container_1467
.e; ++_container_1467.i) if (QAudioDevice out_device = *_container_1467
.i; false) {} else
{
1468 if (cur_out_name == out_device.description()) {
1469 cur_out_device = out_device;
1470 }
1471 }
1472
1473 return cur_out_device;
1474}
1475
1476void RtpPlayerDialog::sinkStateChanged()
1477{
1478 if (marker_stream_->state() == QAudio::ActiveState) {
1479 notify_timer_.start();
1480 } else {
1481 notify_timer_.stop();
1482 }
1483}
1484#else
1485QAudioDeviceInfo RtpPlayerDialog::getCurrentDeviceInfo()
1486{
1487 QAudioDeviceInfo cur_out_device = QAudioDeviceInfo::defaultOutputDevice();
1488 QString cur_out_name = currentOutputDeviceName();
1489 foreach (QAudioDeviceInfo out_device, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput))for (auto _container_1489 = QtPrivate::qMakeForeachContainer(
QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)); _container_1489
.i != _container_1489.e; ++_container_1489.i) if (QAudioDeviceInfo
out_device = *_container_1489.i; false) {} else
{
1490 if (cur_out_name == out_device.deviceName()) {
1491 cur_out_device = out_device;
1492 }
1493 }
1494
1495 return cur_out_device;
1496}
1497#endif
1498
1499#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))
1500QAudioSink *RtpPlayerDialog::getSilenceAudioOutput()
1501{
1502 QAudioDevice cur_out_device = getCurrentDeviceInfo();
1503
1504 QAudioFormat format;
1505 if (marker_stream_requested_out_rate_ > 0) {
1506 format.setSampleRate(marker_stream_requested_out_rate_);
1507 } else {
1508 format.setSampleRate(8000);
1509 }
1510 // Must match rtp_media.h.
1511 format.setSampleFormat(QAudioFormat::Int16);
1512 format.setChannelCount(1);
1513 if (!cur_out_device.isFormatSupported(format)) {
1514 format = cur_out_device.preferredFormat();
1515 }
1516
1517 QAudioSink *sink = new QAudioSink(cur_out_device, format, this);
1518 connect(sink, &QAudioSink::stateChanged, this, &RtpPlayerDialog::sinkStateChanged);
1519 return sink;
1520}
1521#else
1522QAudioOutput *RtpPlayerDialog::getSilenceAudioOutput()
1523{
1524 QAudioOutput *o;
1525 QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo();
1526
1527 QAudioFormat format;
1528 if (marker_stream_requested_out_rate_ > 0) {
1529 format.setSampleRate(marker_stream_requested_out_rate_);
1530 } else {
1531 format.setSampleRate(8000);
1532 }
1533 format.setSampleSize(SAMPLE_BYTES(sizeof(SAMPLE) / sizeof(char)) * 8); // bits
1534 format.setSampleType(QAudioFormat::SignedInt);
1535 format.setChannelCount(1);
1536 format.setCodec("audio/pcm");
1537 if (!cur_out_device.isFormatSupported(format)) {
1538 format = cur_out_device.nearestFormat(format);
1539 }
1540
1541 o = new QAudioOutput(cur_out_device, format, this);
1542 o->setNotifyInterval(100); // ~15 fps
1543 connect(o, &QAudioOutput::notify, this, &RtpPlayerDialog::outputNotify);
1544
1545 return o;
1546}
1547#endif
1548
1549void RtpPlayerDialog::outputNotify()
1550{
1551 double new_current_pos = 0.0;
1552 double current_pos = 0.0;
1553 qint64 usecs = marker_stream_->processedUSecs();
1554
1555#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))
1556 // First notify can show end of buffer, not play point so we have
1557 // remember the shift
1558 if ( -1 == notify_timer_start_diff_ || 0 == notify_timer_start_diff_) {
1559 notify_timer_start_diff_ = usecs;
1560 }
1561 usecs -= notify_timer_start_diff_;
1562#endif
1563 double secs = usecs / 1000000.0;
1564
1565 if (ui->skipSilenceButton->isChecked() && !playing_streams_.isEmpty()) {
1566 // We should check whether we can skip some silence
1567 // We must calculate in time domain as every stream can use different
1568 // play rate
1569 double min_silence = playing_streams_[0]->getEndOfSilenceTime();
1570 for( int i = 1; i<playing_streams_.count(); ++i ) {
1571 qint64 cur_silence = playing_streams_[i]->getEndOfSilenceTime();
1572 if (cur_silence < min_silence) {
1573 min_silence = cur_silence;
1574 }
1575 }
1576
1577 if (min_silence > 0.0) {
1578 double silence_duration;
1579
1580 // Calculate silence duration we can skip
1581 new_current_pos = first_stream_rel_start_time_ + min_silence;
1582 if (ui->todCheckBox->isChecked()) {
1583 current_pos = secs + start_marker_time_play_ + first_stream_rel_start_time_;
1584 } else {
1585 current_pos = secs + start_marker_time_play_;
1586 }
1587 silence_duration = new_current_pos - current_pos;
1588
1589 if (silence_duration >= ui->minSilenceSpinBox->value()) {
1590 // Skip silence gap and update cursor difference
1591 for( int i = 0; i<playing_streams_.count(); ++i ) {
1592 // Convert silence from time domain to samples
1593 qint64 skip_samples = playing_streams_[i]->convertTimeToSamples(min_silence);
1594 playing_streams_[i]->seekPlaying(skip_samples);
1595 }
1596 silence_skipped_time_ = silence_duration;
1597 }
1598 }
1599 }
1600
1601 // Calculate new cursor position
1602 if (ui->todCheckBox->isChecked()) {
1603 secs += start_marker_time_play_;
1604 secs += silence_skipped_time_;
1605 } else {
1606 secs += start_marker_time_play_;
1607 secs -= first_stream_rel_start_time_;
1608 secs += silence_skipped_time_;
1609 }
1610 setPlayPosition(secs);
1611}
1612
1613void RtpPlayerDialog::on_pauseButton_clicked()
1614{
1615 for( int i = 0; i<playing_streams_.count(); ++i ) {
1616 playing_streams_[i]->pausePlaying();
1617 }
1618 if (ui->pauseButton->isChecked()) {
1619 marker_stream_->suspend();
1620 } else {
1621 marker_stream_->resume();
1622 }
1623 updateWidgets();
1624}
1625
1626void RtpPlayerDialog::on_stopButton_clicked()
1627{
1628 // We need copy of list because items will be removed during stopPlaying()
1629 QList<RtpAudioStream *> ps=QList<RtpAudioStream *>(playing_streams_);
1630 for( int i = 0; i<ps.count(); ++i ) {
1631 ps[i]->stopPlaying();
1632 }
1633 marker_stream_->stop();
1634 cur_play_pos_->setVisible(false);
1635 updateWidgets();
1636}
1637
1638void RtpPlayerDialog::on_actionReset_triggered()
1639{
1640 resetXAxis();
1641}
1642
1643void RtpPlayerDialog::on_actionZoomIn_triggered()
1644{
1645 zoomXAxis(true);
1646}
1647
1648void RtpPlayerDialog::on_actionZoomOut_triggered()
1649{
1650 zoomXAxis(false);
1651}
1652
1653void RtpPlayerDialog::on_actionMoveLeft10_triggered()
1654{
1655 panXAxis(-10);
1656}
1657
1658void RtpPlayerDialog::on_actionMoveRight10_triggered()
1659{
1660 panXAxis(10);
1661}
1662
1663void RtpPlayerDialog::on_actionMoveLeft1_triggered()
1664{
1665 panXAxis(-1);
1666}
1667
1668void RtpPlayerDialog::on_actionMoveRight1_triggered()
1669{
1670 panXAxis(1);
1671}
1672
1673void RtpPlayerDialog::on_actionGoToPacket_triggered()
1674{
1675 int packet_num = getHoveredPacket();
1676 if (packet_num > 0) emit goToPacket(packet_num);
1677}
1678
1679void RtpPlayerDialog::handleGoToSetupPacket(QTreeWidgetItem *ti)
1680{
1681 if (ti) {
1682 bool ok;
1683
1684 int packet_num = ti->data(first_pkt_col_, Qt::UserRole).toInt(&ok);
1685 if (ok) {
1686 emit goToPacket(packet_num);
1687 }
1688 }
1689}
1690
1691void RtpPlayerDialog::on_actionGoToSetupPacketPlot_triggered()
1692{
1693 QPoint pos = ui->audioPlot->mapFromGlobal(QCursor::pos());
1694 handleGoToSetupPacket(findItemByCoords(pos));
1695}
1696
1697void RtpPlayerDialog::on_actionGoToSetupPacketTree_triggered()
1698{
1699 handleGoToSetupPacket(last_ti_);
1700}
1701
1702// Make waveform graphs selectable and update the treewidget selection accordingly.
1703void RtpPlayerDialog::on_streamTreeWidget_itemSelectionChanged()
1704{
1705 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
1706 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
1707 RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();
1708 if (audio_graph) {
1709 audio_graph->setSelected(ti->isSelected());
1710 }
1711 }
1712
1713 qsizetype selected = ui->streamTreeWidget->selectedItems().count();
1714 if (selected == 0) {
1715 analyze_btn_->setEnabled(false);
1716 prepare_btn_->setEnabled(false);
1717 export_btn_->setEnabled(false);
1718 } else if (selected == 1) {
1719 analyze_btn_->setEnabled(true);
1720 prepare_btn_->setEnabled(true);
1721 export_btn_->setEnabled(true);
1722 ui->actionSavePayload->setEnabled(true);
1723 } else {
1724 analyze_btn_->setEnabled(true);
1725 prepare_btn_->setEnabled(true);
1726 export_btn_->setEnabled(true);
1727 ui->actionSavePayload->setEnabled(false);
1728 }
1729
1730 if (!block_redraw_) {
1731 ui->audioPlot->replot();
1732 updateHintLabel();
1733 }
1734}
1735
1736// Change channel audio routing if double clicked channel column
1737void RtpPlayerDialog::on_streamTreeWidget_itemDoubleClicked(QTreeWidgetItem *item, const int column)
1738{
1739 if (column == channel_col_) {
1740 RtpAudioStream *audio_stream = item->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
1741 if (!audio_stream)
1742 return;
1743
1744 AudioRouting audio_routing = audio_stream->getAudioRouting();
1745 audio_routing = audio_routing.getNextChannel(stereo_available_);
1746 changeAudioRoutingOnItem(item, audio_routing);
1747 }
1748 updateHintLabel();
1749}
1750
1751void RtpPlayerDialog::removeRow(QTreeWidgetItem *ti)
1752{
1753 if (last_ti_ && (last_ti_ == ti)) {
1754 highlightItem(last_ti_, false);
1755 last_ti_ = NULL__null;
1756 }
1757 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
1758 if (audio_stream) {
1759 stream_hash_.remove(audio_stream->getHash(), audio_stream);
1760 ti->setData(stream_data_col_, Qt::UserRole, QVariant());
1761 delete audio_stream;
1762 }
1763
1764 RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();
1765 if (audio_graph) {
1766 ti->setData(graph_audio_data_col_, Qt::UserRole, QVariant());
1767 audio_graph->remove(ui->audioPlot);
1768 }
1769
1770 QCPGraph *graph;
1771 graph = ti->data(graph_sequence_data_col_, Qt::UserRole).value<QCPGraph*>();
1772 if (graph) {
1773 ti->setData(graph_sequence_data_col_, Qt::UserRole, QVariant());
1774 ui->audioPlot->removeGraph(graph);
1775 }
1776
1777 graph = ti->data(graph_jitter_data_col_, Qt::UserRole).value<QCPGraph*>();
1778 if (graph) {
1779 ti->setData(graph_jitter_data_col_, Qt::UserRole, QVariant());
1780 ui->audioPlot->removeGraph(graph);
1781 }
1782
1783 graph = ti->data(graph_timestamp_data_col_, Qt::UserRole).value<QCPGraph*>();
1784 if (graph) {
1785 ti->setData(graph_timestamp_data_col_, Qt::UserRole, QVariant());
1786 ui->audioPlot->removeGraph(graph);
1787 }
1788
1789 graph = ti->data(graph_silence_data_col_, Qt::UserRole).value<QCPGraph*>();
1790 if (graph) {
1791 ti->setData(graph_silence_data_col_, Qt::UserRole, QVariant());
1792 ui->audioPlot->removeGraph(graph);
1793 }
1794
1795 delete ti;
1796}
1797
1798void RtpPlayerDialog::on_actionRemoveStream_triggered()
1799{
1800 lockUI();
1801 QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();
1802
1803 block_redraw_ = true;
1804 for(int i = static_cast<int>(items.count()) - 1; i>=0; i-- ) {
1805 removeRow(items[i]);
1806 }
1807 block_redraw_ = false;
1808 // TODO: Recalculate legend
1809 // - Graphs used for legend could be removed above and we must add new
1810 // - If no legend is required, it should be removed
1811
1812 // Redraw existing waveforms and rescale Y axis
1813 updateGraphs();
1814
1815 updateWidgets();
1816 unlockUI();
1817}
1818
1819// If called with channel_any, just muted flag should be changed
1820void RtpPlayerDialog::changeAudioRoutingOnItem(QTreeWidgetItem *ti, AudioRouting new_audio_routing)
1821{
1822 if (!ti)
1823 return;
1824
1825 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
1826 if (!audio_stream)
1827 return;
1828
1829 AudioRouting audio_routing = audio_stream->getAudioRouting();
1830 audio_routing.mergeAudioRouting(new_audio_routing);
1831 formatAudioRouting(ti, audio_routing);
1832
1833 audio_stream->setAudioRouting(audio_routing);
1834
1835 RtpAudioGraph *audio_graph = ti->data(graph_audio_data_col_, Qt::UserRole).value<RtpAudioGraph*>();
1836 if (audio_graph) {
1837
1838 audio_graph->setSelected(ti->isSelected());
1839 audio_graph->setMuted(audio_routing.isMuted());
1840 if (!block_redraw_) {
1841 ui->audioPlot->replot();
1842 }
1843 }
1844}
1845
1846// Find current item and apply change on it
1847void RtpPlayerDialog::changeAudioRouting(AudioRouting new_audio_routing)
1848{
1849 lockUI();
1850 QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();
1851
1852 block_redraw_ = true;
1853 for(int i = 0; i<items.count(); i++ ) {
1854
1855 QTreeWidgetItem *ti = items[i];
1856 changeAudioRoutingOnItem(ti, new_audio_routing);
1857 }
1858 block_redraw_ = false;
1859 ui->audioPlot->replot();
1860 updateHintLabel();
1861 unlockUI();
1862}
1863
1864// Invert mute/unmute on item
1865void RtpPlayerDialog::invertAudioMutingOnItem(QTreeWidgetItem *ti)
1866{
1867 if (!ti)
1868 return;
1869
1870 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
1871 if (!audio_stream)
1872 return;
1873
1874 AudioRouting audio_routing = audio_stream->getAudioRouting();
1875 // Invert muting
1876 if (audio_routing.isMuted()) {
1877 changeAudioRoutingOnItem(ti, AudioRouting(AUDIO_UNMUTEDfalse, channel_any));
1878 } else {
1879 changeAudioRoutingOnItem(ti, AudioRouting(AUDIO_MUTEDtrue, channel_any));
1880 }
1881}
1882
1883void RtpPlayerDialog::on_actionAudioRoutingP_triggered()
1884{
1885 changeAudioRouting(AudioRouting(AUDIO_UNMUTEDfalse, channel_mono));
1886}
1887
1888void RtpPlayerDialog::on_actionAudioRoutingL_triggered()
1889{
1890 changeAudioRouting(AudioRouting(AUDIO_UNMUTEDfalse, channel_stereo_left));
1891}
1892
1893void RtpPlayerDialog::on_actionAudioRoutingLR_triggered()
1894{
1895 changeAudioRouting(AudioRouting(AUDIO_UNMUTEDfalse, channel_stereo_both));
1896}
1897
1898void RtpPlayerDialog::on_actionAudioRoutingR_triggered()
1899{
1900 changeAudioRouting(AudioRouting(AUDIO_UNMUTEDfalse, channel_stereo_right));
1901}
1902
1903void RtpPlayerDialog::on_actionAudioRoutingMute_triggered()
1904{
1905 changeAudioRouting(AudioRouting(AUDIO_MUTEDtrue, channel_any));
1906}
1907
1908void RtpPlayerDialog::on_actionAudioRoutingUnmute_triggered()
1909{
1910 changeAudioRouting(AudioRouting(AUDIO_UNMUTEDfalse, channel_any));
1911}
1912
1913void RtpPlayerDialog::on_actionAudioRoutingMuteInvert_triggered()
1914{
1915 lockUI();
1916 QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();
1917
1918 block_redraw_ = true;
1919 for(int i = 0; i<items.count(); i++ ) {
1920
1921 QTreeWidgetItem *ti = items[i];
1922 invertAudioMutingOnItem(ti);
1923 }
1924 block_redraw_ = false;
1925 ui->audioPlot->replot();
1926 updateHintLabel();
1927 unlockUI();
1928}
1929
1930const QString RtpPlayerDialog::getFormatedTime(double f_time)
1931{
1932 QString time_str;
1933
1934 if (ui->todCheckBox->isChecked()) {
1935 QDateTime date_time = QDateTime::fromMSecsSinceEpoch(f_time * 1000.0);
1936 time_str = date_time.toString("yyyy-MM-dd hh:mm:ss.zzz");
1937 } else {
1938 time_str = QString::number(f_time, 'f', 6);
1939 time_str += " s";
1940 }
1941
1942 return time_str;
1943}
1944
1945const QString RtpPlayerDialog::getFormatedHoveredTime()
1946{
1947 QPoint pos = ui->audioPlot->mapFromGlobal(QCursor::pos());
1948 QTreeWidgetItem *ti = findItemByCoords(pos);
1949 if (!ti) return tr("Unknown");
1950
1951 double ts = ui->audioPlot->xAxis->pixelToCoord(pos.x());
1952
1953 return getFormatedTime(ts);
1954}
1955
1956int RtpPlayerDialog::getHoveredPacket()
1957{
1958 QPoint pos = ui->audioPlot->mapFromGlobal(QCursor::pos());
1959 QTreeWidgetItem *ti = findItemByCoords(pos);
1960 if (!ti) return 0;
1961
1962 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
1963
1964 double ts = ui->audioPlot->xAxis->pixelToCoord(pos.x());
1965
1966 return audio_stream->nearestPacket(ts, !ui->todCheckBox->isChecked());
1967}
1968
1969// Used by RtpAudioStreams to initialize QAudioOutput. We could alternatively
1970// pass the corresponding QAudioDeviceInfo directly.
1971QString RtpPlayerDialog::currentOutputDeviceName()
1972{
1973 return ui->outputDeviceComboBox->currentText();
1974}
1975
1976void RtpPlayerDialog::fillAudioRateMenu()
1977{
1978 ui->outputAudioRate->blockSignals(true);
1979 ui->outputAudioRate->clear();
1980 ui->outputAudioRate->addItem(tr("Automatic"));
1981#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))
1982 // XXX QAudioDevice doesn't provide supportedSampleRates(). Fake it with
1983 // what's available.
1984 QAudioDevice cur_out_device = getCurrentDeviceInfo();
1985 QSet<int>sample_rates;
1986 if (!cur_out_device.isNull()) {
1987 sample_rates.insert(cur_out_device.preferredFormat().sampleRate());
1988 // Add 8000 if supported
1989 if ((cur_out_device.minimumSampleRate() <= 8000) &&
1990 (8000 <= cur_out_device.maximumSampleRate())
1991 ) {
1992 sample_rates.insert(8000);
1993 }
1994 // Add 16000 if supported
1995 if ((cur_out_device.minimumSampleRate() <= 16000) &&
1996 (16000 <= cur_out_device.maximumSampleRate())
1997 ) {
1998 sample_rates.insert(16000);
1999 }
2000 // Add 44100 if supported
2001 if ((cur_out_device.minimumSampleRate() <= 44100) &&
2002 (44100 <= cur_out_device.maximumSampleRate())
2003 ) {
2004 sample_rates.insert(44100);
2005 }
2006 }
2007
2008 // Sort values
2009 QList<int> sorter = sample_rates.values();
2010 std::sort(sorter.begin(), sorter.end());
2011
2012 // Insert rates to the list
2013 for (auto rate : sorter) {
2014 ui->outputAudioRate->addItem(QString::number(rate));
2015 }
2016#else
2017 QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo();
2018
2019 if (!cur_out_device.isNull()) {
2020 foreach (int rate, cur_out_device.supportedSampleRates())for (auto _container_2020 = QtPrivate::qMakeForeachContainer(
cur_out_device.supportedSampleRates()); _container_2020.i != _container_2020
.e; ++_container_2020.i) if (int rate = *_container_2020.i; false
) {} else
{
2021 ui->outputAudioRate->addItem(QString::number(rate));
2022 }
2023 }
2024#endif
2025 ui->outputAudioRate->blockSignals(false);
2026}
2027
2028void RtpPlayerDialog::cleanupMarkerStream()
2029{
2030 if (marker_stream_) {
2031 marker_stream_->stop();
2032 delete marker_stream_;
2033 marker_stream_ = NULL__null;
2034 }
2035}
2036
2037void RtpPlayerDialog::on_outputDeviceComboBox_currentTextChanged(const QString &)
2038{
2039 lockUI();
2040 stereo_available_ = isStereoAvailable();
2041 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
2042 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
2043 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
2044 if (!audio_stream)
2045 continue;
2046
2047 changeAudioRoutingOnItem(ti, audio_stream->getAudioRouting().convert(stereo_available_));
2048 }
2049
2050 marker_stream_requested_out_rate_ = 0;
2051 cleanupMarkerStream();
2052 fillAudioRateMenu();
2053 rescanPackets();
2054 unlockUI();
2055}
2056
2057void RtpPlayerDialog::on_outputAudioRate_currentTextChanged(const QString & rate_string)
2058{
2059 lockUI();
2060 // Any unconvertable string is converted to 0 => used as Automatic rate
2061 unsigned selected_rate = rate_string.toInt();
2062
2063 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
2064 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
2065 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
2066 if (!audio_stream)
2067 continue;
2068
2069 audio_stream->setRequestedPlayRate(selected_rate);
2070 }
2071 marker_stream_requested_out_rate_ = selected_rate;
2072 cleanupMarkerStream();
2073 rescanPackets();
2074 unlockUI();
2075}
2076
2077void RtpPlayerDialog::on_jitterSpinBox_valueChanged(double)
2078{
2079 rescanPackets();
2080}
2081
2082void RtpPlayerDialog::on_timingComboBox_currentIndexChanged(int)
2083{
2084 rescanPackets();
2085}
2086
2087void RtpPlayerDialog::on_todCheckBox_toggled(bool)
2088{
2089 QCPAxis *x_axis = ui->audioPlot->xAxis;
2090 double move;
2091
2092 // Create plot with new tod settings
2093 createPlot();
2094
2095 // Move view to same place as was shown before the change
2096 if (ui->todCheckBox->isChecked()) {
2097 // rel -> abs
2098 // based on abs time of first sample
2099 setStartPlayMarker(first_stream_abs_start_time_ + start_marker_time_ - first_stream_rel_start_time_);
2100 move = first_stream_abs_start_time_ - first_stream_rel_start_time_;
2101 } else {
2102 // abs -> rel
2103 // based on 0s
2104 setStartPlayMarker(first_stream_rel_start_time_ + start_marker_time_);
2105 move = - first_stream_abs_start_time_ + first_stream_rel_start_time_;
2106 }
2107 x_axis->moveRange(move);
2108 drawStartPlayMarker();
2109 ui->audioPlot->replot();
2110}
2111
2112void RtpPlayerDialog::on_buttonBox_helpRequested()
2113{
2114 mainApp->helpTopicAction(HELP_TELEPHONY_RTP_PLAYER_DIALOG);
2115}
2116
2117double RtpPlayerDialog::getStartPlayMarker()
2118{
2119 double start_pos;
2120
2121 if (ui->todCheckBox->isChecked()) {
2122 start_pos = start_marker_time_ + first_stream_abs_start_time_;
2123 } else {
2124 start_pos = start_marker_time_;
2125 }
2126
2127 return start_pos;
2128}
2129
2130void RtpPlayerDialog::drawStartPlayMarker()
2131{
2132 double pos = getStartPlayMarker();
2133
2134 start_marker_pos_->point1->setCoords(pos, 0.0);
2135 start_marker_pos_->point2->setCoords(pos, 1.0);
2136
2137 updateHintLabel();
2138}
2139
2140void RtpPlayerDialog::setStartPlayMarker(double new_time)
2141{
2142 if (ui->todCheckBox->isChecked()) {
2143 new_time = qBound(first_stream_abs_start_time_, new_time, first_stream_abs_start_time_ + streams_length_);
2144 // start_play_time is relative, we must calculate it
2145 start_marker_time_ = new_time - first_stream_abs_start_time_;
2146 } else {
2147 new_time = qBound(first_stream_rel_start_time_, new_time, first_stream_rel_start_time_ + streams_length_);
2148 start_marker_time_ = new_time;
2149 }
2150}
2151
2152void RtpPlayerDialog::updateStartStopTime(rtpstream_info_t *rtpstream, bool is_first)
2153{
2154 // Calculate start time of first last packet of last stream
2155 double stream_rel_start_time = nstime_to_sec(&rtpstream->start_rel_time);
2156 double stream_abs_start_time = nstime_to_sec(&rtpstream->start_abs_time);
2157 double stream_rel_stop_time = nstime_to_sec(&rtpstream->stop_rel_time);
2158
2159 if (is_first) {
2160 // Take start/stop time for first stream
2161 first_stream_rel_start_time_ = stream_rel_start_time;
2162 first_stream_abs_start_time_ = stream_abs_start_time;
2163 first_stream_rel_stop_time_ = stream_rel_stop_time;
2164 } else {
2165 // Calculate min/max for start/stop time for other streams
2166 first_stream_rel_start_time_ = qMin(first_stream_rel_start_time_, stream_rel_start_time);
2167 first_stream_abs_start_time_ = qMin(first_stream_abs_start_time_, stream_abs_start_time);
2168 first_stream_rel_stop_time_ = qMax(first_stream_rel_stop_time_, stream_rel_stop_time);
2169 }
2170 streams_length_ = first_stream_rel_stop_time_ - first_stream_rel_start_time_;
2171}
2172
2173void RtpPlayerDialog::formatAudioRouting(QTreeWidgetItem *ti, AudioRouting audio_routing)
2174{
2175 ti->setText(channel_col_, tr(audio_routing.formatAudioRoutingToString()));
2176}
2177
2178bool RtpPlayerDialog::isStereoAvailable()
2179{
2180#if (QT_VERSION((6<<16)|(4<<8)|(2)) >= QT_VERSION_CHECK(6, 0, 0)((6<<16)|(0<<8)|(0)))
2181 QAudioDevice cur_out_device = getCurrentDeviceInfo();
2182 if (cur_out_device.maximumChannelCount() > 1) {
2183 return true;
2184 }
2185#else
2186 QAudioDeviceInfo cur_out_device = getCurrentDeviceInfo();
2187 foreach(int count, cur_out_device.supportedChannelCounts())for (auto _container_2187 = QtPrivate::qMakeForeachContainer(
cur_out_device.supportedChannelCounts()); _container_2187.i !=
_container_2187.e; ++_container_2187.i) if (int count = *_container_2187
.i; false) {} else
{
2188 if (count > 1) {
2189 return true;
2190 }
2191 }
2192#endif
2193
2194 return false;
2195}
2196
2197void RtpPlayerDialog::invertSelection()
2198{
2199 block_redraw_ = true;
2200 ui->streamTreeWidget->blockSignals(true);
2201 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
2202 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
2203 ti->setSelected(!ti->isSelected());
2204 }
2205 ui->streamTreeWidget->blockSignals(false);
2206 block_redraw_ = false;
2207 ui->audioPlot->replot();
2208 updateHintLabel();
2209}
2210
2211void RtpPlayerDialog::on_actionSelectAll_triggered()
2212{
2213 ui->streamTreeWidget->selectAll();
2214 updateHintLabel();
2215}
2216
2217void RtpPlayerDialog::on_actionSelectInvert_triggered()
2218{
2219 invertSelection();
2220 updateHintLabel();
2221}
2222
2223void RtpPlayerDialog::on_actionSelectNone_triggered()
2224{
2225 ui->streamTreeWidget->clearSelection();
2226 updateHintLabel();
2227}
2228
2229void RtpPlayerDialog::on_actionPlay_triggered()
2230{
2231 if (ui->playButton->isEnabled()) {
2232 ui->playButton->animateClick();
2233 } else if (ui->pauseButton->isEnabled()) {
2234 ui->pauseButton->animateClick();
2235 }
2236}
2237
2238void RtpPlayerDialog::on_actionStop_triggered()
2239{
2240 if (ui->stopButton->isEnabled()) {
2241 ui->stopButton->animateClick();
2242 }
2243}
2244
2245qint64 RtpPlayerDialog::saveAudioHeaderAU(QFile *save_file, quint32 channels, unsigned audio_rate)
2246{
2247 uint8_t pd[4];
2248 int64_t nchars;
2249
2250 /* https://pubs.opengroup.org/external/auformat.html */
2251 /* First we write the .au header. All values in the header are
2252 * 4-byte big-endian values, so we use pntoh32() to copy them
2253 * to a 4-byte buffer, in big-endian order, and then write out
2254 * the buffer. */
2255
2256 /* the magic word 0x2e736e64 == .snd */
2257 phton32(pd, 0x2e736e64);
2258 nchars = save_file->write((const char *)pd, 4);
2259 if (nchars != 4) {
2260 return -1;
2261 }
2262
2263 /* header offset == 24 bytes */
2264 phton32(pd, 24);
2265 nchars = save_file->write((const char *)pd, 4);
2266 if (nchars != 4) {
2267 return -1;
2268 }
2269
2270 /* total length; it is permitted to set this to 0xffffffff */
2271 phton32(pd, 0xffffffff);
2272 nchars = save_file->write((const char *)pd, 4);
2273 if (nchars != 4) {
2274 return -1;
2275 }
2276
2277 /* encoding format == 16-bit linear PCM */
2278 phton32(pd, 3);
2279 nchars = save_file->write((const char *)pd, 4);
2280 if (nchars != 4) {
2281 return -1;
2282 }
2283
2284 /* sample rate [Hz] */
2285 phton32(pd, audio_rate);
2286 nchars = save_file->write((const char *)pd, 4);
2287 if (nchars != 4) {
2288 return -1;
2289 }
2290
2291 /* channels */
2292 phton32(pd, channels);
2293 nchars = save_file->write((const char *)pd, 4);
2294 if (nchars != 4) {
2295 return -1;
2296 }
2297
2298 return save_file->pos();
2299}
2300
2301qint64 RtpPlayerDialog::saveAudioHeaderWAV(QFile *save_file, quint32 channels, unsigned audio_rate, qint64 samples)
2302{
2303 uint8_t pd[4];
2304 int64_t nchars;
2305 int32_t subchunk2Size;
2306 int32_t data32;
2307 int16_t data16;
2308
2309 subchunk2Size = sizeof(SAMPLE) * channels * (int32_t)samples;
2310
2311 /* http://soundfile.sapp.org/doc/WaveFormat/ */
2312
2313 /* RIFF header, ChunkID 0x52494646 == RIFF */
2314 phton32(pd, 0x52494646);
2315 nchars = save_file->write((const char *)pd, 4);
2316 if (nchars != 4) {
2317 return -1;
2318 }
2319
2320 /* RIFF header, ChunkSize */
2321 data32 = 36 + subchunk2Size;
2322 nchars = save_file->write((const char *)&data32, 4);
2323 if (nchars != 4) {
2324 return -1;
2325 }
2326
2327 /* RIFF header, Format 0x57415645 == WAVE */
2328 phton32(pd, 0x57415645);
2329 nchars = save_file->write((const char *)pd, 4);
2330 if (nchars != 4) {
2331 return -1;
2332 }
2333
2334 /* WAVE fmt header, Subchunk1ID 0x666d7420 == 'fmt ' */
2335 phton32(pd, 0x666d7420);
2336 nchars = save_file->write((const char *)pd, 4);
2337 if (nchars != 4) {
2338 return -1;
2339 }
2340
2341 /* WAVE fmt header, Subchunk1Size */
2342 data32 = 16;
2343 nchars = save_file->write((const char *)&data32, 4);
2344 if (nchars != 4) {
2345 return -1;
2346 }
2347
2348 /* WAVE fmt header, AudioFormat 1 == PCM */
2349 data16 = 1;
2350 nchars = save_file->write((const char *)&data16, 2);
2351 if (nchars != 2) {
2352 return -1;
2353 }
2354
2355 /* WAVE fmt header, NumChannels */
2356 data16 = channels;
2357 nchars = save_file->write((const char *)&data16, 2);
2358 if (nchars != 2) {
2359 return -1;
2360 }
2361
2362 /* WAVE fmt header, SampleRate */
2363 data32 = audio_rate;
2364 nchars = save_file->write((const char *)&data32, 4);
2365 if (nchars != 4) {
2366 return -1;
2367 }
2368
2369 /* WAVE fmt header, ByteRate */
2370 data32 = audio_rate * channels * sizeof(SAMPLE);
2371 nchars = save_file->write((const char *)&data32, 4);
2372 if (nchars != 4) {
2373 return -1;
2374 }
2375
2376 /* WAVE fmt header, BlockAlign */
2377 data16 = channels * (int16_t)sizeof(SAMPLE);
2378 nchars = save_file->write((const char *)&data16, 2);
2379 if (nchars != 2) {
2380 return -1;
2381 }
2382
2383 /* WAVE fmt header, BitsPerSample */
2384 data16 = (int16_t)sizeof(SAMPLE) * 8;
2385 nchars = save_file->write((const char *)&data16, 2);
2386 if (nchars != 2) {
2387 return -1;
2388 }
2389
2390 /* WAVE data header, Subchunk2ID 0x64617461 == 'data' */
2391 phton32(pd, 0x64617461);
2392 nchars = save_file->write((const char *)pd, 4);
2393 if (nchars != 4) {
2394 return -1;
2395 }
2396
2397 /* WAVE data header, Subchunk2Size */
2398 data32 = subchunk2Size;
2399 nchars = save_file->write((const char *)&data32, 4);
2400 if (nchars != 4) {
2401 return -1;
2402 }
2403
2404 /* Now we are ready for saving data */
2405
2406 return save_file->pos();
2407}
2408
2409bool RtpPlayerDialog::writeAudioSilenceSamples(QFile *out_file, qint64 samples, int stream_count)
2410{
2411 uint8_t pd[2];
2412
2413 phton16(pd, 0x0000);
2414 for(int s=0; s < stream_count; s++) {
2415 for(qint64 i=0; i < samples; i++) {
2416 if (sizeof(SAMPLE) != out_file->write((char *)&pd, sizeof(SAMPLE))) {
2417 return false;
2418 }
2419 }
2420 }
2421
2422 return true;
2423}
2424
2425bool RtpPlayerDialog::writeAudioStreamsSamples(QFile *out_file, QVector<RtpAudioStream *> streams, bool swap_bytes)
2426{
2427 SAMPLE sample;
2428 uint8_t pd[2];
2429
2430 // Did we read something in last cycle?
2431 bool read = true;
2432
2433 while (read) {
2434 read = false;
2435 // Loop over all streams, read one sample from each, write to output
2436 foreach(RtpAudioStream *audio_stream, streams)for (auto _container_2436 = QtPrivate::qMakeForeachContainer(
streams); _container_2436.i != _container_2436.e; ++_container_2436
.i) if (RtpAudioStream *audio_stream = *_container_2436.i; false
) {} else
{
2437 if (sizeof(sample) == audio_stream->readSample(&sample)) {
2438 if (swap_bytes) {
2439 // same as phton16(), but more clear in compare
2440 // to else branch
2441 pd[0] = (uint8_t)(sample >> 8);
2442 pd[1] = (uint8_t)(sample >> 0);
2443 } else {
2444 // just copy
2445 pd[1] = (uint8_t)(sample >> 8);
2446 pd[0] = (uint8_t)(sample >> 0);
2447 }
2448 read = true;
2449 } else {
2450 // for 0x0000 doesn't matter on order
2451 phton16(pd, 0x0000);
2452 }
2453 if (sizeof(sample) != out_file->write((char *)&pd, sizeof(sample))) {
2454 return false;
2455 }
2456 }
2457 }
2458
2459 return true;
2460}
2461
2462save_audio_t RtpPlayerDialog::selectFileAudioFormatAndName(QString *file_path)
2463{
2464 QString ext_filter = "";
2465 QString ext_filter_wav = tr("WAV (*.wav)");
2466 QString ext_filter_au = tr("Sun Audio (*.au)");
2467 ext_filter.append(ext_filter_wav);
2468 ext_filter.append(";;");
2469 ext_filter.append(ext_filter_au);
2470
2471 QString sel_filter;
2472 *file_path = WiresharkFileDialog::getSaveFileName(
2473 this, tr("Save audio"), mainApp->openDialogInitialDir().absoluteFilePath(""),
2474 ext_filter, &sel_filter);
2475
2476 if (file_path->isEmpty()) return save_audio_none;
2477
2478 save_audio_t save_format = save_audio_none;
2479 if (0 == QString::compare(sel_filter, ext_filter_au)) {
2480 save_format = save_audio_au;
2481 } else if (0 == QString::compare(sel_filter, ext_filter_wav)) {
2482 save_format = save_audio_wav;
2483 }
2484
2485 return save_format;
2486}
2487
2488save_payload_t RtpPlayerDialog::selectFilePayloadFormatAndName(QString *file_path)
2489{
2490 QString ext_filter = "";
2491 QString ext_filter_raw = tr("Raw (*.raw)");
2492 ext_filter.append(ext_filter_raw);
2493
2494 QString sel_filter;
2495 *file_path = WiresharkFileDialog::getSaveFileName(
2496 this, tr("Save payload"), mainApp->openDialogInitialDir().absoluteFilePath(""),
2497 ext_filter, &sel_filter);
2498
2499 if (file_path->isEmpty()) return save_payload_none;
2500
2501 save_payload_t save_format = save_payload_none;
2502 if (0 == QString::compare(sel_filter, ext_filter_raw)) {
2503 save_format = save_payload_data;
2504 }
2505
2506 return save_format;
2507}
2508
2509QVector<rtpstream_id_t *>RtpPlayerDialog::getSelectedRtpStreamIDs()
2510{
2511 QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();
2512 QVector<rtpstream_id_t *> ids;
2513
2514 if (items.count() > 0) {
2515 foreach(QTreeWidgetItem *ti, items)for (auto _container_2515 = QtPrivate::qMakeForeachContainer(
items); _container_2515.i != _container_2515.e; ++_container_2515
.i) if (QTreeWidgetItem *ti = *_container_2515.i; false) {} else
{
2516 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
2517 if (audio_stream) {
2518 ids << audio_stream->getID();
2519 }
2520 }
2521 }
2522
2523 return ids;
2524}
2525
2526QVector<RtpAudioStream *>RtpPlayerDialog::getSelectedAudibleNonmutedAudioStreams()
2527{
2528 QList<QTreeWidgetItem *> items = ui->streamTreeWidget->selectedItems();
2529 QVector<RtpAudioStream *> streams;
2530
2531 if (items.count() > 0) {
2532 foreach(QTreeWidgetItem *ti, items)for (auto _container_2532 = QtPrivate::qMakeForeachContainer(
items); _container_2532.i != _container_2532.e; ++_container_2532
.i) if (QTreeWidgetItem *ti = *_container_2532.i; false) {} else
{
2533 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
2534 // Ignore muted streams and streams with no audio
2535 if (audio_stream &&
2536 !audio_stream->getAudioRouting().isMuted() &&
2537 (audio_stream->sampleRate()>0)
2538 ) {
2539 streams << audio_stream;
2540 }
2541 }
2542 }
2543
2544 return streams;
2545}
2546
2547void RtpPlayerDialog::saveAudio(save_mode_t save_mode)
2548{
2549 qint64 minSilenceSamples;
2550 qint64 startSample;
2551 qint64 lead_silence_samples;
2552 qint64 maxSample;
2553 QString path;
2554 QVector<RtpAudioStream *>streams;
2555
2556 streams = getSelectedAudibleNonmutedAudioStreams();
2557 if (streams.count() < 1) {
2558 QMessageBox::warning(this, tr("Warning"), tr("No stream selected or none of selected streams provide audio"));
2559 return;
2560 }
2561
2562 unsigned save_audio_rate = streams[0]->playRate();
2563 // Check whether all streams use same audio rate
2564 foreach(RtpAudioStream *audio_stream, streams)for (auto _container_2564 = QtPrivate::qMakeForeachContainer(
streams); _container_2564.i != _container_2564.e; ++_container_2564
.i) if (RtpAudioStream *audio_stream = *_container_2564.i; false
) {} else
{
2565 if (save_audio_rate != audio_stream->playRate()) {
2566 QMessageBox::warning(this, tr("Error"), tr("All selected streams must use same play rate. Manual set of Output Audio Rate might help."));
2567 return;
2568 }
2569 }
2570
2571 save_audio_t format = selectFileAudioFormatAndName(&path);
2572 if (format == save_audio_none) return;
2573
2574 // Use start silence and length of first stream
2575 minSilenceSamples = streams[0]->getLeadSilenceSamples();
2576 maxSample = streams[0]->getTotalSamples();
2577 // Find shortest start silence and longest stream
2578 foreach(RtpAudioStream *audio_stream, streams)for (auto _container_2578 = QtPrivate::qMakeForeachContainer(
streams); _container_2578.i != _container_2578.e; ++_container_2578
.i) if (RtpAudioStream *audio_stream = *_container_2578.i; false
) {} else
{
2579 if (minSilenceSamples > audio_stream->getLeadSilenceSamples()) {
2580 minSilenceSamples = audio_stream->getLeadSilenceSamples();
2581 }
2582 if (maxSample < audio_stream->getTotalSamples()) {
2583 maxSample = audio_stream->getTotalSamples();
2584 }
2585 }
2586
2587 switch (save_mode) {
2588 case save_mode_from_cursor:
2589 if (ui->todCheckBox->isChecked()) {
2590 startSample = start_marker_time_ * save_audio_rate;
2591 } else {
2592 startSample = (start_marker_time_ - first_stream_rel_start_time_) * save_audio_rate;
2593 }
2594 lead_silence_samples = 0;
2595 break;
2596 case save_mode_sync_stream:
2597 // Skip start of first stream, no lead silence
2598 startSample = minSilenceSamples;
2599 lead_silence_samples = 0;
2600 break;
2601 case save_mode_sync_file:
2602 default:
2603 // Full first stream, lead silence
2604 startSample = 0;
2605 lead_silence_samples = first_stream_rel_start_time_ * save_audio_rate;
2606 break;
2607 }
2608
2609 QVector<RtpAudioStream *>temp = QVector<RtpAudioStream *>(streams);
2610
2611 // Remove streams shorter than startSample and
2612 // seek to correct start for longer ones
2613 foreach(RtpAudioStream *audio_stream, temp)for (auto _container_2613 = QtPrivate::qMakeForeachContainer(
temp); _container_2613.i != _container_2613.e; ++_container_2613
.i) if (RtpAudioStream *audio_stream = *_container_2613.i; false
) {} else
{
2614 if (startSample > audio_stream->getTotalSamples()) {
2615 streams.removeAll(audio_stream);
2616 } else {
2617 audio_stream->seekSample(startSample);
2618 }
2619 }
2620
2621 if (streams.count() < 1) {
2622 QMessageBox::warning(this, tr("Warning"), tr("No streams are suitable for save"));
2623 return;
2624 }
2625
2626 QFile file(path);
2627 file.open(QIODevice::WriteOnly);
2628
2629 if (!file.isOpen() || (file.error() != QFile::NoError)) {
2630 QMessageBox::warning(this, tr("Warning"), tr("Save failed!"));
2631 } else {
2632 switch (format) {
2633 case save_audio_au:
2634 if (-1 == saveAudioHeaderAU(&file, static_cast<quint32>(streams.count()), save_audio_rate)) {
2635 QMessageBox::warning(this, tr("Error"), tr("Can't write header of AU file"));
2636 return;
2637 }
2638 if (lead_silence_samples > 0) {
2639 if (!writeAudioSilenceSamples(&file, lead_silence_samples, static_cast<int>(streams.count()))) {
2640 QMessageBox::warning(this, tr("Warning"), tr("Save failed!"));
2641 }
2642 }
2643 if (!writeAudioStreamsSamples(&file, streams, true)) {
2644 QMessageBox::warning(this, tr("Warning"), tr("Save failed!"));
2645 }
2646 break;
2647 case save_audio_wav:
2648 if (-1 == saveAudioHeaderWAV(&file, static_cast<quint32>(streams.count()), save_audio_rate, (maxSample - startSample) + lead_silence_samples)) {
2649 QMessageBox::warning(this, tr("Error"), tr("Can't write header of WAV file"));
2650 return;
2651 }
2652 if (lead_silence_samples > 0) {
2653 if (!writeAudioSilenceSamples(&file, lead_silence_samples, static_cast<int>(streams.count()))) {
2654 QMessageBox::warning(this, tr("Warning"), tr("Save failed!"));
2655 }
2656 }
2657 if (!writeAudioStreamsSamples(&file, streams, false)) {
2658 QMessageBox::warning(this, tr("Warning"), tr("Save failed!"));
2659 }
2660 break;
2661 case save_audio_none:
2662 break;
2663 }
2664 }
2665
2666 file.close();
2667}
2668
2669void RtpPlayerDialog::savePayload()
2670{
2671 QString path;
2672 QList<QTreeWidgetItem *> items;
2673 RtpAudioStream *audio_stream = NULL__null;
2674
2675 items = ui->streamTreeWidget->selectedItems();
2676 foreach(QTreeWidgetItem *ti, items)for (auto _container_2676 = QtPrivate::qMakeForeachContainer(
items); _container_2676.i != _container_2676.e; ++_container_2676
.i) if (QTreeWidgetItem *ti = *_container_2676.i; false) {} else
{
2677 audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
2678 if (audio_stream)
2679 break;
2680 }
2681 if (items.count() != 1 || !audio_stream) {
2682 QMessageBox::warning(this, tr("Warning"), tr("Payload save works with just one audio stream."));
2683 return;
2684 }
2685
2686 save_payload_t format = selectFilePayloadFormatAndName(&path);
2687 if (format == save_payload_none) return;
2688
2689 QFile file(path);
2690 file.open(QIODevice::WriteOnly);
2691
2692 if (!file.isOpen() || (file.error() != QFile::NoError)) {
2693 QMessageBox::warning(this, tr("Warning"), tr("Save failed!"));
2694 } else if (!audio_stream->savePayload(&file)) {
2695 QMessageBox::warning(this, tr("Warning"), tr("Save failed!"));
2696 }
2697
2698 file.close();
2699}
2700
2701void RtpPlayerDialog::on_actionSaveAudioFromCursor_triggered()
2702{
2703 saveAudio(save_mode_from_cursor);
2704}
2705
2706void RtpPlayerDialog::on_actionSaveAudioSyncStream_triggered()
2707{
2708 saveAudio(save_mode_sync_stream);
2709}
2710
2711void RtpPlayerDialog::on_actionSaveAudioSyncFile_triggered()
2712{
2713 saveAudio(save_mode_sync_file);
2714}
2715
2716void RtpPlayerDialog::on_actionSavePayload_triggered()
2717{
2718 savePayload();
2719}
2720
2721void RtpPlayerDialog::selectInaudible(bool select)
2722{
2723 block_redraw_ = true;
2724 ui->streamTreeWidget->blockSignals(true);
2725 for (int row = 0; row < ui->streamTreeWidget->topLevelItemCount(); row++) {
2726 QTreeWidgetItem *ti = ui->streamTreeWidget->topLevelItem(row);
2727 RtpAudioStream *audio_stream = ti->data(stream_data_col_, Qt::UserRole).value<RtpAudioStream*>();
2728 // Streams with no audio
2729 if (audio_stream && (audio_stream->sampleRate()==0)) {
2730 ti->setSelected(select);
2731 }
2732 }
2733 ui->streamTreeWidget->blockSignals(false);
2734 block_redraw_ = false;
2735 ui->audioPlot->replot();
2736 updateHintLabel();
2737}
2738
2739void RtpPlayerDialog::on_actionSelectInaudible_triggered()
2740{
2741 selectInaudible(true);
2742}
2743
2744void RtpPlayerDialog::on_actionDeselectInaudible_triggered()
2745{
2746 selectInaudible(false);
2747}
2748
2749void RtpPlayerDialog::on_actionPrepareFilter_triggered()
2750{
2751 QVector<rtpstream_id_t *> ids = getSelectedRtpStreamIDs();
2752 QString filter = make_filter_based_on_rtpstream_id(ids);
2753 if (filter.length() > 0) {
2754 emit updateFilter(filter);
2755 }
2756}
2757
2758void RtpPlayerDialog::rtpAnalysisReplace()
2759{
2760 if (ui->streamTreeWidget->selectedItems().count() < 1) return;
2761
2762 emit rtpAnalysisDialogReplaceRtpStreams(getSelectedRtpStreamIDs());
2763}
2764
2765void RtpPlayerDialog::rtpAnalysisAdd()
2766{
2767 if (ui->streamTreeWidget->selectedItems().count() < 1) return;
2768
2769 emit rtpAnalysisDialogAddRtpStreams(getSelectedRtpStreamIDs());
2770}
2771
2772void RtpPlayerDialog::rtpAnalysisRemove()
2773{
2774 if (ui->streamTreeWidget->selectedItems().count() < 1) return;
2775
2776 emit rtpAnalysisDialogRemoveRtpStreams(getSelectedRtpStreamIDs());
2777}
2778
2779void RtpPlayerDialog::on_actionReadCapture_triggered()
2780{
2781#ifdef QT_MULTIMEDIA_LIB1
2782 QTimer::singleShot(0, this, SLOT(retapPackets())qFlagLocation("1" "retapPackets()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "2782")
);
2783#endif
2784}
2785
2786// _U_ is used for case w have no LIBPCAP
2787void RtpPlayerDialog::captureEvent(CaptureEvent e _U___attribute__((unused)))
2788{
2789#ifdef HAVE_LIBPCAP1
2790 bool new_read_capture_enabled = false;
2791 bool found = false;
2792
2793 if ((e.captureContext() & CaptureEvent::Capture) &&
2794 (e.eventType() == CaptureEvent::Prepared)
2795 ) {
2796 new_read_capture_enabled = true;
2797 found = true;
2798 } else if ((e.captureContext() & CaptureEvent::Capture) &&
2799 (e.eventType() == CaptureEvent::Finished)
2800 ) {
2801 new_read_capture_enabled = false;
2802 found = true;
2803 }
2804
2805 if (found) {
2806 bool retap = false;
2807 if (read_capture_enabled_ && !new_read_capture_enabled) {
2808 // Capturing ended, automatically refresh data
2809 retap = true;
2810 }
2811 read_capture_enabled_ = new_read_capture_enabled;
2812 updateWidgets();
2813 if (retap) {
2814 QTimer::singleShot(0, this, SLOT(retapPackets())qFlagLocation("1" "retapPackets()" "\0" "ui/qt/rtp_player_dialog.cpp"
":" "2814")
);
2815 }
2816 }
2817#endif
2818}
2819
2820#endif // QT_MULTIMEDIA_LIB

/usr/include/x86_64-linux-gnu/qt6/QtCore/qpointer.h

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-only
3
4#ifndef QPOINTER_H
5#define QPOINTER_H
6
7#include <QtCore/qsharedpointer.h>
8#include <QtCore/qtypeinfo.h>
9
10#ifndef QT_NO_QOBJECT
11
12QT_BEGIN_NAMESPACE
13
14class QVariant;
15
16template <class T>
17class QPointer
18{
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_QDOC
31 // Stop qdoc from complaining about missing function
32 ~QPointer();
33#endif
34
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() const
41 { return static_cast<T*>(wp.internalData()); }
42 inline T* get() const
43 { return data(); }
44 inline T* operator->() const
45 { return data(); }
46 inline T& operator*() const
47 { return *data(); }
48 inline operator T*() const
49 { return data(); }
50
51 inline bool isNull() const
52 { 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_SET
77#undef DECLARE_TEMPLATE_COMPARE_SET
78};
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) noexcept
91{ p1.swap(p2); }
92
93QT_END_NAMESPACE
94
95#endif // QT_NO_QOBJECT
96
97#endif // QPOINTER_H

/usr/include/x86_64-linux-gnu/qt6/QtCore/qsharedpointer_impl.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-only
5
6#ifndef Q_QDOC
7
8#ifndef QSHAREDPOINTER_H
9#error Do not include qsharedpointer_impl.h directly
10#endif
11
12#if 0
13#pragma qt_sync_skip_header_check
14#pragma qt_sync_stop_processing
15#endif
16
17#if 0
18// These macros are duplicated here to make syncqt not complain a about
19// this header, as we have a "qt_sync_stop_processing" below, which in turn
20// is here because this file contains a template mess and duplicates the
21// classes found in qsharedpointer.h
22QT_BEGIN_NAMESPACE
23QT_END_NAMESPACE
24#pragma qt_sync_stop_processing
25#endif
26
27#include <new>
28#include <QtCore/qatomic.h>
29#include <QtCore/qhashfunctions.h>
30#include <QtCore/qmetatype.h> // for IsPointerToTypeDerivedFromQObject
31
32#include <memory>
33
34QT_BEGIN_NAMESPACE
35
36class QObject;
37template <class T>
38T qobject_cast(const QObject *object);
39
40//
41// forward declarations
42//
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_QOBJECT
57template <class X, class T>
58QSharedPointer<X> qSharedPointerObjectCast(const QSharedPointer<T> &ptr);
59#endif
60
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 pointers
71 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 specialization
83 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 inner
91 // reference counter, and it tracks the lifetime of the pointer itself.
92 // "weakref" is the outer reference counter and it tracks the lifetime of
93 // the ExternalRefCountData object.
94 //
95 // The deleter is stored in the destroyer member and is always a pointer to
96 // a static function in ExternalRefCountWithCustomDeleter or in
97 // ExternalRefCountWithContiguousData
98 struct ExternalRefCountData
99 {
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_QOBJECT
117 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#endif
121 inline void checkQObjectShared(...) { }
122 inline void setQObjectShared(...) { }
123
124 // Normally, only subclasses of ExternalRefCountData are allocated
125 // One exception exists in getAndRef; that uses the global operator new
126 // to prevent a mismatch with the custom operator delete
127 inline void *operator new(std::size_t) = delete;
128 // placement new
129 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 CustomDeleter
137 {
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*) + padding
145 // for Deleter = stateless functor: 8 (32-bit) / 16 (64-bit) due to padding
146 // 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 NormalDeleter
150 // is an optimization: instead of storing a pointer to a function that does
151 // 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 implements
163 // the static function that deletes the object. The pointer and the
164 // custom deleter are kept in the "extra" member so we can construct
165 // and destruct it independently of the full structure.
166 template <class T, typename Deleter>
167 struct ExternalRefCountWithCustomDeleter: public ExternalRefCountData
168 {
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 too
179 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-objects
192 new (&d->extra) CustomDeleter<T, Deleter>(ptr, userDeleter);
193 new (d) BaseClass(actualDeleter); // can't throw
194
195 return d;
196 }
197 private:
198 // prevent construction
199 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 allocate
206 // memory for both QSharedPointer's d-pointer and the actual object being
207 // tracked.
208 template <class T>
209 struct ExternalRefCountWithContiguousData: public ExternalRefCountData
210 {
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 destructor
221 }
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-object
235 // leave d->data uninitialized
236 new (d) Parent(destroy); // can't throw
237
238 *ptr = &d->data;
239 return d;
240 }
241
242 private:
243 // prevent construction
244 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_QOBJECT
250 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#endif
253} // namespace QtSharedPointer
254
255template <class T> class QSharedPointer
256{
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) // noexcept
286 { internalConstruct(ptr, QtSharedPointer::NormalDeleter()); }
287
288 template <class X, typename Deleter, IfCompatible<X> = true>
289 inline QSharedPointer(X *ptr, Deleter deleter) : value(ptr) // throws
290 { 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) noexcept
299 {
300 QSharedPointer copy(other);
301 swap(copy);
302 return *this;
303 }
304 QSharedPointer(QSharedPointer &&other) noexcept
305 : 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) noexcept
314 : 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) noexcept
322 {
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) noexcept
349 { 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() const
360 {
361 return qSharedPointerCast<X, T>(*this);
362 }
363
364 template <class X>
365 QSharedPointer<X> dynamicCast() const
366 {
367 return qSharedPointerDynamicCast<X, T>(*this);
368 }
369
370 template <class X>
371 QSharedPointer<X> constCast() const
372 {
373 return qSharedPointerConstCast<X, T>(*this);
374 }
375
376#ifndef QT_NO_QOBJECT
377 template <class X>
378 QSharedPointer<X> objectCast() const
379 {
380 return qSharedPointerObjectCast<X, T>(*this);
381 }
382#endif
383
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_POINTERS
393 typename Private::DestroyerFn destroy = &Private::safetyCheckDeleter;
394# else
395 typename Private::DestroyerFn destroy = &Private::deleter;
396# endif
397 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 data
403 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_POINTERS
408 internalSafetyCheckAdd(result.d, result.value);
409# endif
410 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_SET
434#undef DECLARE_COMPARE_SET
435
436private:
437 explicit QSharedPointer(Qt::Initialization) {}
438
439 void deref() noexcept
440 { deref(d); }
441 static void deref(Data *dd) noexcept
442 {
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_POINTERS
464 typename Private::DestroyerFn actualDeleter = &Private::safetyCheckDeleter;
465# else
466 typename Private::DestroyerFn actualDeleter = &Private::deleter;
467# endif
468 d = Private::create(ptr, deleter, actualDeleter);
469
470#ifdef QT_SHAREDPOINTER_TRACK_POINTERS
471 internalSafetyCheckAdd(d, ptr);
472#endif
473 d->setQObjectShared(ptr, true);
474 enableSharedFromThis(ptr);
475 }
476
477 void internalSwap(QSharedPointer &other) noexcept
478 {
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 zero
492 // 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; // succeeded
498 tmp = o->strongref.loadRelaxed(); // failed, try again
499 }
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 data
515 deref(o);
516 }
517
518 Type *value;
519 Data *d;
520};
521
522template <class T>
523class QWeakPointer
524{
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) noexcept
548 : 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) noexcept
557 : 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) noexcept
565 {
566 QWeakPointer moved(std::move(other));
567 swap(moved);
568 return *this;
569 }
570
571 QWeakPointer &operator=(const QWeakPointer &other) noexcept
572 {
573 QWeakPointer copy(other);
574 swap(copy);
575 return *this;
576 }
577
578 void swap(QWeakPointer &other) noexcept
579 {
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 table
600 // so force the operation to go through QSharedPointer
601 *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 noexcept
624 { return d == o.d && value == static_cast<const T *>(o.value); }
625
626 template <class X>
627 bool operator!=(const QWeakPointer<X> &o) const noexcept
628 { return !(*this == o); }
629
630 template <class X>
631 bool operator==(const QSharedPointer<X> &o) const noexcept
632 { return d == o.d; }
633
634 template <class X>
635 bool operator!=(const QSharedPointer<X> &o) const noexcept
636 { return !(*this == o); }
637
638 template <typename X>
639 friend bool operator==(const QSharedPointer<X> &p1, const QWeakPointer &p2) noexcept
640 { return p2 == p1; }
641 template <typename X>
642 friend bool operator!=(const QSharedPointer<X> &p1, const QWeakPointer &p2) noexcept
643 { 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_QOBJECT
665 template <class X, IfCompatible<X> = true>
666 inline QWeakPointer(X *ptr, bool) : d(ptr ? Data::getAndRef(ptr) : nullptr), value(ptr)
667 { }
668#endif
669
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 ever
682 // access a weak pointer's data but the weak pointer itself
683 inline T *internalData() const noexcept
684 {
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 making
698// EnableInternalData a dependent name:
699template <typename T>
700struct EnableInternalDataWrap : EnableInternalData {};
701}
702
703template <class T>
704class QEnableSharedFromThis
705{
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) const
719 {
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// qHash
769//
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() const
779{
780 return QWeakPointer<T>(*this);
781}
782
783template <class T>
784inline void swap(QSharedPointer<T> &p1, QSharedPointer<T> &p2) noexcept
785{ p1.swap(p2); }
786
787template <class T>
788inline void swap(QWeakPointer<T> &p1, QWeakPointer<T> &p2) noexcept
789{ 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 operators
803template <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 invalid
807 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 invalid
819 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 invalid
833 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_TEMPLATEinline
843QWeakPointer<X> qWeakPointerCast(const QSharedPointer<T> &src)
844{
845 return qSharedPointerCast<X, T>(src).toWeakRef();
846}
847
848#ifndef QT_NO_QOBJECT
849template <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 for
875/// 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 helpers
890
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#endif
926
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_NAMESPACE
932
933#endif