Source: lib/media/time_ranges_utils.js

  1. /**
  2. * @license
  3. * Copyright 2016 Google Inc.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. goog.provide('shaka.media.TimeRangesUtils');
  18. goog.require('shaka.util.Platform');
  19. /**
  20. * @namespace shaka.media.TimeRangesUtils
  21. * @summary A set of utility functions for dealing with TimeRanges objects.
  22. */
  23. /**
  24. * Gets the first timestamp in the buffer.
  25. *
  26. * @param {TimeRanges} b
  27. * @return {?number} The first buffered timestamp, in seconds, if |buffered|
  28. * is non-empty; otherwise, return null.
  29. */
  30. shaka.media.TimeRangesUtils.bufferStart = function(b) {
  31. if (!b) return null;
  32. // Workaround Safari bug: https://bit.ly/2trx6O8
  33. if (b.length == 1 && b.end(0) - b.start(0) < 1e-6) return null;
  34. // Workaround Edge bug: https://bit.ly/2JYLPeB
  35. if (b.length == 1 && b.start(0) < 0) return 0;
  36. return b.length ? b.start(0) : null;
  37. };
  38. /**
  39. * Gets the last timestamp in the buffer.
  40. *
  41. * @param {TimeRanges} b
  42. * @return {?number} The last buffered timestamp, in seconds, if |buffered|
  43. * is non-empty; otherwise, return null.
  44. */
  45. shaka.media.TimeRangesUtils.bufferEnd = function(b) {
  46. if (!b) return null;
  47. // Workaround Safari bug: https://bit.ly/2trx6O8
  48. if (b.length == 1 && b.end(0) - b.start(0) < 1e-6) return null;
  49. return b.length ? b.end(b.length - 1) : null;
  50. };
  51. /**
  52. * Determines if the given time is inside a buffered range. This includes gaps,
  53. * meaning that if the playhead is in a gap, it is considered buffered. If there
  54. * is a small gap between the playhead and buffer start, consider it as
  55. * buffered.
  56. *
  57. * @param {TimeRanges} b
  58. * @param {number} time Playhead time
  59. * @param {number=} smallGapLimit Set in configuration
  60. * @return {boolean}
  61. */
  62. shaka.media.TimeRangesUtils.isBuffered = function(b, time, smallGapLimit = 0) {
  63. if (!b || !b.length) return false;
  64. // Workaround Safari bug: https://bit.ly/2trx6O8
  65. if (b.length == 1 && b.end(0) - b.start(0) < 1e-6) return false;
  66. if (time > b.end(b.length - 1)) {
  67. return false;
  68. }
  69. // Push the time forward by the gap limit so that it is more likely to be in
  70. // the range.
  71. return (time + smallGapLimit >= b.start(0));
  72. };
  73. /**
  74. * Computes how far ahead of the given timestamp is buffered. To provide smooth
  75. * playback while jumping gaps, we don't include the gaps when calculating this.
  76. * This only includes the amount of content that is buffered.
  77. *
  78. * @param {TimeRanges} b
  79. * @param {number} time
  80. * @return {number} The number of seconds buffered, in seconds, ahead of the
  81. * given time.
  82. */
  83. shaka.media.TimeRangesUtils.bufferedAheadOf = function(b, time) {
  84. if (!b || !b.length) return 0;
  85. // Workaround Safari bug: https://bit.ly/2trx6O8
  86. if (b.length == 1 && b.end(0) - b.start(0) < 1e-6) return 0;
  87. // NOTE: On IE11, buffered ranges may show appended data before the associated
  88. // append operation is complete.
  89. // We calculate the buffered amount by ONLY accounting for the content
  90. // buffered (i.e. we ignore the times of the gaps). We also buffer through
  91. // all gaps.
  92. // Therefore, we start at the end and add up all buffers until |time|.
  93. let result = 0;
  94. for (let i = b.length - 1; i >= 0 && b.end(i) > time; --i) {
  95. result += b.end(i) - Math.max(b.start(i), time);
  96. }
  97. return result;
  98. };
  99. /**
  100. * Determines if the given time is inside a gap between buffered ranges. If it
  101. * is, this returns the index of the buffer that is *ahead* of the gap.
  102. *
  103. * @param {TimeRanges} b
  104. * @param {number} time
  105. * @return {?number} The index of the buffer after the gap, or null if not in a
  106. * gap.
  107. */
  108. shaka.media.TimeRangesUtils.getGapIndex = function(b, time) {
  109. const Platform = shaka.util.Platform;
  110. if (!b || !b.length) return null;
  111. // Workaround Safari bug: https://bit.ly/2trx6O8
  112. if (b.length == 1 && b.end(0) - b.start(0) < 1e-6) return null;
  113. // Some browsers will stop earlier than others before a gap (e.g. IE/Edge
  114. // stops 0.5 seconds before a gap). So for some browsers we need to use a
  115. // larger threshold. See: https://bit.ly/2K5xmJO
  116. const useLargeThreshold = Platform.isLegacyEdge() ||
  117. Platform.isIE() ||
  118. Platform.isTizen() ||
  119. Platform.isChromecast();
  120. const threshold = useLargeThreshold ? 0.5 : 0.1;
  121. for (let i = 0; i < b.length; i++) {
  122. if (b.start(i) > time && (i == 0 || b.end(i - 1) - time <= threshold)) {
  123. return i;
  124. }
  125. }
  126. return null;
  127. };
  128. /**
  129. * @param {TimeRanges} b
  130. * @return {!Array.<shaka.extern.BufferedRange>}
  131. */
  132. shaka.media.TimeRangesUtils.getBufferedInfo = function(b) {
  133. if (!b) return [];
  134. let ret = [];
  135. for (let i = 0; i < b.length; i++) {
  136. ret.push({start: b.start(i), end: b.end(i)});
  137. }
  138. return ret;
  139. };