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 #ifndef DOM_MEDIA_INTERVALS_H_
8 #define DOM_MEDIA_INTERVALS_H_
9 
10 #include <algorithm>
11 
12 #include "nsTArray.h"
13 
14 // Specialization for nsTArray CopyChooser.
15 namespace mozilla {
16 namespace media {
17 template <class T>
18 class IntervalSet;
19 }  // namespace media
20 }  // namespace mozilla
21 
22 template <class E>
23 struct nsTArray_RelocationStrategy<mozilla::media::IntervalSet<E>> {
24   typedef nsTArray_RelocateUsingMoveConstructor<mozilla::media::IntervalSet<E>>
25       Type;
26 };
27 
28 namespace mozilla {
29 namespace media {
30 
31 /* Interval defines an interval between two points. Unlike a traditional
32    interval [A,B] where A <= x <= B, the upper boundary B is exclusive: A <= x <
33    B (e.g [A,B[ or [A,B) depending on where you're living) It provides basic
34    interval arithmetic and fuzzy edges. The type T must provides a default
35    constructor and +, -, <, <= and == operators.
36  */
37 template <typename T>
38 class Interval {
39  public:
40   typedef Interval<T> SelfType;
41 
42   Interval() : mStart(T()), mEnd(T()), mFuzz(T()) {}
43 
44   template <typename StartArg, typename EndArg>
45   Interval(StartArg&& aStart, EndArg&& aEnd)
46       : mStart(std::forward<StartArg>(aStart)),
47         mEnd(std::forward<EndArg>(aEnd)),
48         mFuzz() {
49     MOZ_DIAGNOSTIC_ASSERT(mStart <= mEnd, "Invalid Interval");
50   }
51 
52   template <typename StartArg, typename EndArg, typename FuzzArg>
53   Interval(StartArg&& aStart, EndArg&& aEnd, FuzzArg&& aFuzz)
54       : mStart(std::forward<StartArg>(aStart)),
55         mEnd(std::forward<EndArg>(aEnd)),
56         mFuzz(std::forward<FuzzArg>(aFuzz)) {
57     MOZ_DIAGNOSTIC_ASSERT(mStart <= mEnd, "Invalid Interval");
58   }
59 
60   Interval(const SelfType& aOther)
61       : mStart(aOther.mStart), mEnd(aOther.mEnd), mFuzz(aOther.mFuzz) {}
62 
63   Interval(SelfType&& aOther)
64       : mStart(std::move(aOther.mStart)),
65         mEnd(std::move(aOther.mEnd)),
66         mFuzz(std::move(aOther.mFuzz)) {}
67 
68   SelfType& operator=(const SelfType& aOther) {
69     mStart = aOther.mStart;
70     mEnd = aOther.mEnd;
71     mFuzz = aOther.mFuzz;
72     return *this;
73   }
74 
75   SelfType& operator=(SelfType&& aOther) {
76     MOZ_ASSERT(&aOther != this, "self-moves are prohibited");
77     this->~Interval();
78     new (this) Interval(std::move(aOther));
79     return *this;
80   }
81 
82   // Basic interval arithmetic operator definition.
83   SelfType operator+(const SelfType& aOther) const {
84     return SelfType(mStart + aOther.mStart, mEnd + aOther.mEnd,
85                     mFuzz + aOther.mFuzz);
86   }
87 
88   SelfType operator+(const T& aVal) const {
89     return SelfType(mStart + aVal, mEnd + aVal, mFuzz);
90   }
91 
92   SelfType operator-(const SelfType& aOther) const {
93     return SelfType(mStart - aOther.mEnd, mEnd - aOther.mStart,
94                     mFuzz + aOther.mFuzz);
95   }
96 
97   SelfType operator-(const T& aVal) const {
98     return SelfType(mStart - aVal, mEnd - aVal, mFuzz);
99   }
100 
101   SelfType& operator+=(const SelfType& aOther) {
102     mStart += aOther.mStart;
103     mEnd += aOther.mEnd;
104     mFuzz += aOther.mFuzz;
105     return *this;
106   }
107 
108   SelfType& operator+=(const T& aVal) {
109     mStart += aVal;
110     mEnd += aVal;
111     return *this;
112   }
113 
114   SelfType& operator-=(const SelfType& aOther) {
115     mStart -= aOther.mStart;
116     mEnd -= aOther.mEnd;
117     mFuzz += aOther.mFuzz;
118     return *this;
119   }
120 
121   SelfType& operator-=(const T& aVal) {
122     mStart -= aVal;
123     mEnd -= aVal;
124     return *this;
125   }
126 
127   bool operator==(const SelfType& aOther) const {
128     return mStart == aOther.mStart && mEnd == aOther.mEnd;
129   }
130 
131   bool operator!=(const SelfType& aOther) const { return !(*this == aOther); }
132 
133   bool Contains(const T& aX) const {
134     return mStart - mFuzz <= aX && aX < mEnd + mFuzz;
135   }
136 
137   bool ContainsStrict(const T& aX) const { return mStart <= aX && aX < mEnd; }
138 
139   bool ContainsWithStrictEnd(const T& aX) const {
140     return mStart - mFuzz <= aX && aX < mEnd;
141   }
142 
143   bool Contains(const SelfType& aOther) const {
144     return (mStart - mFuzz <= aOther.mStart + aOther.mFuzz) &&
145            (aOther.mEnd - aOther.mFuzz <= mEnd + mFuzz);
146   }
147 
148   bool ContainsStrict(const SelfType& aOther) const {
149     return mStart <= aOther.mStart && aOther.mEnd <= mEnd;
150   }
151 
152   bool ContainsWithStrictEnd(const SelfType& aOther) const {
153     return (mStart - mFuzz <= aOther.mStart + aOther.mFuzz) &&
154            aOther.mEnd <= mEnd;
155   }
156 
157   bool Intersects(const SelfType& aOther) const {
158     return (mStart - mFuzz < aOther.mEnd + aOther.mFuzz) &&
159            (aOther.mStart - aOther.mFuzz < mEnd + mFuzz);
160   }
161 
162   bool IntersectsStrict(const SelfType& aOther) const {
163     return mStart < aOther.mEnd && aOther.mStart < mEnd;
164   }
165 
166   // Same as Intersects, but including the boundaries.
167   bool Touches(const SelfType& aOther) const {
168     return (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz) &&
169            (aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
170   }
171 
172   // Returns true if aOther is strictly to the right of this and contiguous.
173   // This operation isn't commutative.
174   bool Contiguous(const SelfType& aOther) const {
175     return mEnd <= aOther.mStart &&
176            aOther.mStart - mEnd <= mFuzz + aOther.mFuzz;
177   }
178 
179   bool RightOf(const SelfType& aOther) const {
180     return aOther.mEnd - aOther.mFuzz <= mStart + mFuzz;
181   }
182 
183   bool LeftOf(const SelfType& aOther) const {
184     return mEnd - mFuzz <= aOther.mStart + aOther.mFuzz;
185   }
186 
187   SelfType Span(const SelfType& aOther) const {
188     if (IsEmpty()) {
189       return aOther;
190     }
191     SelfType result(*this);
192     if (aOther.mStart < mStart) {
193       result.mStart = aOther.mStart;
194     }
195     if (mEnd < aOther.mEnd) {
196       result.mEnd = aOther.mEnd;
197     }
198     if (mFuzz < aOther.mFuzz) {
199       result.mFuzz = aOther.mFuzz;
200     }
201     return result;
202   }
203 
204   SelfType Intersection(const SelfType& aOther) const {
205     const T& s = std::max(mStart, aOther.mStart);
206     const T& e = std::min(mEnd, aOther.mEnd);
207     const T& f = std::max(mFuzz, aOther.mFuzz);
208     if (s < e) {
209       return SelfType(s, e, f);
210     }
211     // Return an empty interval.
212     return SelfType();
213   }
214 
215   T Length() const { return mEnd - mStart; }
216 
217   bool IsEmpty() const { return mStart == mEnd; }
218 
219   void SetFuzz(const T& aFuzz) { mFuzz = aFuzz; }
220 
221   // Returns true if the two intervals intersect with this being on the right
222   // of aOther
223   bool TouchesOnRight(const SelfType& aOther) const {
224     return aOther.mStart <= mStart &&
225            (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz) &&
226            (aOther.mStart - aOther.mFuzz <= mEnd + mFuzz);
227   }
228 
229   // Returns true if the two intervals intersect with this being on the right
230   // of aOther, ignoring fuzz.
231   bool TouchesOnRightStrict(const SelfType& aOther) const {
232     return aOther.mStart <= mStart && mStart <= aOther.mEnd;
233   }
234 
235   T mStart;
236   T mEnd;
237   T mFuzz;
238 
239  private:
240 };
241 
242 // An IntervalSet in a collection of Intervals. The IntervalSet is always
243 // normalized.
244 template <typename T>
245 class IntervalSet {
246  public:
247   typedef IntervalSet<T> SelfType;
248   typedef Interval<T> ElemType;
249   typedef AutoTArray<ElemType, 4> ContainerType;
250   typedef typename ContainerType::index_type IndexType;
251 
252   IntervalSet() = default;
253   virtual ~IntervalSet() = default;
254 
255   IntervalSet(const SelfType& aOther) : mIntervals(aOther.mIntervals.Clone()) {}
256 
257   IntervalSet(SelfType&& aOther) {
258     mIntervals.AppendElements(std::move(aOther.mIntervals));
259   }
260 
261   explicit IntervalSet(const ElemType& aOther) {
262     if (!aOther.IsEmpty()) {
263       mIntervals.AppendElement(aOther);
264     }
265   }
266 
267   explicit IntervalSet(ElemType&& aOther) {
268     if (!aOther.IsEmpty()) {
269       mIntervals.AppendElement(std::move(aOther));
270     }
271   }
272 
273   bool operator==(const SelfType& aOther) const {
274     return mIntervals == aOther.mIntervals;
275   }
276 
277   bool operator!=(const SelfType& aOther) const {
278     return mIntervals != aOther.mIntervals;
279   }
280 
281   SelfType& operator=(const SelfType& aOther) {
282     mIntervals = aOther.mIntervals.Clone();
283     return *this;
284   }
285 
286   SelfType& operator=(SelfType&& aOther) {
287     MOZ_ASSERT(&aOther != this, "self-moves are prohibited");
288     this->~IntervalSet();
289     new (this) IntervalSet(std::move(aOther));
290     return *this;
291   }
292 
293   SelfType& operator=(const ElemType& aInterval) {
294     mIntervals.Clear();
295     if (!aInterval.IsEmpty()) {
296       mIntervals.AppendElement(aInterval);
297     }
298     return *this;
299   }
300 
301   SelfType& operator=(ElemType&& aInterval) {
302     mIntervals.Clear();
303     if (!aInterval.IsEmpty()) {
304       mIntervals.AppendElement(std::move(aInterval));
305     }
306     return *this;
307   }
308 
309   SelfType& Add(const SelfType& aIntervals) {
310     if (aIntervals.mIntervals.Length() == 1) {
311       Add(aIntervals.mIntervals[0]);
312     } else {
313       mIntervals.AppendElements(aIntervals.mIntervals);
314       Normalize();
315     }
316     return *this;
317   }
318 
319   SelfType& Add(const ElemType& aInterval) {
320     if (aInterval.IsEmpty()) {
321       return *this;
322     }
323     if (mIntervals.IsEmpty()) {
324       mIntervals.AppendElement(aInterval);
325       return *this;
326     }
327     ElemType& last = mIntervals.LastElement();
328     if (aInterval.TouchesOnRight(last)) {
329       last = last.Span(aInterval);
330       return *this;
331     }
332     // Most of our actual usage is adding an interval that will be outside the
333     // range. We can speed up normalization here.
334     if (aInterval.RightOf(last)) {
335       mIntervals.AppendElement(aInterval);
336       return *this;
337     }
338 
339     ContainerType normalized;
340     ElemType current(aInterval);
341     IndexType i = 0;
342     for (; i < mIntervals.Length(); i++) {
343       ElemType& interval = mIntervals[i];
344       if (current.Touches(interval)) {
345         current = current.Span(interval);
346       } else if (current.LeftOf(interval)) {
347         break;
348       } else {
349         normalized.AppendElement(std::move(interval));
350       }
351     }
352     normalized.AppendElement(std::move(current));
353     for (; i < mIntervals.Length(); i++) {
354       normalized.AppendElement(std::move(mIntervals[i]));
355     }
356     mIntervals.Clear();
357     mIntervals.AppendElements(std::move(normalized));
358 
359     return *this;
360   }
361 
362   SelfType& operator+=(const SelfType& aIntervals) {
363     Add(aIntervals);
364     return *this;
365   }
366 
367   SelfType& operator+=(const ElemType& aInterval) {
368     Add(aInterval);
369     return *this;
370   }
371 
372   SelfType operator+(const SelfType& aIntervals) const {
373     SelfType intervals(*this);
374     intervals.Add(aIntervals);
375     return intervals;
376   }
377 
378   SelfType operator+(const ElemType& aInterval) const {
379     SelfType intervals(*this);
380     intervals.Add(aInterval);
381     return intervals;
382   }
383 
384   friend SelfType operator+(const ElemType& aInterval,
385                             const SelfType& aIntervals) {
386     SelfType intervals;
387     intervals.Add(aInterval);
388     intervals.Add(aIntervals);
389     return intervals;
390   }
391 
392   // Excludes an interval from an IntervalSet.
393   SelfType& operator-=(const ElemType& aInterval) {
394     if (aInterval.IsEmpty() || mIntervals.IsEmpty()) {
395       return *this;
396     }
397     if (mIntervals.Length() == 1 &&
398         mIntervals[0].TouchesOnRightStrict(aInterval)) {
399       // Fast path when we're removing from the front of a set with a
400       // single interval. This is common for the buffered time ranges
401       // we see on Twitch.
402       if (aInterval.mEnd >= mIntervals[0].mEnd) {
403         mIntervals.RemoveElementAt(0);
404       } else {
405         mIntervals[0].mStart = aInterval.mEnd;
406         mIntervals[0].mFuzz = std::max(mIntervals[0].mFuzz, aInterval.mFuzz);
407       }
408       return *this;
409     }
410 
411     // General case performed by inverting aInterval within the bounds of
412     // mIntervals and then doing the intersection.
413     T firstEnd = std::max(mIntervals[0].mStart, aInterval.mStart);
414     T secondStart = std::min(mIntervals.LastElement().mEnd, aInterval.mEnd);
415     ElemType startInterval(mIntervals[0].mStart, firstEnd);
416     ElemType endInterval(secondStart, mIntervals.LastElement().mEnd);
417     SelfType intervals(std::move(startInterval));
418     intervals += std::move(endInterval);
419     return Intersection(intervals);
420   }
421 
422   SelfType& operator-=(const SelfType& aIntervals) {
423     for (const auto& interval : aIntervals.mIntervals) {
424       *this -= interval;
425     }
426     return *this;
427   }
428 
429   SelfType operator-(const SelfType& aInterval) const {
430     SelfType intervals(*this);
431     intervals -= aInterval;
432     return intervals;
433   }
434 
435   SelfType operator-(const ElemType& aInterval) const {
436     SelfType intervals(*this);
437     intervals -= aInterval;
438     return intervals;
439   }
440 
441   // Mutate this IntervalSet to be the union of this and aOther.
442   SelfType& Union(const SelfType& aOther) {
443     Add(aOther);
444     return *this;
445   }
446 
447   SelfType& Union(const ElemType& aInterval) {
448     Add(aInterval);
449     return *this;
450   }
451 
452   // Mutate this TimeRange to be the intersection of this and aOther.
453   SelfType& Intersection(const SelfType& aOther) {
454     ContainerType intersection;
455 
456     // Ensure the intersection has enough capacity to store the upper bound on
457     // the intersection size. This ensures that we don't spend time reallocating
458     // the storage as we append, at the expense of extra memory.
459     intersection.SetCapacity(std::max(aOther.Length(), mIntervals.Length()));
460 
461     const ContainerType& other = aOther.mIntervals;
462     IndexType i = 0, j = 0;
463     for (; i < mIntervals.Length() && j < other.Length();) {
464       if (mIntervals[i].IntersectsStrict(other[j])) {
465         intersection.AppendElement(mIntervals[i].Intersection(other[j]));
466       }
467       if (mIntervals[i].mEnd < other[j].mEnd) {
468         i++;
469       } else {
470         j++;
471       }
472     }
473     mIntervals = std::move(intersection);
474     return *this;
475   }
476 
477   SelfType& Intersection(const ElemType& aInterval) {
478     SelfType intervals(aInterval);
479     return Intersection(intervals);
480   }
481 
482   const ElemType& operator[](IndexType aIndex) const {
483     return mIntervals[aIndex];
484   }
485 
486   // Returns the start boundary of the first interval. Or a default constructed
487   // T if IntervalSet is empty (and aExists if provided will be set to false).
488   T GetStart(bool* aExists = nullptr) const {
489     bool exists = !mIntervals.IsEmpty();
490 
491     if (aExists) {
492       *aExists = exists;
493     }
494 
495     if (exists) {
496       return mIntervals[0].mStart;
497     } else {
498       return T();
499     }
500   }
501 
502   // Returns the end boundary of the last interval. Or a default constructed T
503   // if IntervalSet is empty (and aExists if provided will be set to false).
504   T GetEnd(bool* aExists = nullptr) const {
505     bool exists = !mIntervals.IsEmpty();
506     if (aExists) {
507       *aExists = exists;
508     }
509 
510     if (exists) {
511       return mIntervals.LastElement().mEnd;
512     } else {
513       return T();
514     }
515   }
516 
517   IndexType Length() const { return mIntervals.Length(); }
518 
519   bool IsEmpty() const { return mIntervals.IsEmpty(); }
520 
521   T Start(IndexType aIndex) const { return mIntervals[aIndex].mStart; }
522 
523   T Start(IndexType aIndex, bool& aExists) const {
524     aExists = aIndex < mIntervals.Length();
525 
526     if (aExists) {
527       return mIntervals[aIndex].mStart;
528     } else {
529       return T();
530     }
531   }
532 
533   T End(IndexType aIndex) const { return mIntervals[aIndex].mEnd; }
534 
535   T End(IndexType aIndex, bool& aExists) const {
536     aExists = aIndex < mIntervals.Length();
537 
538     if (aExists) {
539       return mIntervals[aIndex].mEnd;
540     } else {
541       return T();
542     }
543   }
544 
545   bool Contains(const ElemType& aInterval) const {
546     for (const auto& interval : mIntervals) {
547       if (interval.Contains(aInterval)) {
548         return true;
549       }
550     }
551     return false;
552   }
553 
554   bool ContainsStrict(const ElemType& aInterval) const {
555     for (const auto& interval : mIntervals) {
556       if (interval.ContainsStrict(aInterval)) {
557         return true;
558       }
559     }
560     return false;
561   }
562 
563   bool Contains(const T& aX) const {
564     for (const auto& interval : mIntervals) {
565       if (interval.Contains(aX)) {
566         return true;
567       }
568     }
569     return false;
570   }
571 
572   bool ContainsStrict(const T& aX) const {
573     for (const auto& interval : mIntervals) {
574       if (interval.ContainsStrict(aX)) {
575         return true;
576       }
577     }
578     return false;
579   }
580 
581   bool ContainsWithStrictEnd(const T& aX) const {
582     for (const auto& interval : mIntervals) {
583       if (interval.ContainsWithStrictEnd(aX)) {
584         return true;
585       }
586     }
587     return false;
588   }
589 
590   bool ContainsWithStrictEnd(const ElemType& aInterval) const {
591     for (const auto& interval : mIntervals) {
592       if (interval.ContainsWithStrictEnd(aInterval)) {
593         return true;
594       }
595     }
596     return false;
597   }
598 
599   bool Intersects(const ElemType& aInterval) const {
600     for (const auto& interval : mIntervals) {
601       if (interval.Intersects(aInterval)) {
602         return true;
603       }
604     }
605     return false;
606   }
607 
608   bool IntersectsStrict(const ElemType& aInterval) const {
609     for (const auto& interval : mIntervals) {
610       if (interval.IntersectsStrict(aInterval)) {
611         return true;
612       }
613     }
614     return false;
615   }
616 
617   // Returns if there's any intersection between this and aOther.
618   bool IntersectsStrict(const SelfType& aOther) const {
619     const ContainerType& other = aOther.mIntervals;
620     IndexType i = 0, j = 0;
621     for (; i < mIntervals.Length() && j < other.Length();) {
622       if (mIntervals[i].IntersectsStrict(other[j])) {
623         return true;
624       }
625       if (mIntervals[i].mEnd < other[j].mEnd) {
626         i++;
627       } else {
628         j++;
629       }
630     }
631     return false;
632   }
633 
634   bool IntersectsWithStrictEnd(const ElemType& aInterval) const {
635     for (const auto& interval : mIntervals) {
636       if (interval.IntersectsWithStrictEnd(aInterval)) {
637         return true;
638       }
639     }
640     return false;
641   }
642 
643   // Shift all values by aOffset.
644   SelfType& Shift(const T& aOffset) {
645     for (auto& interval : mIntervals) {
646       interval.mStart = interval.mStart + aOffset;
647       interval.mEnd = interval.mEnd + aOffset;
648     }
649     return *this;
650   }
651 
652   void SetFuzz(const T& aFuzz) {
653     for (auto& interval : mIntervals) {
654       interval.SetFuzz(aFuzz);
655     }
656     MergeOverlappingIntervals();
657   }
658 
659   static const IndexType NoIndex = IndexType(-1);
660 
661   IndexType Find(const T& aValue) const {
662     for (IndexType i = 0; i < mIntervals.Length(); i++) {
663       if (mIntervals[i].Contains(aValue)) {
664         return i;
665       }
666     }
667     return NoIndex;
668   }
669 
670   // Methods for range-based for loops.
671   typename ContainerType::iterator begin() { return mIntervals.begin(); }
672 
673   typename ContainerType::const_iterator begin() const {
674     return mIntervals.begin();
675   }
676 
677   typename ContainerType::iterator end() { return mIntervals.end(); }
678 
679   typename ContainerType::const_iterator end() const {
680     return mIntervals.end();
681   }
682 
683   ElemType& LastInterval() {
684     MOZ_ASSERT(!mIntervals.IsEmpty());
685     return mIntervals.LastElement();
686   }
687 
688   const ElemType& LastInterval() const {
689     MOZ_ASSERT(!mIntervals.IsEmpty());
690     return mIntervals.LastElement();
691   }
692 
693   void Clear() { mIntervals.Clear(); }
694 
695  protected:
696   ContainerType mIntervals;
697 
698  private:
699   void Normalize() {
700     if (mIntervals.Length() < 2) {
701       return;
702     }
703     mIntervals.Sort(CompareIntervals());
704     MergeOverlappingIntervals();
705   }
706 
707   void MergeOverlappingIntervals() {
708     if (mIntervals.Length() < 2) {
709       return;
710     }
711 
712     // This merges the intervals in place.
713     IndexType read = 0;
714     IndexType write = 0;
715     while (read < mIntervals.Length()) {
716       ElemType current(mIntervals[read]);
717       read++;
718       while (read < mIntervals.Length() && current.Touches(mIntervals[read])) {
719         current = current.Span(mIntervals[read]);
720         read++;
721       }
722       mIntervals[write] = current;
723       write++;
724     }
725     mIntervals.SetLength(write);
726   }
727 
728   struct CompareIntervals {
729     bool Equals(const ElemType& aT1, const ElemType& aT2) const {
730       return aT1.mStart == aT2.mStart && aT1.mEnd == aT2.mEnd;
731     }
732 
733     bool LessThan(const ElemType& aT1, const ElemType& aT2) const {
734       return aT1.mStart - aT1.mFuzz < aT2.mStart + aT2.mFuzz;
735     }
736   };
737 };
738 
739 // clang doesn't allow for this to be defined inline of IntervalSet.
740 template <typename T>
741 IntervalSet<T> Union(const IntervalSet<T>& aIntervals1,
742                      const IntervalSet<T>& aIntervals2) {
743   IntervalSet<T> intervals(aIntervals1);
744   intervals.Union(aIntervals2);
745   return intervals;
746 }
747 
748 template <typename T>
749 IntervalSet<T> Intersection(const IntervalSet<T>& aIntervals1,
750                             const IntervalSet<T>& aIntervals2) {
751   IntervalSet<T> intersection(aIntervals1);
752   intersection.Intersection(aIntervals2);
753   return intersection;
754 }
755 
756 }  // namespace media
757 }  // namespace mozilla
758 
759 #endif  // DOM_MEDIA_INTERVALS_H_
760