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

mcallegari / qlcplus / 20794594664

07 Jan 2026 07:52PM UTC coverage: 34.17% (-0.1%) from 34.276%
20794594664

push

github

mcallegari
Back to 5.1.1 debug

17716 of 51846 relevant lines covered (34.17%)

19821.52 hits per line

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

48.91
/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()
×
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()
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)
3✔
222
{
223
    if (m_zIndex == idx)
3✔
224
        return;
×
225

226
    m_zIndex = idx;
3✔
227
    emit zIndexChanged(m_zIndex);
3✔
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()
×
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()
×
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
        setZIndex(2);
1✔
263
    }
264

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

283
    emit sourceChanged(m_sourceUrl);
3✔
284

285
    return true;
3✔
286
}
3✔
287

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

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

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

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

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

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

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

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

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

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

344
    return attrIndex;
×
345
}
346

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

352
/*********************************************************************
353
 * Save & Load
354
 *********************************************************************/
355

356
bool Video::saveXML(QXmlStreamWriter *doc)
1✔
357
{
358
    Q_ASSERT(doc != NULL);
1✔
359

360
    /* Function tag */
361
    doc->writeStartElement(KXMLQLCFunction);
2✔
362

363
    /* Common attributes */
364
    saveXMLCommon(doc);
1✔
365

366
    /* Speed */
367
    saveXMLSpeed(doc);
1✔
368

369
    /* Playback mode */
370
    saveXMLRunOrder(doc);
1✔
371

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

397
    doc->writeEndElement();
1✔
398

399
    /* End the <Function> tag */
400
    doc->writeEndElement();
1✔
401

402
    return true;
1✔
403
}
404

405
bool Video::loadXML(QXmlStreamReader &root)
1✔
406
{
407
    if (root.name() != KXMLQLCFunction)
1✔
408
    {
409
        qWarning() << Q_FUNC_INFO << "Function node not found";
×
410
        return false;
×
411
    }
412

413
    if (root.attributes().value(KXMLQLCFunctionType).toString() != typeToString(Function::VideoType))
2✔
414
    {
415
        qWarning() << Q_FUNC_INFO << root.attributes().value(KXMLQLCFunctionType).toString()
×
416
                   << "is not Video";
×
417
        return false;
×
418
    }
419

420
    QString fname = name();
1✔
421

422
    while (root.readNextStartElement())
4✔
423
    {
424
        if (root.name() == KXMLQLCVideoSource)
3✔
425
        {
426
            QXmlStreamAttributes attrs = root.attributes();
1✔
427
            if (attrs.hasAttribute(KXMLQLCVideoScreen))
1✔
428
                setScreen(attrs.value(KXMLQLCVideoScreen).toString().toInt());
1✔
429

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

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

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

491
    setName(fname);
1✔
492

493
    return true;
1✔
494
}
1✔
495

496
void Video::postLoad()
×
497
{
498
}
×
499

500
/*********************************************************************
501
 * Running
502
 *********************************************************************/
503
void Video::preRun(MasterTimer* timer)
×
504
{
505
    emit requestPlayback();
×
506
    Function::preRun(timer);
×
507
}
×
508

509
void Video::setPause(bool enable)
×
510
{
511
    if (isRunning())
×
512
    {
513
        emit requestPause(enable);
×
514
        Function::setPause(enable);
×
515
    }
516
}
×
517

518
void Video::write(MasterTimer* timer, QList<Universe *> universes)
×
519
{
520
    Q_UNUSED(timer)
521
    Q_UNUSED(universes)
522

523
    incrementElapsed();
×
524
}
×
525

526
void Video::postRun(MasterTimer* timer, QList<Universe*> universes)
×
527
{
528
    emit requestStop();
×
529
    Function::postRun(timer, universes);
×
530
}
×
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