TimeRanges.cpp (5149B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/TimeRanges.h" 8 9 #include "TimeUnits.h" 10 #include "mozilla/dom/HTMLMediaElement.h" 11 #include "mozilla/dom/TimeRangesBinding.h" 12 #include "nsError.h" 13 14 namespace mozilla::dom { 15 16 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(TimeRanges, mParent) 17 NS_IMPL_CYCLE_COLLECTING_ADDREF(TimeRanges) 18 NS_IMPL_CYCLE_COLLECTING_RELEASE(TimeRanges) 19 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TimeRanges) 20 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 21 NS_INTERFACE_MAP_ENTRY(nsISupports) 22 NS_INTERFACE_MAP_END 23 24 TimeRanges::TimeRanges() : mParent(nullptr) {} 25 26 TimeRanges::TimeRanges(nsISupports* aParent) : mParent(aParent) {} 27 28 TimeRanges::TimeRanges(nsISupports* aParent, 29 const media::TimeIntervals& aTimeIntervals) 30 : TimeRanges(aParent) { 31 if (aTimeIntervals.IsInvalid()) { 32 return; 33 } 34 for (const media::TimeInterval& interval : aTimeIntervals) { 35 Add(interval.mStart.ToSeconds(), interval.mEnd.ToSeconds()); 36 } 37 } 38 39 TimeRanges::TimeRanges(nsISupports* aParent, 40 const media::TimeRanges& aTimeRanges) 41 : TimeRanges(aParent) { 42 if (aTimeRanges.IsInvalid()) { 43 return; 44 } 45 for (const media::TimeRange& interval : aTimeRanges) { 46 Add(interval.mStart, interval.mEnd); 47 } 48 } 49 50 TimeRanges::TimeRanges(const media::TimeIntervals& aTimeIntervals) 51 : TimeRanges(nullptr, aTimeIntervals) {} 52 53 TimeRanges::TimeRanges(const media::TimeRanges& aTimeRanges) 54 : TimeRanges(nullptr, aTimeRanges) {} 55 56 media::TimeIntervals TimeRanges::ToTimeIntervals() const { 57 media::TimeIntervals t; 58 for (uint32_t i = 0; i < Length(); i++) { 59 t += media::TimeInterval(media::TimeUnit::FromSeconds(Start(i)), 60 media::TimeUnit::FromSeconds(End(i))); 61 } 62 return t; 63 } 64 65 TimeRanges::~TimeRanges() = default; 66 67 double TimeRanges::Start(uint32_t aIndex, ErrorResult& aRv) const { 68 if (aIndex >= mRanges.Length()) { 69 aRv = NS_ERROR_DOM_INDEX_SIZE_ERR; 70 return 0; 71 } 72 73 return Start(aIndex); 74 } 75 76 double TimeRanges::End(uint32_t aIndex, ErrorResult& aRv) const { 77 if (aIndex >= mRanges.Length()) { 78 aRv = NS_ERROR_DOM_INDEX_SIZE_ERR; 79 return 0; 80 } 81 82 return End(aIndex); 83 } 84 85 void TimeRanges::Add(double aStart, double aEnd) { 86 if (aStart > aEnd) { 87 NS_WARNING("Can't add a range if the end is older that the start."); 88 return; 89 } 90 mRanges.AppendElement(TimeRange(aStart, aEnd)); 91 } 92 93 double TimeRanges::GetStartTime() { 94 if (mRanges.IsEmpty()) { 95 return -1.0; 96 } 97 return mRanges[0].mStart; 98 } 99 100 double TimeRanges::GetEndTime() { 101 if (mRanges.IsEmpty()) { 102 return -1.0; 103 } 104 return mRanges[mRanges.Length() - 1].mEnd; 105 } 106 107 void TimeRanges::Normalize(double aTolerance) { 108 if (mRanges.Length() >= 2) { 109 AutoTArray<TimeRange, 4> normalized; 110 111 mRanges.Sort(CompareTimeRanges()); 112 113 // This merges the intervals. 114 TimeRange current(mRanges[0]); 115 for (uint32_t i = 1; i < mRanges.Length(); i++) { 116 if (current.mStart <= mRanges[i].mStart && 117 current.mEnd >= mRanges[i].mEnd) { 118 continue; 119 } 120 if (current.mEnd + aTolerance >= mRanges[i].mStart) { 121 current.mEnd = mRanges[i].mEnd; 122 } else { 123 normalized.AppendElement(current); 124 current = mRanges[i]; 125 } 126 } 127 128 normalized.AppendElement(current); 129 130 mRanges = std::move(normalized); 131 } 132 } 133 134 void TimeRanges::Union(const TimeRanges* aOtherRanges, double aTolerance) { 135 mRanges.AppendElements(aOtherRanges->mRanges); 136 Normalize(aTolerance); 137 } 138 139 void TimeRanges::Intersection(const TimeRanges* aOtherRanges) { 140 AutoTArray<TimeRange, 4> intersection; 141 142 const nsTArray<TimeRange>& otherRanges = aOtherRanges->mRanges; 143 for (index_type i = 0, j = 0; 144 i < mRanges.Length() && j < otherRanges.Length();) { 145 double start = std::max(mRanges[i].mStart, otherRanges[j].mStart); 146 double end = std::min(mRanges[i].mEnd, otherRanges[j].mEnd); 147 if (start < end) { 148 intersection.AppendElement(TimeRange(start, end)); 149 } 150 if (mRanges[i].mEnd < otherRanges[j].mEnd) { 151 i += 1; 152 } else { 153 j += 1; 154 } 155 } 156 157 mRanges = std::move(intersection); 158 } 159 160 TimeRanges::index_type TimeRanges::Find(double aTime, 161 double aTolerance /* = 0 */) { 162 for (index_type i = 0; i < mRanges.Length(); ++i) { 163 if (aTime < mRanges[i].mEnd && (aTime + aTolerance) >= mRanges[i].mStart) { 164 return i; 165 } 166 } 167 return NoIndex; 168 } 169 170 JSObject* TimeRanges::WrapObject(JSContext* aCx, 171 JS::Handle<JSObject*> aGivenProto) { 172 return TimeRanges_Binding::Wrap(aCx, this, aGivenProto); 173 } 174 175 nsISupports* TimeRanges::GetParentObject() const { return mParent; } 176 177 void TimeRanges::Shift(double aOffset) { 178 for (index_type i = 0; i < mRanges.Length(); ++i) { 179 mRanges[i].mStart += aOffset; 180 mRanges[i].mEnd += aOffset; 181 } 182 } 183 184 } // namespace mozilla::dom