1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
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** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <QtCore/qdebug.h>
41
42#include "qmediatimerange.h"
43
44QT_BEGIN_NAMESPACE
45
46/*!
47 \class QMediaTimeInterval
48 \brief The QMediaTimeInterval class represents a time interval with integer precision.
49 \inmodule QtMultimedia
50
51 \ingroup multimedia
52 \ingroup multimedia_core
53
54 An interval is specified by an inclusive start() and end() time. These
55 must be set in the constructor, as this is an immutable class. The
56 specific units of time represented by the class have not been defined - it
57 is suitable for any times which can be represented by a signed 64 bit
58 integer.
59
60 The isNormal() method determines if a time interval is normal (a normal
61 time interval has start() <= end()). A normal interval can be received
62 from an abnormal interval by calling the normalized() method.
63
64 The contains() method determines if a specified time lies within the time
65 interval.
66
67 The translated() method returns a time interval which has been translated
68 forwards or backwards through time by a specified offset.
69
70 \sa QMediaTimeRange
71*/
72
73/*!
74 \fn QMediaTimeInterval::QMediaTimeInterval()
75
76 Constructs an empty interval.
77*/
78QMediaTimeInterval::QMediaTimeInterval()
79 : s(0)
80 , e(0)
81{
82
83}
84
85/*!
86 \fn QMediaTimeInterval::QMediaTimeInterval(qint64 start, qint64 end)
87
88 Constructs an interval with the specified \a start and \a end times.
89*/
90QMediaTimeInterval::QMediaTimeInterval(qint64 start, qint64 end)
91 : s(start)
92 , e(end)
93{
94
95}
96
97#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
98/*!
99 \fn QMediaTimeInterval::QMediaTimeInterval(const QMediaTimeInterval &other)
100
101 Constructs an interval by taking a copy of \a other.
102*/
103QMediaTimeInterval::QMediaTimeInterval(const QMediaTimeInterval &other)
104 : s(other.s)
105 , e(other.e)
106{
107
108}
109#endif
110
111/*!
112 \fn QMediaTimeInterval::start() const
113
114 Returns the start time of the interval.
115
116 \sa end()
117*/
118qint64 QMediaTimeInterval::start() const
119{
120 return s;
121}
122
123/*!
124 \fn QMediaTimeInterval::end() const
125
126 Returns the end time of the interval.
127
128 \sa start()
129*/
130qint64 QMediaTimeInterval::end() const
131{
132 return e;
133}
134
135/*!
136 \fn QMediaTimeInterval::contains(qint64 time) const
137
138 Returns true if the time interval contains the specified \a time.
139 That is, start() <= time <= end().
140*/
141bool QMediaTimeInterval::contains(qint64 time) const
142{
143 return isNormal() ? (s <= time && time <= e)
144 : (e <= time && time <= s);
145}
146
147/*!
148 \fn QMediaTimeInterval::isNormal() const
149
150 Returns true if this time interval is normal.
151 A normal time interval has start() <= end().
152
153 \sa normalized()
154*/
155bool QMediaTimeInterval::isNormal() const
156{
157 return s <= e;
158}
159
160/*!
161 \fn QMediaTimeInterval::normalized() const
162
163 Returns a normalized version of this interval.
164
165 If the start() time of the interval is greater than the end() time,
166 then the returned interval has the start and end times swapped.
167*/
168QMediaTimeInterval QMediaTimeInterval::normalized() const
169{
170 if(s > e)
171 return QMediaTimeInterval(e, s);
172
173 return *this;
174}
175
176/*!
177 \fn QMediaTimeInterval::translated(qint64 offset) const
178
179 Returns a copy of this time interval, translated by a value of \a offset.
180 An interval can be moved forward through time with a positive offset, or backward
181 through time with a negative offset.
182*/
183QMediaTimeInterval QMediaTimeInterval::translated(qint64 offset) const
184{
185 return QMediaTimeInterval(s + offset, e + offset);
186}
187
188/*!
189 \relates QMediaTimeRange
190
191 Returns true if \a a is exactly equal to \a b.
192*/
193bool operator==(const QMediaTimeInterval &a, const QMediaTimeInterval &b)
194{
195 return a.start() == b.start() && a.end() == b.end();
196}
197
198/*!
199 \relates QMediaTimeRange
200
201 Returns true if \a a is not exactly equal to \a b.
202*/
203bool operator!=(const QMediaTimeInterval &a, const QMediaTimeInterval &b)
204{
205 return a.start() != b.start() || a.end() != b.end();
206}
207
208class QMediaTimeRangePrivate : public QSharedData
209{
210public:
211
212 QMediaTimeRangePrivate();
213 QMediaTimeRangePrivate(const QMediaTimeRangePrivate &other);
214 QMediaTimeRangePrivate(const QMediaTimeInterval &interval);
215
216 QList<QMediaTimeInterval> intervals;
217
218 void addInterval(const QMediaTimeInterval &interval);
219 void removeInterval(const QMediaTimeInterval &interval);
220};
221
222QMediaTimeRangePrivate::QMediaTimeRangePrivate()
223 : QSharedData()
224{
225
226}
227
228QMediaTimeRangePrivate::QMediaTimeRangePrivate(const QMediaTimeRangePrivate &other)
229 : QSharedData()
230 , intervals(other.intervals)
231{
232
233}
234
235QMediaTimeRangePrivate::QMediaTimeRangePrivate(const QMediaTimeInterval &interval)
236 : QSharedData()
237{
238 if(interval.isNormal())
239 intervals << interval;
240}
241
242void QMediaTimeRangePrivate::addInterval(const QMediaTimeInterval &interval)
243{
244 // Handle normalized intervals only
245 if(!interval.isNormal())
246 return;
247
248 // Find a place to insert the interval
249 int i;
250 for (i = 0; i < intervals.count(); i++) {
251 // Insert before this element
252 if(interval.s < intervals[i].s) {
253 intervals.insert(i, interval);
254 break;
255 }
256 }
257
258 // Interval needs to be added to the end of the list
259 if (i == intervals.count())
260 intervals.append(interval);
261
262 // Do we need to correct the element before us?
263 if(i > 0 && intervals[i - 1].e >= interval.s - 1)
264 i--;
265
266 // Merge trailing ranges
267 while (i < intervals.count() - 1
268 && intervals[i].e >= intervals[i + 1].s - 1) {
269 intervals[i].e = qMax(intervals[i].e, intervals[i + 1].e);
270 intervals.removeAt(i + 1);
271 }
272}
273
274void QMediaTimeRangePrivate::removeInterval(const QMediaTimeInterval &interval)
275{
276 // Handle normalized intervals only
277 if(!interval.isNormal())
278 return;
279
280 for (int i = 0; i < intervals.count(); i++) {
281 QMediaTimeInterval r = intervals[i];
282
283 if (r.e < interval.s) {
284 // Before the removal interval
285 continue;
286 } else if (interval.e < r.s) {
287 // After the removal interval - stop here
288 break;
289 } else if (r.s < interval.s && interval.e < r.e) {
290 // Split case - a single range has a chunk removed
291 intervals[i].e = interval.s -1;
292 addInterval(QMediaTimeInterval(interval.e + 1, r.e));
293 break;
294 } else if (r.s < interval.s) {
295 // Trimming Tail Case
296 intervals[i].e = interval.s - 1;
297 } else if (interval.e < r.e) {
298 // Trimming Head Case - we can stop after this
299 intervals[i].s = interval.e + 1;
300 break;
301 } else {
302 // Complete coverage case
303 intervals.removeAt(i);
304 --i;
305 }
306 }
307}
308
309/*!
310 \class QMediaTimeRange
311 \brief The QMediaTimeRange class represents a set of zero or more disjoint
312 time intervals.
313 \ingroup multimedia
314 \inmodule QtMultimedia
315
316 \reentrant
317
318 The earliestTime(), latestTime(), intervals() and isEmpty()
319 methods are used to get information about the current time range.
320
321 The addInterval(), removeInterval() and clear() methods are used to modify
322 the current time range.
323
324 When adding or removing intervals from the time range, existing intervals
325 within the range may be expanded, trimmed, deleted, merged or split to ensure
326 that all intervals within the time range remain distinct and disjoint. As a
327 consequence, all intervals added or removed from a time range must be
328 \l{QMediaTimeInterval::isNormal()}{normal}.
329
330 \sa QMediaTimeInterval
331*/
332
333/*!
334 \fn QMediaTimeRange::QMediaTimeRange()
335
336 Constructs an empty time range.
337*/
338QMediaTimeRange::QMediaTimeRange()
339 : d(new QMediaTimeRangePrivate)
340{
341
342}
343
344/*!
345 \fn QMediaTimeRange::QMediaTimeRange(qint64 start, qint64 end)
346
347 Constructs a time range that contains an initial interval from
348 \a start to \a end inclusive.
349
350 If the interval is not \l{QMediaTimeInterval::isNormal()}{normal},
351 the resulting time range will be empty.
352
353 \sa addInterval()
354*/
355QMediaTimeRange::QMediaTimeRange(qint64 start, qint64 end)
356 : d(new QMediaTimeRangePrivate(QMediaTimeInterval(start, end)))
357{
358
359}
360
361/*!
362 \fn QMediaTimeRange::QMediaTimeRange(const QMediaTimeInterval &interval)
363
364 Constructs a time range that contains an initial interval, \a interval.
365
366 If \a interval is not \l{QMediaTimeInterval::isNormal()}{normal},
367 the resulting time range will be empty.
368
369 \sa addInterval()
370*/
371QMediaTimeRange::QMediaTimeRange(const QMediaTimeInterval &interval)
372 : d(new QMediaTimeRangePrivate(interval))
373{
374
375}
376
377/*!
378 \fn QMediaTimeRange::QMediaTimeRange(const QMediaTimeRange &range)
379
380 Constructs a time range by copying another time \a range.
381*/
382QMediaTimeRange::QMediaTimeRange(const QMediaTimeRange &range)
383 : d(range.d)
384{
385
386}
387
388/*!
389 \fn QMediaTimeRange::~QMediaTimeRange()
390
391 Destructor.
392*/
393QMediaTimeRange::~QMediaTimeRange()
394{
395
396}
397
398/*!
399 Takes a copy of the \a other time range and returns itself.
400*/
401QMediaTimeRange &QMediaTimeRange::operator=(const QMediaTimeRange &other)
402{
403 d = other.d;
404 return *this;
405}
406
407/*!
408 Sets the time range to a single continuous interval, \a interval.
409*/
410QMediaTimeRange &QMediaTimeRange::operator=(const QMediaTimeInterval &interval)
411{
412 d = new QMediaTimeRangePrivate(interval);
413 return *this;
414}
415
416/*!
417 \fn QMediaTimeRange::earliestTime() const
418
419 Returns the earliest time within the time range.
420
421 For empty time ranges, this value is equal to zero.
422
423 \sa latestTime()
424*/
425qint64 QMediaTimeRange::earliestTime() const
426{
427 if (!d->intervals.isEmpty())
428 return d->intervals[0].s;
429
430 return 0;
431}
432
433/*!
434 \fn QMediaTimeRange::latestTime() const
435
436 Returns the latest time within the time range.
437
438 For empty time ranges, this value is equal to zero.
439
440 \sa earliestTime()
441*/
442qint64 QMediaTimeRange::latestTime() const
443{
444 if (!d->intervals.isEmpty())
445 return d->intervals[d->intervals.count() - 1].e;
446
447 return 0;
448}
449
450/*!
451 \fn QMediaTimeRange::addInterval(qint64 start, qint64 end)
452 \overload
453
454 Adds the interval specified by \a start and \a end
455 to the time range.
456
457 \sa addInterval()
458*/
459void QMediaTimeRange::addInterval(qint64 start, qint64 end)
460{
461 d->addInterval(QMediaTimeInterval(start, end));
462}
463
464/*!
465 \fn QMediaTimeRange::addInterval(const QMediaTimeInterval &interval)
466
467 Adds the specified \a interval to the time range.
468
469 Adding intervals which are not \l{QMediaTimeInterval::isNormal()}{normal}
470 is invalid, and will be ignored.
471
472 If the specified interval is adjacent to, or overlaps existing
473 intervals within the time range, these intervals will be merged.
474
475 This operation takes linear time.
476
477 \sa removeInterval()
478*/
479void QMediaTimeRange::addInterval(const QMediaTimeInterval &interval)
480{
481 d->addInterval(interval);
482}
483
484/*!
485 Adds each of the intervals in \a range to this time range.
486
487 Equivalent to calling addInterval() for each interval in \a range.
488*/
489void QMediaTimeRange::addTimeRange(const QMediaTimeRange &range)
490{
491 const auto intervals = range.intervals();
492 for (const QMediaTimeInterval &i : intervals) {
493 d->addInterval(i);
494 }
495}
496
497/*!
498 \fn QMediaTimeRange::removeInterval(qint64 start, qint64 end)
499 \overload
500
501 Removes the interval specified by \a start and \a end
502 from the time range.
503
504 \sa removeInterval()
505*/
506void QMediaTimeRange::removeInterval(qint64 start, qint64 end)
507{
508 d->removeInterval(QMediaTimeInterval(start, end));
509}
510
511/*!
512 \fn QMediaTimeRange::removeInterval(const QMediaTimeInterval &interval)
513
514 Removes the specified \a interval from the time range.
515
516 Removing intervals which are not \l{QMediaTimeInterval::isNormal()}{normal}
517 is invalid, and will be ignored.
518
519 Intervals within the time range will be trimmed, split or deleted
520 such that no intervals within the time range include any part of the
521 target interval.
522
523 This operation takes linear time.
524
525 \sa addInterval()
526*/
527void QMediaTimeRange::removeInterval(const QMediaTimeInterval &interval)
528{
529 d->removeInterval(interval);
530}
531
532/*!
533 Removes each of the intervals in \a range from this time range.
534
535 Equivalent to calling removeInterval() for each interval in \a range.
536*/
537void QMediaTimeRange::removeTimeRange(const QMediaTimeRange &range)
538{
539 const auto intervals = range.intervals();
540 for (const QMediaTimeInterval &i : intervals) {
541 d->removeInterval(i);
542 }
543}
544
545/*!
546 Adds each interval in \a other to the time range and returns the result.
547*/
548QMediaTimeRange& QMediaTimeRange::operator+=(const QMediaTimeRange &other)
549{
550 addTimeRange(other);
551 return *this;
552}
553
554/*!
555 Adds the specified \a interval to the time range and returns the result.
556*/
557QMediaTimeRange& QMediaTimeRange::operator+=(const QMediaTimeInterval &interval)
558{
559 addInterval(interval);
560 return *this;
561}
562
563/*!
564 Removes each interval in \a other from the time range and returns the result.
565*/
566QMediaTimeRange& QMediaTimeRange::operator-=(const QMediaTimeRange &other)
567{
568 removeTimeRange(other);
569 return *this;
570}
571
572/*!
573 Removes the specified \a interval from the time range and returns the result.
574*/
575QMediaTimeRange& QMediaTimeRange::operator-=(const QMediaTimeInterval &interval)
576{
577 removeInterval(interval);
578 return *this;
579}
580
581/*!
582 \fn QMediaTimeRange::clear()
583
584 Removes all intervals from the time range.
585
586 \sa removeInterval()
587*/
588void QMediaTimeRange::clear()
589{
590 d->intervals.clear();
591}
592
593/*!
594 \fn QMediaTimeRange::intervals() const
595
596 Returns the list of intervals covered by this time range.
597*/
598QList<QMediaTimeInterval> QMediaTimeRange::intervals() const
599{
600 return d->intervals;
601}
602
603/*!
604 \fn QMediaTimeRange::isEmpty() const
605
606 Returns true if there are no intervals within the time range.
607
608 \sa intervals()
609*/
610bool QMediaTimeRange::isEmpty() const
611{
612 return d->intervals.isEmpty();
613}
614
615/*!
616 \fn QMediaTimeRange::isContinuous() const
617
618 Returns true if the time range consists of a continuous interval.
619 That is, there is one or fewer disjoint intervals within the time range.
620*/
621bool QMediaTimeRange::isContinuous() const
622{
623 return (d->intervals.count() <= 1);
624}
625
626/*!
627 \fn QMediaTimeRange::contains(qint64 time) const
628
629 Returns true if the specified \a time lies within the time range.
630*/
631bool QMediaTimeRange::contains(qint64 time) const
632{
633 for (int i = 0; i < d->intervals.count(); i++) {
634 if (d->intervals[i].contains(time))
635 return true;
636
637 if (time < d->intervals[i].s)
638 break;
639 }
640
641 return false;
642}
643
644/*!
645 \relates QMediaTimeRange
646
647 Returns true if all intervals in \a a are present in \a b.
648*/
649bool operator==(const QMediaTimeRange &a, const QMediaTimeRange &b)
650{
651 return a.intervals() == b.intervals();
652}
653
654/*!
655 \relates QMediaTimeRange
656
657 Returns true if one or more intervals in \a a are not present in \a b.
658*/
659bool operator!=(const QMediaTimeRange &a, const QMediaTimeRange &b)
660{
661 return !(a == b);
662}
663
664/*!
665 \relates QMediaTimeRange
666
667 Returns a time range containing the union between \a r1 and \a r2.
668 */
669QMediaTimeRange operator+(const QMediaTimeRange &r1, const QMediaTimeRange &r2)
670{
671 return (QMediaTimeRange(r1) += r2);
672}
673
674/*!
675 \relates QMediaTimeRange
676
677 Returns a time range containing \a r2 subtracted from \a r1.
678 */
679QMediaTimeRange operator-(const QMediaTimeRange &r1, const QMediaTimeRange &r2)
680{
681 return (QMediaTimeRange(r1) -= r2);
682}
683
684#ifndef QT_NO_DEBUG_STREAM
685QDebug operator<<(QDebug dbg, const QMediaTimeRange &range)
686{
687 QDebugStateSaver saver(dbg);
688 dbg.nospace();
689 dbg << "QMediaTimeRange( ";
690 const auto intervals = range.intervals();
691 for (const QMediaTimeInterval &interval : intervals)
692 dbg << '(' << interval.start() << ", " << interval.end() << ") ";
693 dbg.space();
694 dbg << ')';
695 return dbg;
696}
697#endif
698
699QT_END_NAMESPACE
700
701