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 "qplayer.h"
52 #include <QtWidgets>
53 #include <QVideoSurfaceFormat>
54
55 #define MIME_IS_GIF(s) !(s).compare("image/gif")
56 #define MIME_IS_IMAGE(s) ((s).startsWith("image/") && !MIME_IS_GIF(s))
57 #define MIME_IS_VIDEO(s) ((s).startsWith("video/") || MIME_IS_GIF(s))
58 #define MIME_IS_AUDIO(s) (s).startsWith("audio/")
59
QPlayer()60 QPlayer::QPlayer()
61 : player(nullptr, QMediaPlayer::VideoSurface)
62 , list(nullptr)
63 , playButton(nullptr)
64 , positionSlider(nullptr)
65 {
66 exitButton = new QPushButton(tr("Exit"));
67 connect(exitButton, &QAbstractButton::clicked, this, &QPlayer::exit);
68
69 playButton = new QPushButton;
70 playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
71
72 connect(playButton, &QAbstractButton::clicked, this, &QPlayer::play);
73
74 positionSlider = new QSlider(Qt::Horizontal);
75 positionSlider->setRange(0, 0);
76 connect(positionSlider, &QAbstractSlider::sliderMoved, this, &QPlayer::setPosition);
77 connect(positionSlider, &QAbstractSlider::sliderReleased, this, &QPlayer::unMute);
78
79 controlLayout = new QHBoxLayout;
80 controlLayout->addWidget(exitButton);
81 controlLayout->addWidget(playButton);
82 controlLayout->addWidget(positionSlider);
83 controlLayout->setMargin(0);
84 control = new QWidget();
85 control->setLayout(controlLayout);
86 control->setWindowFlag(Qt::FramelessWindowHint);
87
88 videoViewer = new QVideoWidget;
89 imageViewer = new QLabel();
90 imageViewer->setAlignment(Qt::AlignCenter);
91 QBoxLayout *mainLayout = new QVBoxLayout();
92 mainLayout->setMargin(0);
93 mainLayout->setSpacing(0);
94 mainLayout->addWidget(videoViewer);
95 mainLayout->addWidget(imageViewer);
96 mainLayout->addWidget(control);
97
98 QWidget *widget = new QWidget();
99 widget->setLayout(mainLayout);
100
101 list = new QMediaPlaylist;
102 player.setVideoOutput(videoViewer);
103
104 setCentralWidget(widget);
105 setWindowState(Qt::WindowMaximized);
106 setWindowFlags(Qt::FramelessWindowHint);
107
108 QPalette palette = QWidget::palette();
109 palette.setColor(QPalette::Window, Qt::black);
110 setPalette(palette);
111
112 connect(&player, &QMediaPlayer::stateChanged, this, &QPlayer::mediaStateChanged);
113 connect(&player, &QMediaPlayer::positionChanged, this, &QPlayer::positionChanged);
114 connect(&player, &QMediaPlayer::durationChanged, this, &QPlayer::durationChanged);
115 connect(&player, &QMediaPlayer::currentMediaChanged, this, &QPlayer::currentMediaChanged);
116 connect(&player, QOverload<QMediaPlayer::Error>::of(&QMediaPlayer::error),
117 this, &QPlayer::handleError);
118 connect(&timer1, &QTimer::timeout, this, &QPlayer::displayImage);
119 connect(&timer2, &QTimer::timeout, this, &QPlayer::next);
120 }
121
~QPlayer()122 QPlayer::~QPlayer()
123 {
124 }
125
isPlayerAvailable() const126 bool QPlayer::isPlayerAvailable() const
127 {
128 return player.isAvailable();
129 }
130
mouseDoubleClickEvent(QMouseEvent * e)131 void QPlayer::mouseDoubleClickEvent(QMouseEvent *e)
132 {
133 if(e->button() == Qt::LeftButton){
134 if(isFullScreen()){
135 showMaximized();
136 control->show();
137 }else {
138 showFullScreen();
139 control->hide();
140 }
141 }
142 }
143
mouseReleaseEvent(QMouseEvent * e)144 void QPlayer::mouseReleaseEvent(QMouseEvent *e)
145 {
146 if(e->button() == Qt::LeftButton){
147
148 }
149 }
150
keyReleaseEvent(QKeyEvent * e)151 void QPlayer::keyReleaseEvent(QKeyEvent *e)
152 {
153 int v;
154
155 switch(e->key())
156 {
157 case Qt::Key_VolumeUp:
158 case Qt::Key_Up:
159 v = player.volume();
160 if(v < 100){
161 v = v + 10;
162 v = v<100?v:100;
163 player.setVolume(v);
164 qDebug() << "Key_VolumeUp: " << v;
165 }
166 break;
167 case Qt::Key_VolumeDown:
168 case Qt::Key_Down:
169 v = player.volume();
170 if(v > 0){
171 v = v - 10;
172 v = v>0?v:0;
173 player.setVolume(v);
174 qDebug() << "Key_VolumeDown: " << v;
175 }
176 break;
177 }
178 }
179
exit()180 void QPlayer::exit()
181 {
182 qApp->exit(0);
183 }
184
displayImage()185 void QPlayer::displayImage()
186 {
187 QMimeDatabase db;
188 QUrl url = list->currentMedia().canonicalUrl();
189 QString s = db.mimeTypeForUrl(url).name();
190
191 player.pause();
192 timer1.stop();
193 if(MIME_IS_IMAGE(s)){
194 QImage img;
195 img.load(url.toLocalFile());
196 int w = geometry().width();
197 int h = geometry().height() * 4 /5;
198 imageViewer->setPixmap(QPixmap::fromImage(img.scaled(w, h, Qt::KeepAspectRatio)));
199 imageViewer->show();
200 if(list->mediaCount() > 1){
201 timer2.start(5000);
202 }
203 }
204 }
205
next()206 void QPlayer::next()
207 {
208 timer2.stop();
209 list->setCurrentIndex(list->nextIndex());
210 play();
211 }
212
setPlaylist(QStringList l)213 void QPlayer::setPlaylist(QStringList l)
214 {
215 if(! l.empty()){
216 for(int i = 0; i < l.size(); i++){
217 QString str = l.at(i);
218 QFile f(l.at(i));
219 QUrl u(str);
220
221 if(f.exists()){
222 list->addMedia(QUrl::fromLocalFile(str));
223 }else if(u.isValid()){
224 list->addMedia(u);
225 }
226
227 }
228 list->setCurrentIndex(1);
229 player.setPlaylist(list);
230 }
231
232 if(list->mediaCount() > 1){
233 list->setPlaybackMode(QMediaPlaylist::Loop);
234 } else {
235 list->setPlaybackMode(QMediaPlaylist::CurrentItemOnce);
236 }
237 }
238
load(const QUrl & url)239 void QPlayer::load(const QUrl &url)
240 {
241 player.setMedia(url);
242 playButton->setEnabled(true);
243 }
244
play()245 void QPlayer::play()
246 {
247 switch(player.state()) {
248 case QMediaPlayer::PlayingState:
249 player.pause();
250 break;
251 default:
252 player.play();
253 break;
254 }
255 }
256
mediaStateChanged(QMediaPlayer::State state)257 void QPlayer::mediaStateChanged(QMediaPlayer::State state)
258 {
259 switch(state) {
260 case QMediaPlayer::PlayingState:
261 playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPause));
262 break;
263 case QMediaPlayer::StoppedState:
264 qApp->exit(0);
265 break;
266 default:
267 playButton->setIcon(style()->standardIcon(QStyle::SP_MediaPlay));
268 break;
269 }
270 }
271
positionChanged(qint64 position)272 void QPlayer::positionChanged(qint64 position)
273 {
274 positionSlider->setValue(position);
275 }
276
durationChanged(qint64 duration)277 void QPlayer::durationChanged(qint64 duration)
278 {
279 positionSlider->setRange(0, duration);
280 }
281
setPosition(int position)282 void QPlayer::setPosition(int position)
283 {
284 if(! player.isSeekable())
285 return;
286 if(! player.isMuted())
287 player.setMuted(true);
288 player.setPosition(position);
289 }
290
unMute()291 void QPlayer::unMute()
292 {
293 player.setMuted(false);
294 }
295
currentMediaChanged(const QMediaContent & media)296 void QPlayer::currentMediaChanged(const QMediaContent &media)
297 {
298 QUrl url = media.canonicalUrl();
299 QMimeDatabase db;
300 QMimeType mt = db.mimeTypeForUrl(url);
301 QString s = mt.name();
302 if(MIME_IS_IMAGE(s)){
303 imageViewer->show();
304 player.setVideoOutput(new QVideoWidget);
305 timer1.start(1);
306 }else if (MIME_IS_AUDIO(s)){
307 int w = geometry().width();
308 int h = geometry().height() - controlLayout->sizeHint().height();
309 imageViewer->setPixmap(QPixmap(":/album.jpeg").scaled(w*2/3, h*2/3, Qt::KeepAspectRatio));
310 imageViewer->show();
311 player.setVideoOutput(new QVideoWidget);
312 }else if(MIME_IS_VIDEO(s)){
313 imageViewer->hide();
314 player.setVideoOutput(videoViewer);
315 videoViewer->show();
316 }
317 }
318
handleError()319 void QPlayer::handleError()
320 {
321 const QString errorString = player.errorString();
322 QString message = "Error: ";
323 if (errorString.isEmpty())
324 message += " #" + QString::number(int(player.error()));
325 else
326 message += errorString;
327 qDebug() << message;
328
329 if(QMessageBox::critical(this, "Qplayer Error", message)){
330 exit();
331 }
332 }
333