1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the examples of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:BSD$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** BSD License Usage |
18 | ** Alternatively, you may use this file under the terms of the BSD license |
19 | ** as follows: |
20 | ** |
21 | ** "Redistribution and use in source and binary forms, with or without |
22 | ** modification, are permitted provided that the following conditions are |
23 | ** met: |
24 | ** * Redistributions of source code must retain the above copyright |
25 | ** notice, this list of conditions and the following disclaimer. |
26 | ** * Redistributions in binary form must reproduce the above copyright |
27 | ** notice, this list of conditions and the following disclaimer in |
28 | ** the documentation and/or other materials provided with the |
29 | ** distribution. |
30 | ** * Neither the name of The Qt Company Ltd nor the names of its |
31 | ** contributors may be used to endorse or promote products derived |
32 | ** from this software without specific prior written permission. |
33 | ** |
34 | ** |
35 | ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
36 | ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
37 | ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
38 | ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
39 | ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
40 | ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
41 | ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
42 | ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
43 | ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
44 | ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
45 | ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
46 | ** |
47 | ** $QT_END_LICENSE$ |
48 | ** |
49 | ****************************************************************************/ |
50 | |
51 | #include "player.h" |
52 | |
53 | #include "playercontrols.h" |
54 | #include "playlistmodel.h" |
55 | #include "histogramwidget.h" |
56 | #include "videowidget.h" |
57 | |
58 | #include <QMediaService> |
59 | #include <QMediaPlaylist> |
60 | #include <QVideoProbe> |
61 | #include <QAudioProbe> |
62 | #include <QMediaMetaData> |
63 | #include <QtWidgets> |
64 | |
65 | Player::Player(QWidget *parent) |
66 | : QWidget(parent) |
67 | { |
68 | //! [create-objs] |
69 | m_player = new QMediaPlayer(this); |
70 | m_player->setAudioRole(QAudio::VideoRole); |
71 | qInfo() << "Supported audio roles:" ; |
72 | for (QAudio::Role role : m_player->supportedAudioRoles()) |
73 | qInfo() << " " << role; |
74 | // owned by PlaylistModel |
75 | m_playlist = new QMediaPlaylist(); |
76 | m_player->setPlaylist(m_playlist); |
77 | //! [create-objs] |
78 | |
79 | connect(sender: m_player, signal: &QMediaPlayer::durationChanged, receiver: this, slot: &Player::durationChanged); |
80 | connect(sender: m_player, signal: &QMediaPlayer::positionChanged, receiver: this, slot: &Player::positionChanged); |
81 | connect(sender: m_player, signal: QOverload<>::of(ptr: &QMediaPlayer::metaDataChanged), receiver: this, slot: &Player::metaDataChanged); |
82 | connect(sender: m_playlist, signal: &QMediaPlaylist::currentIndexChanged, receiver: this, slot: &Player::playlistPositionChanged); |
83 | connect(sender: m_player, signal: &QMediaPlayer::mediaStatusChanged, receiver: this, slot: &Player::statusChanged); |
84 | connect(sender: m_player, signal: &QMediaPlayer::bufferStatusChanged, receiver: this, slot: &Player::bufferingProgress); |
85 | connect(sender: m_player, signal: &QMediaPlayer::videoAvailableChanged, receiver: this, slot: &Player::videoAvailableChanged); |
86 | connect(sender: m_player, signal: QOverload<QMediaPlayer::Error>::of(ptr: &QMediaPlayer::error), receiver: this, slot: &Player::displayErrorMessage); |
87 | connect(sender: m_player, signal: &QMediaPlayer::stateChanged, receiver: this, slot: &Player::stateChanged); |
88 | |
89 | //! [2] |
90 | m_videoWidget = new VideoWidget(this); |
91 | m_player->setVideoOutput(m_videoWidget); |
92 | |
93 | m_playlistModel = new PlaylistModel(this); |
94 | m_playlistModel->setPlaylist(m_playlist); |
95 | //! [2] |
96 | |
97 | m_playlistView = new QListView(this); |
98 | m_playlistView->setModel(m_playlistModel); |
99 | m_playlistView->setCurrentIndex(m_playlistModel->index(row: m_playlist->currentIndex(), column: 0)); |
100 | |
101 | connect(sender: m_playlistView, signal: &QAbstractItemView::activated, receiver: this, slot: &Player::jump); |
102 | |
103 | m_slider = new QSlider(Qt::Horizontal, this); |
104 | m_slider->setRange(min: 0, max: m_player->duration() / 1000); |
105 | |
106 | m_labelDuration = new QLabel(this); |
107 | connect(sender: m_slider, signal: &QSlider::sliderMoved, receiver: this, slot: &Player::seek); |
108 | |
109 | m_labelHistogram = new QLabel(this); |
110 | m_labelHistogram->setText("Histogram:" ); |
111 | m_videoHistogram = new HistogramWidget(this); |
112 | m_audioHistogram = new HistogramWidget(this); |
113 | QHBoxLayout *histogramLayout = new QHBoxLayout; |
114 | histogramLayout->addWidget(m_labelHistogram); |
115 | histogramLayout->addWidget(m_videoHistogram, stretch: 1); |
116 | histogramLayout->addWidget(m_audioHistogram, stretch: 2); |
117 | |
118 | m_videoProbe = new QVideoProbe(this); |
119 | connect(sender: m_videoProbe, signal: &QVideoProbe::videoFrameProbed, receiver: m_videoHistogram, slot: &HistogramWidget::processFrame); |
120 | m_videoProbe->setSource(m_player); |
121 | |
122 | m_audioProbe = new QAudioProbe(this); |
123 | connect(sender: m_audioProbe, signal: &QAudioProbe::audioBufferProbed, receiver: m_audioHistogram, slot: &HistogramWidget::processBuffer); |
124 | m_audioProbe->setSource(m_player); |
125 | |
126 | QPushButton *openButton = new QPushButton(tr(s: "Open" ), this); |
127 | |
128 | connect(sender: openButton, signal: &QPushButton::clicked, receiver: this, slot: &Player::open); |
129 | |
130 | PlayerControls *controls = new PlayerControls(this); |
131 | controls->setState(m_player->state()); |
132 | controls->setVolume(m_player->volume()); |
133 | controls->setMuted(controls->isMuted()); |
134 | |
135 | connect(sender: controls, signal: &PlayerControls::play, receiver: m_player, slot: &QMediaPlayer::play); |
136 | connect(sender: controls, signal: &PlayerControls::pause, receiver: m_player, slot: &QMediaPlayer::pause); |
137 | connect(sender: controls, signal: &PlayerControls::stop, receiver: m_player, slot: &QMediaPlayer::stop); |
138 | connect(sender: controls, signal: &PlayerControls::next, receiver: m_playlist, slot: &QMediaPlaylist::next); |
139 | connect(sender: controls, signal: &PlayerControls::previous, receiver: this, slot: &Player::previousClicked); |
140 | connect(sender: controls, signal: &PlayerControls::changeVolume, receiver: m_player, slot: &QMediaPlayer::setVolume); |
141 | connect(sender: controls, signal: &PlayerControls::changeMuting, receiver: m_player, slot: &QMediaPlayer::setMuted); |
142 | connect(sender: controls, signal: &PlayerControls::changeRate, receiver: m_player, slot: &QMediaPlayer::setPlaybackRate); |
143 | connect(sender: controls, signal: &PlayerControls::stop, receiver: m_videoWidget, slot: QOverload<>::of(ptr: &QVideoWidget::update)); |
144 | |
145 | connect(sender: m_player, signal: &QMediaPlayer::stateChanged, receiver: controls, slot: &PlayerControls::setState); |
146 | connect(sender: m_player, signal: &QMediaPlayer::volumeChanged, receiver: controls, slot: &PlayerControls::setVolume); |
147 | connect(sender: m_player, signal: &QMediaPlayer::mutedChanged, receiver: controls, slot: &PlayerControls::setMuted); |
148 | |
149 | m_fullScreenButton = new QPushButton(tr(s: "FullScreen" ), this); |
150 | m_fullScreenButton->setCheckable(true); |
151 | |
152 | m_colorButton = new QPushButton(tr(s: "Color Options..." ), this); |
153 | m_colorButton->setEnabled(false); |
154 | connect(sender: m_colorButton, signal: &QPushButton::clicked, receiver: this, slot: &Player::showColorDialog); |
155 | |
156 | QBoxLayout *displayLayout = new QHBoxLayout; |
157 | displayLayout->addWidget(m_videoWidget, stretch: 2); |
158 | displayLayout->addWidget(m_playlistView); |
159 | |
160 | QBoxLayout *controlLayout = new QHBoxLayout; |
161 | controlLayout->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0); |
162 | controlLayout->addWidget(openButton); |
163 | controlLayout->addStretch(stretch: 1); |
164 | controlLayout->addWidget(controls); |
165 | controlLayout->addStretch(stretch: 1); |
166 | controlLayout->addWidget(m_fullScreenButton); |
167 | controlLayout->addWidget(m_colorButton); |
168 | |
169 | QBoxLayout *layout = new QVBoxLayout; |
170 | layout->addLayout(layout: displayLayout); |
171 | QHBoxLayout *hLayout = new QHBoxLayout; |
172 | hLayout->addWidget(m_slider); |
173 | hLayout->addWidget(m_labelDuration); |
174 | layout->addLayout(layout: hLayout); |
175 | layout->addLayout(layout: controlLayout); |
176 | layout->addLayout(layout: histogramLayout); |
177 | #if defined(Q_OS_QNX) |
178 | // On QNX, the main window doesn't have a title bar (or any other decorations). |
179 | // Create a status bar for the status information instead. |
180 | m_statusLabel = new QLabel; |
181 | m_statusBar = new QStatusBar; |
182 | m_statusBar->addPermanentWidget(m_statusLabel); |
183 | m_statusBar->setSizeGripEnabled(false); // Without mouse grabbing, it doesn't work very well. |
184 | layout->addWidget(m_statusBar); |
185 | #endif |
186 | |
187 | setLayout(layout); |
188 | |
189 | if (!isPlayerAvailable()) { |
190 | QMessageBox::warning(parent: this, title: tr(s: "Service not available" ), |
191 | text: tr(s: "The QMediaPlayer object does not have a valid service.\n" \ |
192 | "Please check the media service plugins are installed." )); |
193 | |
194 | controls->setEnabled(false); |
195 | m_playlistView->setEnabled(false); |
196 | openButton->setEnabled(false); |
197 | m_colorButton->setEnabled(false); |
198 | m_fullScreenButton->setEnabled(false); |
199 | } |
200 | |
201 | metaDataChanged(); |
202 | } |
203 | |
204 | Player::~Player() |
205 | { |
206 | } |
207 | |
208 | bool Player::isPlayerAvailable() const |
209 | { |
210 | return m_player->isAvailable(); |
211 | } |
212 | |
213 | void Player::open() |
214 | { |
215 | QFileDialog fileDialog(this); |
216 | fileDialog.setAcceptMode(QFileDialog::AcceptOpen); |
217 | fileDialog.setWindowTitle(tr(s: "Open Files" )); |
218 | QStringList supportedMimeTypes = m_player->supportedMimeTypes(); |
219 | if (!supportedMimeTypes.isEmpty()) { |
220 | supportedMimeTypes.append(t: "audio/x-m3u" ); // MP3 playlists |
221 | fileDialog.setMimeTypeFilters(supportedMimeTypes); |
222 | } |
223 | fileDialog.setDirectory(QStandardPaths::standardLocations(type: QStandardPaths::MoviesLocation).value(i: 0, defaultValue: QDir::homePath())); |
224 | if (fileDialog.exec() == QDialog::Accepted) |
225 | addToPlaylist(urls: fileDialog.selectedUrls()); |
226 | } |
227 | |
228 | static bool isPlaylist(const QUrl &url) // Check for ".m3u" playlists. |
229 | { |
230 | if (!url.isLocalFile()) |
231 | return false; |
232 | const QFileInfo fileInfo(url.toLocalFile()); |
233 | return fileInfo.exists() && !fileInfo.suffix().compare(other: QLatin1String("m3u" ), cs: Qt::CaseInsensitive); |
234 | } |
235 | |
236 | void Player::addToPlaylist(const QList<QUrl> &urls) |
237 | { |
238 | for (auto &url: urls) { |
239 | if (isPlaylist(url)) |
240 | m_playlist->load(location: url); |
241 | else |
242 | m_playlist->addMedia(content: url); |
243 | } |
244 | } |
245 | |
246 | void Player::setCustomAudioRole(const QString &role) |
247 | { |
248 | m_player->setCustomAudioRole(role); |
249 | } |
250 | |
251 | void Player::durationChanged(qint64 duration) |
252 | { |
253 | m_duration = duration / 1000; |
254 | m_slider->setMaximum(m_duration); |
255 | } |
256 | |
257 | void Player::positionChanged(qint64 progress) |
258 | { |
259 | if (!m_slider->isSliderDown()) |
260 | m_slider->setValue(progress / 1000); |
261 | |
262 | updateDurationInfo(currentInfo: progress / 1000); |
263 | } |
264 | |
265 | void Player::metaDataChanged() |
266 | { |
267 | if (m_player->isMetaDataAvailable()) { |
268 | setTrackInfo(QString("%1 - %2" ) |
269 | .arg(a: m_player->metaData(key: QMediaMetaData::AlbumArtist).toString()) |
270 | .arg(a: m_player->metaData(key: QMediaMetaData::Title).toString())); |
271 | |
272 | if (m_coverLabel) { |
273 | QUrl url = m_player->metaData(key: QMediaMetaData::CoverArtUrlLarge).value<QUrl>(); |
274 | |
275 | m_coverLabel->setPixmap(!url.isEmpty() |
276 | ? QPixmap(url.toString()) |
277 | : QPixmap()); |
278 | } |
279 | } |
280 | } |
281 | |
282 | void Player::previousClicked() |
283 | { |
284 | // Go to previous track if we are within the first 5 seconds of playback |
285 | // Otherwise, seek to the beginning. |
286 | if (m_player->position() <= 5000) |
287 | m_playlist->previous(); |
288 | else |
289 | m_player->setPosition(0); |
290 | } |
291 | |
292 | void Player::jump(const QModelIndex &index) |
293 | { |
294 | if (index.isValid()) { |
295 | m_playlist->setCurrentIndex(index.row()); |
296 | m_player->play(); |
297 | } |
298 | } |
299 | |
300 | void Player::playlistPositionChanged(int currentItem) |
301 | { |
302 | clearHistogram(); |
303 | m_playlistView->setCurrentIndex(m_playlistModel->index(row: currentItem, column: 0)); |
304 | } |
305 | |
306 | void Player::seek(int seconds) |
307 | { |
308 | m_player->setPosition(seconds * 1000); |
309 | } |
310 | |
311 | void Player::statusChanged(QMediaPlayer::MediaStatus status) |
312 | { |
313 | handleCursor(status); |
314 | |
315 | // handle status message |
316 | switch (status) { |
317 | case QMediaPlayer::UnknownMediaStatus: |
318 | case QMediaPlayer::NoMedia: |
319 | case QMediaPlayer::LoadedMedia: |
320 | setStatusInfo(QString()); |
321 | break; |
322 | case QMediaPlayer::LoadingMedia: |
323 | setStatusInfo(tr(s: "Loading..." )); |
324 | break; |
325 | case QMediaPlayer::BufferingMedia: |
326 | case QMediaPlayer::BufferedMedia: |
327 | setStatusInfo(tr(s: "Buffering %1%" ).arg(a: m_player->bufferStatus())); |
328 | break; |
329 | case QMediaPlayer::StalledMedia: |
330 | setStatusInfo(tr(s: "Stalled %1%" ).arg(a: m_player->bufferStatus())); |
331 | break; |
332 | case QMediaPlayer::EndOfMedia: |
333 | QApplication::alert(widget: this); |
334 | break; |
335 | case QMediaPlayer::InvalidMedia: |
336 | displayErrorMessage(); |
337 | break; |
338 | } |
339 | } |
340 | |
341 | void Player::stateChanged(QMediaPlayer::State state) |
342 | { |
343 | if (state == QMediaPlayer::StoppedState) |
344 | clearHistogram(); |
345 | } |
346 | |
347 | void Player::handleCursor(QMediaPlayer::MediaStatus status) |
348 | { |
349 | #ifndef QT_NO_CURSOR |
350 | if (status == QMediaPlayer::LoadingMedia || |
351 | status == QMediaPlayer::BufferingMedia || |
352 | status == QMediaPlayer::StalledMedia) |
353 | setCursor(QCursor(Qt::BusyCursor)); |
354 | else |
355 | unsetCursor(); |
356 | #endif |
357 | } |
358 | |
359 | void Player::bufferingProgress(int progress) |
360 | { |
361 | if (m_player->mediaStatus() == QMediaPlayer::StalledMedia) |
362 | setStatusInfo(tr(s: "Stalled %1%" ).arg(a: progress)); |
363 | else |
364 | setStatusInfo(tr(s: "Buffering %1%" ).arg(a: progress)); |
365 | } |
366 | |
367 | void Player::videoAvailableChanged(bool available) |
368 | { |
369 | if (!available) { |
370 | disconnect(sender: m_fullScreenButton, signal: &QPushButton::clicked, receiver: m_videoWidget, slot: &QVideoWidget::setFullScreen); |
371 | disconnect(sender: m_videoWidget, signal: &QVideoWidget::fullScreenChanged, receiver: m_fullScreenButton, slot: &QPushButton::setChecked); |
372 | m_videoWidget->setFullScreen(false); |
373 | } else { |
374 | connect(sender: m_fullScreenButton, signal: &QPushButton::clicked, receiver: m_videoWidget, slot: &QVideoWidget::setFullScreen); |
375 | connect(sender: m_videoWidget, signal: &QVideoWidget::fullScreenChanged, receiver: m_fullScreenButton, slot: &QPushButton::setChecked); |
376 | |
377 | if (m_fullScreenButton->isChecked()) |
378 | m_videoWidget->setFullScreen(true); |
379 | } |
380 | m_colorButton->setEnabled(available); |
381 | } |
382 | |
383 | void Player::setTrackInfo(const QString &info) |
384 | { |
385 | m_trackInfo = info; |
386 | |
387 | if (m_statusBar) { |
388 | m_statusBar->showMessage(text: m_trackInfo); |
389 | m_statusLabel->setText(m_statusInfo); |
390 | } else { |
391 | if (!m_statusInfo.isEmpty()) |
392 | setWindowTitle(QString("%1 | %2" ).arg(a: m_trackInfo).arg(a: m_statusInfo)); |
393 | else |
394 | setWindowTitle(m_trackInfo); |
395 | } |
396 | } |
397 | |
398 | void Player::setStatusInfo(const QString &info) |
399 | { |
400 | m_statusInfo = info; |
401 | |
402 | if (m_statusBar) { |
403 | m_statusBar->showMessage(text: m_trackInfo); |
404 | m_statusLabel->setText(m_statusInfo); |
405 | } else { |
406 | if (!m_statusInfo.isEmpty()) |
407 | setWindowTitle(QString("%1 | %2" ).arg(a: m_trackInfo).arg(a: m_statusInfo)); |
408 | else |
409 | setWindowTitle(m_trackInfo); |
410 | } |
411 | } |
412 | |
413 | void Player::displayErrorMessage() |
414 | { |
415 | setStatusInfo(m_player->errorString()); |
416 | } |
417 | |
418 | void Player::updateDurationInfo(qint64 currentInfo) |
419 | { |
420 | QString tStr; |
421 | if (currentInfo || m_duration) { |
422 | QTime currentTime((currentInfo / 3600) % 60, (currentInfo / 60) % 60, |
423 | currentInfo % 60, (currentInfo * 1000) % 1000); |
424 | QTime totalTime((m_duration / 3600) % 60, (m_duration / 60) % 60, |
425 | m_duration % 60, (m_duration * 1000) % 1000); |
426 | QString format = "mm:ss" ; |
427 | if (m_duration > 3600) |
428 | format = "hh:mm:ss" ; |
429 | tStr = currentTime.toString(format) + " / " + totalTime.toString(format); |
430 | } |
431 | m_labelDuration->setText(tStr); |
432 | } |
433 | |
434 | void Player::showColorDialog() |
435 | { |
436 | if (!m_colorDialog) { |
437 | QSlider *brightnessSlider = new QSlider(Qt::Horizontal); |
438 | brightnessSlider->setRange(min: -100, max: 100); |
439 | brightnessSlider->setValue(m_videoWidget->brightness()); |
440 | connect(sender: brightnessSlider, signal: &QSlider::sliderMoved, receiver: m_videoWidget, slot: &QVideoWidget::setBrightness); |
441 | connect(sender: m_videoWidget, signal: &QVideoWidget::brightnessChanged, receiver: brightnessSlider, slot: &QSlider::setValue); |
442 | |
443 | QSlider *contrastSlider = new QSlider(Qt::Horizontal); |
444 | contrastSlider->setRange(min: -100, max: 100); |
445 | contrastSlider->setValue(m_videoWidget->contrast()); |
446 | connect(sender: contrastSlider, signal: &QSlider::sliderMoved, receiver: m_videoWidget, slot: &QVideoWidget::setContrast); |
447 | connect(sender: m_videoWidget, signal: &QVideoWidget::contrastChanged, receiver: contrastSlider, slot: &QSlider::setValue); |
448 | |
449 | QSlider *hueSlider = new QSlider(Qt::Horizontal); |
450 | hueSlider->setRange(min: -100, max: 100); |
451 | hueSlider->setValue(m_videoWidget->hue()); |
452 | connect(sender: hueSlider, signal: &QSlider::sliderMoved, receiver: m_videoWidget, slot: &QVideoWidget::setHue); |
453 | connect(sender: m_videoWidget, signal: &QVideoWidget::hueChanged, receiver: hueSlider, slot: &QSlider::setValue); |
454 | |
455 | QSlider *saturationSlider = new QSlider(Qt::Horizontal); |
456 | saturationSlider->setRange(min: -100, max: 100); |
457 | saturationSlider->setValue(m_videoWidget->saturation()); |
458 | connect(sender: saturationSlider, signal: &QSlider::sliderMoved, receiver: m_videoWidget, slot: &QVideoWidget::setSaturation); |
459 | connect(sender: m_videoWidget, signal: &QVideoWidget::saturationChanged, receiver: saturationSlider, slot: &QSlider::setValue); |
460 | |
461 | QFormLayout *layout = new QFormLayout; |
462 | layout->addRow(labelText: tr(s: "Brightness" ), field: brightnessSlider); |
463 | layout->addRow(labelText: tr(s: "Contrast" ), field: contrastSlider); |
464 | layout->addRow(labelText: tr(s: "Hue" ), field: hueSlider); |
465 | layout->addRow(labelText: tr(s: "Saturation" ), field: saturationSlider); |
466 | |
467 | QPushButton *button = new QPushButton(tr(s: "Close" )); |
468 | layout->addRow(widget: button); |
469 | |
470 | m_colorDialog = new QDialog(this); |
471 | m_colorDialog->setWindowTitle(tr(s: "Color Options" )); |
472 | m_colorDialog->setLayout(layout); |
473 | |
474 | connect(sender: button, signal: &QPushButton::clicked, receiver: m_colorDialog, slot: &QDialog::close); |
475 | } |
476 | m_colorDialog->show(); |
477 | } |
478 | |
479 | void Player::clearHistogram() |
480 | { |
481 | QMetaObject::invokeMethod(obj: m_videoHistogram, member: "processFrame" , type: Qt::QueuedConnection, Q_ARG(QVideoFrame, QVideoFrame())); |
482 | QMetaObject::invokeMethod(obj: m_audioHistogram, member: "processBuffer" , type: Qt::QueuedConnection, Q_ARG(QAudioBuffer, QAudioBuffer())); |
483 | } |
484 | |