Revision control

Copy as Markdown

// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/qdebug.h>
#include "qmediatimerange.h"
QT_BEGIN_NAMESPACE
/*!
\class QMediaTimeRange::Interval
\brief The QMediaTimeRange::Interval class represents a time interval with
integer precision.
\inmodule QtMultimedia
\ingroup multimedia
\ingroup multimedia_core
An interval is specified by an inclusive start() and end() time. These
must be set in the constructor, as this is an immutable class. The
specific units of time represented by the class have not been defined - it
is suitable for any times which can be represented by a signed 64 bit
integer.
The isNormal() method determines if a time interval is normal (a normal
time interval has start() <= end()). A normal interval can be received
from an abnormal interval by calling the normalized() method.
The contains() method determines if a specified time lies within the time
interval.
The translated() method returns a time interval which has been translated
forwards or backwards through time by a specified offset.
\sa QMediaTimeRange
*/
/*!
\fn QMediaTimeRange::Interval::Interval(qint64 start, qint64 end)
Constructs an interval with the specified \a start and \a end times.
*/
/*!
\fn QMediaTimeRange::Interval::start() const
Returns the start time of the interval.
\sa end()
*/
/*!
\fn QMediaTimeRange::Interval::end() const
Returns the end time of the interval.
\sa start()
*/
/*!
\fn QMediaTimeRange::Interval::contains(qint64 time) const
Returns true if the time interval contains the specified \a time.
That is, start() <= time <= end().
*/
/*!
\fn QMediaTimeRange::Interval::isNormal() const
Returns true if this time interval is normal.
A normal time interval has start() <= end().
\sa normalized()
*/
/*!
\fn QMediaTimeRange::Interval::normalized() const
Returns a normalized version of this interval.
If the start() time of the interval is greater than the end() time,
then the returned interval has the start and end times swapped.
*/
/*!
\fn QMediaTimeRange::Interval::translated(qint64 offset) const
Returns a copy of this time interval, translated by a value of \a offset.
An interval can be moved forward through time with a positive offset, or backward
through time with a negative offset.
*/
/*!
\fn bool QMediaTimeRange::Interval::operator==(QMediaTimeRange::Interval lhs, QMediaTimeRange::Interval rhs)
Returns true if \a lhs is exactly equal to \a rhs.
*/
/*!
\fn bool QMediaTimeRange::Interval::operator!=(QMediaTimeRange::Interval lhs, QMediaTimeRange::Interval rhs)
Returns true if \a lhs is not exactly equal to \a rhs.
*/
class QMediaTimeRangePrivate : public QSharedData
{
public:
QMediaTimeRangePrivate() = default;
QMediaTimeRangePrivate(const QMediaTimeRange::Interval &interval);
QList<QMediaTimeRange::Interval> intervals;
void addInterval(const QMediaTimeRange::Interval &interval);
void removeInterval(const QMediaTimeRange::Interval &interval);
};
QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QMediaTimeRangePrivate);
QMediaTimeRangePrivate::QMediaTimeRangePrivate(const QMediaTimeRange::Interval &interval)
{
if (interval.isNormal())
intervals << interval;
}
void QMediaTimeRangePrivate::addInterval(const QMediaTimeRange::Interval &interval)
{
// Handle normalized intervals only
if (!interval.isNormal())
return;
// Find a place to insert the interval
int i;
for (i = 0; i < intervals.size(); i++) {
// Insert before this element
if(interval.s < intervals[i].s) {
intervals.insert(i, interval);
break;
}
}
// Interval needs to be added to the end of the list
if (i == intervals.size())
intervals.append(interval);
// Do we need to correct the element before us?
if (i > 0 && intervals[i - 1].e >= interval.s - 1)
i--;
// Merge trailing ranges
while (i < intervals.size() - 1
&& intervals[i].e >= intervals[i + 1].s - 1) {
intervals[i].e = qMax(intervals[i].e, intervals[i + 1].e);
intervals.removeAt(i + 1);
}
}
void QMediaTimeRangePrivate::removeInterval(const QMediaTimeRange::Interval &interval)
{
// Handle normalized intervals only
if (!interval.isNormal())
return;
for (int i = 0; i < intervals.size(); i++) {
const QMediaTimeRange::Interval r = intervals.at(i);
if (r.e < interval.s) {
// Before the removal interval
continue;
} else if (interval.e < r.s) {
// After the removal interval - stop here
break;
} else if (r.s < interval.s && interval.e < r.e) {
// Split case - a single range has a chunk removed
intervals[i].e = interval.s -1;
addInterval(QMediaTimeRange::Interval(interval.e + 1, r.e));
break;
} else if (r.s < interval.s) {
// Trimming Tail Case
intervals[i].e = interval.s - 1;
} else if (interval.e < r.e) {
// Trimming Head Case - we can stop after this
intervals[i].s = interval.e + 1;
break;
} else {
// Complete coverage case
intervals.removeAt(i);
--i;
}
}
}
/*!
\class QMediaTimeRange
\brief The QMediaTimeRange class represents a set of zero or more disjoint
time intervals.
\ingroup multimedia
\inmodule QtMultimedia
\reentrant
The earliestTime(), latestTime(), intervals() and isEmpty()
methods are used to get information about the current time range.
The addInterval(), removeInterval() and clear() methods are used to modify
the current time range.
When adding or removing intervals from the time range, existing intervals
within the range may be expanded, trimmed, deleted, merged or split to ensure
that all intervals within the time range remain distinct and disjoint. As a
consequence, all intervals added or removed from a time range must be
\l{QMediaTimeRange::Interval::isNormal()}{normal}.
\sa QMediaTimeRange::Interval
*/
/*!
Constructs an empty time range.
*/
QMediaTimeRange::QMediaTimeRange()
: d(new QMediaTimeRangePrivate)
{
}
/*!
Constructs a time range that contains an initial interval from
\a start to \a end inclusive.
If the interval is not \l{QMediaTimeRange::Interval::isNormal()}{normal},
the resulting time range will be empty.
\sa addInterval()
*/
QMediaTimeRange::QMediaTimeRange(qint64 start, qint64 end)
: QMediaTimeRange(Interval(start, end))
{
}
/*!
Constructs a time range that contains an initial interval, \a interval.
If \a interval is not \l{QMediaTimeRange::Interval::isNormal()}{normal},
the resulting time range will be empty.
\sa addInterval()
*/
QMediaTimeRange::QMediaTimeRange(const QMediaTimeRange::Interval &interval)
: d(new QMediaTimeRangePrivate(interval))
{
}
/*!
Constructs a time range by copying another time \a range.
*/
QMediaTimeRange::QMediaTimeRange(const QMediaTimeRange &range) noexcept = default;
/*! \fn QMediaTimeRange::QMediaTimeRange(QMediaTimeRange &&other)
Constructs a time range by moving from \a other.
*/
/*!
\fn void QMediaTimeRange::swap(QMediaTimeRange &other) noexcept
Swaps the current instance with the \a other.
*/
/*!
\fn QMediaTimeRange::~QMediaTimeRange()
Destructor.
*/
QMediaTimeRange::~QMediaTimeRange() = default;
/*!
Takes a copy of the \a other time range and returns itself.
*/
QMediaTimeRange &QMediaTimeRange::operator=(const QMediaTimeRange &other) noexcept = default;
/*!
\fn QMediaTimeRange &QMediaTimeRange::operator=(QMediaTimeRange &&other)
Moves \a other into this time range.
*/
/*!
Sets the time range to a single continuous interval, \a interval.
*/
QMediaTimeRange &QMediaTimeRange::operator=(const QMediaTimeRange::Interval &interval)
{
d = new QMediaTimeRangePrivate(interval);
return *this;
}
/*!
Returns the earliest time within the time range.
For empty time ranges, this value is equal to zero.
\sa latestTime()
*/
qint64 QMediaTimeRange::earliestTime() const
{
if (!d->intervals.isEmpty())
return d->intervals[0].start();
return 0;
}
/*!
Returns the latest time within the time range.
For empty time ranges, this value is equal to zero.
\sa earliestTime()
*/
qint64 QMediaTimeRange::latestTime() const
{
if (!d->intervals.isEmpty())
return d->intervals[d->intervals.size() - 1].end();
return 0;
}
/*!
\overload
Adds the interval specified by \a start and \a end
to the time range.
\sa addInterval()
*/
void QMediaTimeRange::addInterval(qint64 start, qint64 end)
{
detach();
d->addInterval(Interval(start, end));
}
/*!
Adds the specified \a interval to the time range.
Adding intervals which are not \l{QMediaTimeRange::Interval::isNormal()}{normal}
is invalid, and will be ignored.
If the specified interval is adjacent to, or overlaps existing
intervals within the time range, these intervals will be merged.
This operation takes linear time.
\sa removeInterval()
*/
void QMediaTimeRange::addInterval(const QMediaTimeRange::Interval &interval)
{
detach();
d->addInterval(interval);
}
/*!
Adds each of the intervals in \a range to this time range.
Equivalent to calling addInterval() for each interval in \a range.
*/
void QMediaTimeRange::addTimeRange(const QMediaTimeRange &range)
{
detach();
const auto intervals = range.intervals();
for (const Interval &i : intervals) {
d->addInterval(i);
}
}
/*!
\overload
Removes the interval specified by \a start and \a end
from the time range.
\sa removeInterval()
*/
void QMediaTimeRange::removeInterval(qint64 start, qint64 end)
{
detach();
d->removeInterval(Interval(start, end));
}
/*!
\fn QMediaTimeRange::removeInterval(const QMediaTimeRange::Interval &interval)
Removes the specified \a interval from the time range.
Removing intervals which are not \l{QMediaTimeRange::Interval::isNormal()}{normal}
is invalid, and will be ignored.
Intervals within the time range will be trimmed, split or deleted
such that no intervals within the time range include any part of the
target interval.
This operation takes linear time.
\sa addInterval()
*/
void QMediaTimeRange::removeInterval(const QMediaTimeRange::Interval &interval)
{
detach();
d->removeInterval(interval);
}
/*!
Removes each of the intervals in \a range from this time range.
Equivalent to calling removeInterval() for each interval in \a range.
*/
void QMediaTimeRange::removeTimeRange(const QMediaTimeRange &range)
{
detach();
const auto intervals = range.intervals();
for (const Interval &i : intervals) {
d->removeInterval(i);
}
}
/*!
Adds each interval in \a other to the time range and returns the result.
*/
QMediaTimeRange& QMediaTimeRange::operator+=(const QMediaTimeRange &other)
{
addTimeRange(other);
return *this;
}
/*!
Adds the specified \a interval to the time range and returns the result.
*/
QMediaTimeRange& QMediaTimeRange::operator+=(const QMediaTimeRange::Interval &interval)
{
addInterval(interval);
return *this;
}
/*!
Removes each interval in \a other from the time range and returns the result.
*/
QMediaTimeRange& QMediaTimeRange::operator-=(const QMediaTimeRange &other)
{
removeTimeRange(other);
return *this;
}
/*!
Removes the specified \a interval from the time range and returns the result.
*/
QMediaTimeRange& QMediaTimeRange::operator-=(const QMediaTimeRange::Interval &interval)
{
removeInterval(interval);
return *this;
}
/*!
\fn QMediaTimeRange::clear()
Removes all intervals from the time range.
\sa removeInterval()
*/
void QMediaTimeRange::clear()
{
detach();
d->intervals.clear();
}
/*!
\internal
*/
void QMediaTimeRange::detach()
{
d.detach();
}
/*!
\fn QMediaTimeRange::intervals() const
Returns the list of intervals covered by this time range.
*/
QList<QMediaTimeRange::Interval> QMediaTimeRange::intervals() const
{
return d->intervals;
}
/*!
\fn QMediaTimeRange::isEmpty() const
Returns true if there are no intervals within the time range.
\sa intervals()
*/
bool QMediaTimeRange::isEmpty() const
{
return d->intervals.isEmpty();
}
/*!
\fn QMediaTimeRange::isContinuous() const
Returns true if the time range consists of a continuous interval.
That is, there is one or fewer disjoint intervals within the time range.
*/
bool QMediaTimeRange::isContinuous() const
{
return (d->intervals.size() <= 1);
}
/*!
\fn QMediaTimeRange::contains(qint64 time) const
Returns true if the specified \a time lies within the time range.
*/
bool QMediaTimeRange::contains(qint64 time) const
{
for (int i = 0; i < d->intervals.size(); i++) {
if (d->intervals[i].contains(time))
return true;
if (time < d->intervals[i].start())
break;
}
return false;
}
/*!
\fn bool QMediaTimeRange::operator==(const QMediaTimeRange &lhs, const QMediaTimeRange &rhs)
Returns true if all intervals in \a lhs are present in \a rhs.
*/
/*!
\fn bool QMediaTimeRange::operator!=(const QMediaTimeRange &lhs, const QMediaTimeRange &rhs)
Returns true if one or more intervals in \a lhs are not present in \a rhs.
*/
/*!
\fn QMediaTimeRange operator+(const QMediaTimeRange &r1, const QMediaTimeRange &r2)
\relates QMediaTimeRange
Returns a time range containing the union between \a r1 and \a r2.
*/
/*!
\fn QMediaTimeRange operator-(const QMediaTimeRange &r1, const QMediaTimeRange &r2)
\relates QMediaTimeRange
Returns a time range containing \a r2 subtracted from \a r1.
*/
#ifndef QT_NO_DEBUG_STREAM
QDebug operator<<(QDebug dbg, const QMediaTimeRange::Interval &interval)
{
QDebugStateSaver saver(dbg);
dbg.nospace();
dbg << "QMediaTimeRange::Interval( " << interval.start() << ", " << interval.end() << " )";
return dbg;
}
QDebug operator<<(QDebug dbg, const QMediaTimeRange &range)
{
QDebugStateSaver saver(dbg);
dbg.nospace();
dbg << "QMediaTimeRange( ";
const auto intervals = range.intervals();
for (const auto &interval : intervals)
dbg << '(' << interval.start() << ", " << interval.end() << ") ";
dbg.space();
dbg << ')';
return dbg;
}
#endif
QT_END_NAMESPACE