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

source code of qtmultimedia/src/multimedia/qmediatimerange.cpp