Source: lib/offline/indexeddb/v2_storage_cell.js

  1. /*! @license
  2. * Shaka Player
  3. * Copyright 2016 Google LLC
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. goog.provide('shaka.offline.indexeddb.V2StorageCell');
  7. goog.require('shaka.offline.indexeddb.BaseStorageCell');
  8. goog.require('shaka.util.PeriodCombiner');
  9. /**
  10. * The V2StorageCell is for all stores that follow the shaka.externs V2 and V3
  11. * offline types. V2 was introduced in Shaka Player v2.3.0 and quickly
  12. * replaced with V3 in Shaka Player v2.3.2. V3 was then deprecated in v3.0.
  13. *
  14. * Upgrading from V1 to V2 initially broke the database in a way that prevented
  15. * adding new records. The problem was with the upgrade process, not with the
  16. * database format. Once database upgrades were removed, we increased the
  17. * database version to V3 and marked V2 as read-only. Therefore, V2 and V3
  18. * databases can both be read by this cell.
  19. *
  20. * The manifest and segment stores didn't change in database V4, but a separate
  21. * table for session IDs was added. So this cell also covers DB V4.
  22. *
  23. * @implements {shaka.extern.StorageCell}
  24. */
  25. shaka.offline.indexeddb.V2StorageCell = class
  26. extends shaka.offline.indexeddb.BaseStorageCell {
  27. /**
  28. * @override
  29. * @param {shaka.extern.ManifestDBV2} old
  30. * @return {!Promise.<shaka.extern.ManifestDB>}
  31. */
  32. async convertManifest(old) {
  33. const streamsPerPeriod = [];
  34. for (let i = 0; i < old.periods.length; ++i) {
  35. // The last period ends at the end of the presentation.
  36. const periodEnd = i == old.periods.length - 1 ?
  37. old.duration : old.periods[i + 1].startTime;
  38. const duration = periodEnd - old.periods[i].startTime;
  39. const streams = this.convertPeriod_(old.periods[i], duration);
  40. streamsPerPeriod.push(streams);
  41. }
  42. const streams = await shaka.util.PeriodCombiner.combineDbStreams(
  43. streamsPerPeriod);
  44. return {
  45. appMetadata: old.appMetadata,
  46. creationTime: 0,
  47. drmInfo: old.drmInfo,
  48. duration: old.duration,
  49. // JSON serialization turns Infinity into null, so turn it back now.
  50. expiration: old.expiration == null ? Infinity : old.expiration,
  51. originalManifestUri: old.originalManifestUri,
  52. sessionIds: old.sessionIds,
  53. size: old.size,
  54. streams,
  55. };
  56. }
  57. /**
  58. * @param {shaka.extern.PeriodDBV2} period
  59. * @param {number} periodDuration
  60. * @return {!Array.<shaka.extern.StreamDB>}
  61. * @private
  62. */
  63. convertPeriod_(period, periodDuration) {
  64. const streams = [];
  65. for (const stream of period.streams) {
  66. // The v4 version of the database as written by v2.5.0 - v2.5.9 might have
  67. // been corrupted slightly. A bug caused the stream metadata from all
  68. // periods to be written to each period. This was corrected in v2.5.10.
  69. // To fix this, we can identify the extra streams by their lack of
  70. // variantIds and skip them.
  71. if (stream.variantIds.length == 0) {
  72. continue;
  73. }
  74. streams.push(this.convertStream_(
  75. stream, period.startTime, period.startTime + periodDuration));
  76. }
  77. return streams;
  78. }
  79. /**
  80. * @param {shaka.extern.StreamDBV2} old
  81. * @param {number} periodStart
  82. * @param {number} periodEnd
  83. * @return {shaka.extern.StreamDB}
  84. * @private
  85. */
  86. convertStream_(old, periodStart, periodEnd) {
  87. return {
  88. id: old.id,
  89. originalId: old.originalId,
  90. primary: old.primary,
  91. type: old.contentType,
  92. mimeType: old.mimeType,
  93. codecs: old.codecs,
  94. frameRate: old.frameRate,
  95. pixelAspectRatio: old.pixelAspectRatio,
  96. hdr: undefined,
  97. kind: old.kind,
  98. language: old.language,
  99. label: old.label,
  100. width: old.width,
  101. height: old.height,
  102. encrypted: old.encrypted,
  103. keyIds: new Set([old.keyId]),
  104. segments: old.segments.map((segment) =>
  105. this.convertSegment_(
  106. segment, old.initSegmentKey, periodStart, periodEnd,
  107. old.presentationTimeOffset)),
  108. variantIds: old.variantIds,
  109. roles: [],
  110. forced: false,
  111. audioSamplingRate: null,
  112. channelsCount: null,
  113. spatialAudio: false,
  114. closedCaptions: null,
  115. tilesLayout: undefined,
  116. };
  117. }
  118. /**
  119. * @param {shaka.extern.SegmentDBV2} old
  120. * @param {?number} initSegmentKey
  121. * @param {number} periodStart
  122. * @param {number} periodEnd
  123. * @param {number} presentationTimeOffset
  124. * @return {shaka.extern.SegmentDB}
  125. * @private
  126. */
  127. convertSegment_(
  128. old, initSegmentKey, periodStart, periodEnd, presentationTimeOffset) {
  129. const timestampOffset = periodStart - presentationTimeOffset;
  130. return {
  131. startTime: periodStart + old.startTime,
  132. endTime: periodStart + old.endTime,
  133. initSegmentKey,
  134. appendWindowStart: periodStart,
  135. appendWindowEnd: periodEnd,
  136. timestampOffset,
  137. dataKey: old.dataKey,
  138. };
  139. }
  140. };