• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mcallegari / qlcplus / 23157810099

16 Mar 2026 05:44PM UTC coverage: 33.973% (-0.08%) from 34.05%
23157810099

push

github

mcallegari
Back to 5.2.2/4.14.5 debug

17651 of 51956 relevant lines covered (33.97%)

19863.28 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

47.84
/engine/src/video.cpp
1
/*
2
  Q Light Controller Plus
3
  video.cpp
4

5
  Copyright (c) Massimo Callegari
6

7
  Licensed under the Apache License, Version 2.0 (the "License");
8
  you may not use this file except in compliance with the License.
9
  You may obtain a copy of the License at
10

11
      http://www.apache.org/licenses/LICENSE-2.0.txt
12

13
  Unless required by applicable law or agreed to in writing, software
14
  distributed under the License is distributed on an "AS IS" BASIS,
15
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
  See the License for the specific language governing permissions and
17
  limitations under the License.
18
*/
19

20
#include <QXmlStreamReader>
21
#include <QXmlStreamWriter>
22
#include <QMediaPlayer>
23
#include <QDebug>
24
#include <QFile>
25

26
#include "video.h"
27
#include "doc.h"
28

29
#define KXMLQLCVideoSource      QStringLiteral("Source")
30
#define KXMLQLCVideoScreen      QStringLiteral("Screen")
31
#define KXMLQLCVideoFullscreen  QStringLiteral("Fullscreen")
32
#define KXMLQLCVideoGeometry    QStringLiteral("Geometry")
33
#define KXMLQLCVideoRotation    QStringLiteral("Rotation")
34
#define KXMLQLCVideoZIndex      QStringLiteral("ZIndex")
35

36
const QStringList Video::m_defaultVideoCaps =
37
        QStringList() << "*.avi" << "*.wmv" << "*.mkv" << "*.mp4" << "*.mov" << "*.mpg" << "*.mpeg" << "*.flv" << "*.webm";
38
const QStringList Video::m_defaultPictureCaps =
39
        QStringList() << "*.png" << "*.bmp" << "*.jpg" << "*.jpeg" << "*.gif";
40

41
/*****************************************************************************
42
 * Initialization
43
 *****************************************************************************/
44

45
Video::Video(Doc* doc)
4✔
46
  : Function(doc, Function::VideoType)
47
  , m_doc(doc)
4✔
48
  , m_sourceUrl("")
4✔
49
  , m_isPicture(false)
4✔
50
  , m_videoDuration(0)
4✔
51
  , m_resolution(QSize(0,0))
4✔
52
  , m_customGeometry(QRect())
4✔
53
  , m_rotation(QVector3D(0, 0, 0))
4✔
54
  , m_zIndex(1)
4✔
55
  , m_screen(0)
4✔
56
  , m_fullscreen(false)
4✔
57
{
58
    setName(tr("New Video"));
4✔
59
    setRunOrder(Video::SingleShot);
4✔
60

61
    registerAttribute(tr("Volume"), Function::LastWins, 0, 100, 100);
4✔
62
    registerAttribute(tr("X Rotation"), Function::LastWins, -360.0, 360.0, 0.0);
4✔
63
    registerAttribute(tr("Y Rotation"), Function::LastWins, -360.0, 360.0, 0.0);
4✔
64
    registerAttribute(tr("Z Rotation"), Function::LastWins, -360.0, 360.0, 0.0);
4✔
65
    registerAttribute(tr("X Position"), Function::LastWins, -100.0, 100.0, 0.0);
4✔
66
    registerAttribute(tr("Y Position"), Function::LastWins, -100.0, 100.0, 0.0);
4✔
67
    registerAttribute(tr("Width scale"), Function::LastWins, 0, 1000.0, 100.0);
4✔
68
    registerAttribute(tr("Height scale"), Function::LastWins, 0, 1000.0, 100.0);
4✔
69

70
    // Listen to member Function removals
71
    connect(doc, SIGNAL(functionRemoved(quint32)),
4✔
72
            this, SLOT(slotFunctionRemoved(quint32)));
73
}
4✔
74

75
Video::~Video()
4✔
76
{
77
}
4✔
78

79
QIcon Video::getIcon() const
×
80
{
81
    return QIcon(":/video.png");
×
82
}
83

84
/*****************************************************************************
85
 * Copying
86
 *****************************************************************************/
87

88
Function* Video::createCopy(Doc* doc, bool addToDoc)
×
89
{
90
    Q_ASSERT(doc != NULL);
×
91

92
    Function* copy = new Video(doc);
×
93
    if (copy->copyFrom(this) == false)
×
94
    {
95
        delete copy;
×
96
        copy = NULL;
×
97
    }
98
    if (addToDoc == true && doc->addFunction(copy) == false)
×
99
    {
100
        delete copy;
×
101
        copy = NULL;
×
102
    }
103

104
    return copy;
×
105
}
106

107
bool Video::copyFrom(const Function* function)
×
108
{
109
    const Video* vid = qobject_cast<const Video*> (function);
×
110
    if (vid == NULL)
×
111
        return false;
×
112

113
    setSourceUrl(vid->m_sourceUrl);
×
114
    m_videoDuration = vid->m_videoDuration;
×
115

116
    return Function::copyFrom(function);
×
117
}
118

119
QStringList Video::getVideoCapabilities()
×
120
{
121
    QStringList caps;
×
122
    QStringList mimeTypes;
×
123
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
124
    mimeTypes = QMediaPlayer::supportedMimeTypes();
125
#endif
126

127
    if (mimeTypes.isEmpty())
×
128
    {
129
        return m_defaultVideoCaps;
×
130
    }
131
    else
132
    {
133
        qDebug() << "Supported video types:" << mimeTypes;
×
134

135
        foreach (QString mime, mimeTypes)
×
136
        {
137
            if (mime.startsWith("video/"))
×
138
            {
139
                if (mime.endsWith("/3gpp")) caps << "*.3gp";
×
140
                else if (mime.endsWith("/mp4")) caps << "*.mp4";
×
141
                else if (mime.endsWith("/avi")) caps << "*.avi";
×
142
                else if (mime.endsWith("/m2ts")) caps << "*.m2ts";
×
143
                else if (mime.endsWith("m4v")) caps << "*.m4v";
×
144
                else if (mime.endsWith("/mpeg")) caps << "*.mpeg";
×
145
                else if (mime.endsWith("/mpg")) caps << "*.mpg";
×
146
                else if (mime.endsWith("/quicktime")) caps << "*.mov";
×
147
                else if (mime.endsWith("/webm")) caps << "*.webm";
×
148
                else if (mime.endsWith("matroska")) caps << "*.mkv";
×
149
            }
150
        }
×
151
    }
152
    return caps;
×
153
}
×
154

155
QStringList Video::getPictureCapabilities()
×
156
{
157
    return m_defaultPictureCaps;
×
158
}
159

160
/*********************************************************************
161
 * Properties
162
 *********************************************************************/
163
void Video::setTotalDuration(quint32 duration)
×
164
{
165
    if (m_videoDuration == (qint64)duration)
×
166
        return;
×
167

168
    m_videoDuration = (qint64)duration;
×
169
    emit totalTimeChanged(m_videoDuration);
×
170
}
171

172
quint32 Video::totalDuration()
×
173
{
174
    return (quint32)m_videoDuration;
×
175
}
176

177
QSize Video::resolution() const
×
178
{
179
    return m_resolution;
×
180
}
181

182
void Video::setResolution(QSize size)
×
183
{
184
    m_resolution = size;
×
185
    emit metaDataChanged("Resolution", QVariant(m_resolution));
×
186
}
×
187

188
QRect Video::customGeometry() const
1✔
189
{
190
    return m_customGeometry;
1✔
191
}
192

193
void Video::setCustomGeometry(QRect rect)
2✔
194
{
195
    if (rect == m_customGeometry)
2✔
196
        return;
×
197

198
    m_customGeometry = rect;
2✔
199
    emit customGeometryChanged(rect);
2✔
200
}
201

202
QVector3D Video::rotation() const
1✔
203
{
204
    return m_rotation;
1✔
205
}
206

207
void Video::setRotation(QVector3D rotation)
2✔
208
{
209
    if (m_rotation == rotation)
2✔
210
        return;
×
211

212
    m_rotation = rotation;
2✔
213
    emit rotationChanged(m_rotation);
2✔
214
}
215

216
int Video::zIndex() const
1✔
217
{
218
    return m_zIndex;
1✔
219
}
220

221
void Video::setZIndex(int idx)
2✔
222
{
223
    if (m_zIndex == idx)
2✔
224
        return;
×
225

226
    m_zIndex = idx;
2✔
227
    emit zIndexChanged(m_zIndex);
2✔
228
}
229

230
void Video::setAudioCodec(QString codec)
×
231
{
232
    m_audioCodec = codec;
×
233
    emit metaDataChanged("AudioCodec", QVariant(m_audioCodec));
×
234
}
×
235

236
QString Video::audioCodec() const
×
237
{
238
    return m_audioCodec;
×
239
}
240

241
void Video::setVideoCodec(QString codec)
×
242
{
243
    m_videoCodec = codec;
×
244
    emit metaDataChanged("VideoCodec", QVariant(m_videoCodec));
×
245
}
×
246

247
QString Video::videoCodec() const
×
248
{
249
    return m_videoCodec;
×
250
}
251

252
bool Video::setSourceUrl(QString filename)
3✔
253
{
254
    m_sourceUrl = filename;
3✔
255
    qDebug() << Q_FUNC_INFO << "Source name set:" << m_sourceUrl;
3✔
256

257
    QString fileExt = "*" + filename.mid(filename.lastIndexOf('.'));
3✔
258

259
    if (m_defaultPictureCaps.contains(fileExt))
3✔
260
    {
261
        m_isPicture = true;
1✔
262
    }
263

264
    if (m_sourceUrl.contains("://"))
3✔
265
    {
266
        QUrl url(m_sourceUrl);
3✔
267
        setName(url.fileName());
3✔
268
    }
3✔
269
    else
270
    {
271
        if (QFile(m_sourceUrl).exists())
×
272
        {
273
            setName(QFileInfo(m_sourceUrl).fileName());
×
274
        }
275
        else
276
        {
277
            doc()->appendToErrorLog(tr("Video file <b>%1</b> not found").arg(m_sourceUrl));
×
278
            setName(tr("File not found"));
×
279
        }
280
    }
281

282
    emit sourceChanged(m_sourceUrl);
3✔
283

284
    return true;
3✔
285
}
3✔
286

287
bool Video::isPicture() const
2✔
288
{
289
    return m_isPicture;
2✔
290
}
291

292
QString Video::sourceUrl() const
3✔
293
{
294
    return m_sourceUrl;
3✔
295
}
296

297
void Video::setScreen(int index)
3✔
298
{
299
    m_screen = index;
3✔
300
    emit changed(id());
3✔
301
}
3✔
302

303
int Video::screen() const
2✔
304
{
305
    return m_screen;
2✔
306
}
307

308
void Video::setFullscreen(bool enable)
3✔
309
{
310
    if (m_fullscreen == enable)
3✔
311
        return;
×
312

313
    m_fullscreen = enable;
3✔
314
    emit changed(id());
3✔
315
}
316

317
qreal Video::intensity() const
×
318
{
319
    return getAttributeValue(Intensity);
×
320
}
321

322
bool Video::fullscreen() const
3✔
323
{
324
    return m_fullscreen;
3✔
325
}
326

327
int Video::adjustAttribute(qreal fraction, int attributeId)
×
328
{
329
    int attrIndex = Function::adjustAttribute(fraction, attributeId);
×
330

331
    switch (attrIndex)
×
332
    {
333
        case Intensity:
×
334
        {
335
            emit requestBrightnessVolumeAdjust(getAttributeValue(Intensity));
×
336
            emit intensityChanged();
×
337
        }
338
        break;
×
339
        default:
×
340
        break;
×
341
    }
342

343
    return attrIndex;
×
344
}
345

346
void Video::slotFunctionRemoved(quint32 fid)
×
347
{
348
    Q_UNUSED(fid)
349
}
×
350

351
void Video::stopFromUI()
×
352
{
353
    if (isRunning())
×
354
        stop(FunctionParent::master());
×
355
}
×
356

357
/*********************************************************************
358
 * Save & Load
359
 *********************************************************************/
360

361
bool Video::saveXML(QXmlStreamWriter *doc) const
1✔
362
{
363
    Q_ASSERT(doc != NULL);
1✔
364

365
    /* Function tag */
366
    doc->writeStartElement(KXMLQLCFunction);
2✔
367

368
    /* Common attributes */
369
    saveXMLCommon(doc);
1✔
370

371
    /* Speed */
372
    saveXMLSpeed(doc);
1✔
373

374
    /* Playback mode */
375
    saveXMLRunOrder(doc);
1✔
376

377
    doc->writeStartElement(KXMLQLCVideoSource);
2✔
378
    if (m_screen > 0)
1✔
379
        doc->writeAttribute(KXMLQLCVideoScreen, QString::number(m_screen));
2✔
380
    if (m_fullscreen == true)
1✔
381
        doc->writeAttribute(KXMLQLCVideoFullscreen, "1");
2✔
382
#ifdef QMLUI
383
    if (m_customGeometry.isNull() == false)
384
    {
385
        QString rect = QString("%1,%2,%3,%4")
386
                .arg(m_customGeometry.x()).arg(m_customGeometry.y())
387
                .arg(m_customGeometry.width()).arg(m_customGeometry.height());
388
        doc->writeAttribute(KXMLQLCVideoGeometry, rect);
389
    }
390
    if (m_rotation.isNull() == false)
391
    {
392
        QString rot = QString("%1,%2,%3").arg(m_rotation.x()).arg(m_rotation.y()).arg(m_rotation.z());
393
        doc->writeAttribute(KXMLQLCVideoRotation, rot);
394
    }
395
    doc->writeAttribute(KXMLQLCVideoZIndex, QString::number(m_zIndex));
396
#endif
397
    if (m_sourceUrl.contains("://"))
1✔
398
        doc->writeCharacters(m_sourceUrl);
1✔
399
    else
400
        doc->writeCharacters(m_doc->normalizeComponentPath(m_sourceUrl));
×
401

402
    doc->writeEndElement();
1✔
403

404
    /* End the <Function> tag */
405
    doc->writeEndElement();
1✔
406

407
    return true;
1✔
408
}
409

410
bool Video::loadXML(QXmlStreamReader &root)
1✔
411
{
412
    if (root.name() != KXMLQLCFunction)
1✔
413
    {
414
        qWarning() << Q_FUNC_INFO << "Function node not found";
×
415
        return false;
×
416
    }
417

418
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::VideoType))
2✔
419
    {
420
        qWarning() << Q_FUNC_INFO << root.attributes().value(KXMLQLCFunctionType).toString()
×
421
                   << "is not Video";
×
422
        return false;
×
423
    }
424

425
    QString fname = name();
1✔
426

427
    while (root.readNextStartElement())
4✔
428
    {
429
        if (root.name() == KXMLQLCVideoSource)
3✔
430
        {
431
            QXmlStreamAttributes attrs = root.attributes();
1✔
432
            if (attrs.hasAttribute(KXMLQLCVideoScreen))
1✔
433
                setScreen(attrs.value(KXMLQLCVideoScreen).toString().toInt());
1✔
434

435
            if (attrs.hasAttribute(KXMLQLCVideoFullscreen))
1✔
436
            {
437
                if (attrs.value(KXMLQLCVideoFullscreen).toString() == "1")
2✔
438
                    setFullscreen(true);
1✔
439
                else
440
                    setFullscreen(false);
×
441
            }
442
#ifdef QMLUI
443
            if (attrs.hasAttribute(KXMLQLCVideoGeometry))
444
            {
445
                QStringList slist = attrs.value(KXMLQLCVideoGeometry).toString().split(",");
446
                if (slist.count() == 4)
447
                {
448
                    QRect r;
449
                    r.setX(slist.at(0).toInt());
450
                    r.setY(slist.at(1).toInt());
451
                    r.setWidth(slist.at(2).toInt());
452
                    r.setHeight(slist.at(3).toInt());
453

454
                    setCustomGeometry(r);
455
                }
456
            }
457
            if (attrs.hasAttribute(KXMLQLCVideoRotation))
458
            {
459
                QStringList slist = attrs.value(KXMLQLCVideoRotation).toString().split(",");
460
                if (slist.count() == 3)
461
                {
462
                    QVector3D v;
463
                    v.setX(slist.at(0).toInt());
464
                    v.setY(slist.at(1).toInt());
465
                    v.setZ(slist.at(2).toInt());
466
                    setRotation(v);
467
                }
468
            }
469
            if (attrs.hasAttribute(KXMLQLCVideoZIndex))
470
            {
471
                setZIndex(attrs.value(KXMLQLCVideoZIndex).toInt());
472
            }
473
#endif
474

475
            QString path = root.readElementText();
1✔
476
            if (path.contains("://") == true)
1✔
477
                setSourceUrl(path);
1✔
478
            else
479
                setSourceUrl(m_doc->denormalizeComponentPath(path));
×
480
        }
1✔
481
        else if (root.name() == KXMLQLCFunctionSpeed)
2✔
482
        {
483
            loadXMLSpeed(root);
1✔
484
        }
485
        else if (root.name() == KXMLQLCFunctionRunOrder)
1✔
486
        {
487
            loadXMLRunOrder(root);
1✔
488
        }
489
        else
490
        {
491
            qWarning() << Q_FUNC_INFO << "Unknown Video tag:" << root.name();
×
492
            root.skipCurrentElement();
×
493
        }
494
    }
495

496
    setName(fname);
1✔
497

498
    return true;
1✔
499
}
1✔
500

501
void Video::postLoad()
×
502
{
503
}
×
504

505
/*********************************************************************
506
 * Running
507
 *********************************************************************/
508
void Video::preRun(MasterTimer* timer)
×
509
{
510
    emit requestPlayback();
×
511
    Function::preRun(timer);
×
512
}
×
513

514
void Video::setPause(bool enable)
×
515
{
516
    if (isRunning())
×
517
    {
518
        emit requestPause(enable);
×
519
        Function::setPause(enable);
×
520
    }
521
}
×
522

523
void Video::write(MasterTimer* timer, QList<Universe *> universes)
×
524
{
525
    Q_UNUSED(timer)
526
    Q_UNUSED(universes)
527

528
    incrementElapsed();
×
529
}
×
530

531
void Video::postRun(MasterTimer* timer, QList<Universe*> universes)
×
532
{
533
    emit requestStop();
×
534
    Function::postRun(timer, universes);
×
535
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc