Source: lib/media/mp4_segment_index_parser.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.Mp4SegmentIndexParser');
  18. goog.require('goog.asserts');
  19. goog.require('shaka.log');
  20. goog.require('shaka.media.SegmentReference');
  21. goog.require('shaka.util.DataViewReader');
  22. goog.require('shaka.util.Error');
  23. // TODO: Add unit tests for this.
  24. /**
  25. * Creates an MP4 SIDX parser.
  26. *
  27. * @constructor
  28. * @struct
  29. */
  30. shaka.media.Mp4SegmentIndexParser = function() {};
  31. /**
  32. * Indicates the SIDX box structure. It is equal to the string 'sidx' as a
  33. * 32-bit unsigned integer.
  34. * @const {number}
  35. */
  36. shaka.media.Mp4SegmentIndexParser.SIDX_INDICATOR = 0x73696478;
  37. /**
  38. * Parses SegmentReferences from an ISO BMFF SIDX structure.
  39. * @param {!ArrayBuffer} sidxData The MP4's container's SIDX.
  40. * @param {number} sidxOffset The SIDX's offset, in bytes, from the start of
  41. * the MP4 container.
  42. * @param {!Array.<string>} uris The possible locations of the MP4 file that
  43. * contains the segments.
  44. * @param {number} presentationTimeOffset
  45. * @return {!Array.<!shaka.media.SegmentReference>}
  46. * @throws {shaka.util.Error}
  47. */
  48. shaka.media.Mp4SegmentIndexParser.prototype.parse = function(
  49. sidxData, sidxOffset, uris, presentationTimeOffset) {
  50. var references = [];
  51. var reader = new shaka.util.DataViewReader(
  52. new DataView(sidxData),
  53. shaka.util.DataViewReader.Endianness.BIG_ENDIAN);
  54. // A SIDX structure is contained within a FullBox structure, which itself is
  55. // contained within a Box structure.
  56. // Parse the Box structure.
  57. var boxSize = reader.readUint32();
  58. var boxType = reader.readUint32();
  59. if (boxType != shaka.media.Mp4SegmentIndexParser.SIDX_INDICATOR) {
  60. shaka.log.error('Invalid box type, expected "sidx".');
  61. throw new shaka.util.Error(
  62. shaka.util.Error.Category.MEDIA,
  63. shaka.util.Error.Code.MP4_SIDX_WRONG_BOX_TYPE);
  64. }
  65. if (boxSize == 1) {
  66. boxSize = reader.readUint64();
  67. }
  68. // Parse the FullBox structure.
  69. var version = reader.readUint8();
  70. // Skip flags (24 bits)
  71. reader.skip(3);
  72. // Parse the SIDX structure.
  73. // Skip reference_ID (32 bits).
  74. reader.skip(4);
  75. var timescale = reader.readUint32();
  76. goog.asserts.assert(timescale != 0, 'timescale cannot be 0');
  77. if (timescale == 0) {
  78. shaka.log.error('Invalid timescale.');
  79. throw new shaka.util.Error(
  80. shaka.util.Error.Category.MEDIA,
  81. shaka.util.Error.Code.MP4_SIDX_INVALID_TIMESCALE);
  82. }
  83. var earliestPresentationTime;
  84. var firstOffset;
  85. if (version == 0) {
  86. earliestPresentationTime = reader.readUint32();
  87. firstOffset = reader.readUint32();
  88. } else {
  89. earliestPresentationTime = reader.readUint64();
  90. firstOffset = reader.readUint64();
  91. }
  92. // Skip reserved (16 bits).
  93. reader.skip(2);
  94. // Add references.
  95. var referenceCount = reader.readUint16();
  96. // Substract the presentationTimeOffset
  97. var unscaledStartTime = earliestPresentationTime - presentationTimeOffset;
  98. var startByte = sidxOffset + boxSize + firstOffset;
  99. for (var i = 0; i < referenceCount; i++) {
  100. // |chunk| is 1 bit for |referenceType|, and 31 bits for |referenceSize|.
  101. var chunk = reader.readUint32();
  102. var referenceType = (chunk & 0x80000000) >>> 31;
  103. var referenceSize = chunk & 0x7FFFFFFF;
  104. var subsegmentDuration = reader.readUint32();
  105. // Skipping 1 bit for |startsWithSap|, 3 bits for |sapType|, and 28 bits
  106. // for |sapDelta|.
  107. reader.skip(4);
  108. // If |referenceType| is 1 then the reference is to another SIDX.
  109. // We do not support this.
  110. if (referenceType == 1) {
  111. shaka.log.error('Heirarchical SIDXs are not supported.');
  112. throw new shaka.util.Error(
  113. shaka.util.Error.Category.MEDIA,
  114. shaka.util.Error.Code.MP4_SIDX_TYPE_NOT_SUPPORTED);
  115. }
  116. references.push(
  117. new shaka.media.SegmentReference(
  118. references.length,
  119. unscaledStartTime / timescale,
  120. (unscaledStartTime + subsegmentDuration) / timescale,
  121. uris,
  122. startByte,
  123. startByte + referenceSize - 1));
  124. unscaledStartTime += subsegmentDuration;
  125. startByte += referenceSize;
  126. }
  127. return references;
  128. };