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