shaka-player 4.4.2 → 4.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/demo/common/asset.js +5 -0
  3. package/demo/common/assets.js +20 -5
  4. package/demo/common/message_ids.js +1 -1
  5. package/demo/locales/en.json +2 -1
  6. package/demo/locales/source.json +5 -1
  7. package/demo/main.js +1 -1
  8. package/demo/search.js +3 -3
  9. package/dist/demo.compiled.debug.js +72 -70
  10. package/dist/demo.compiled.debug.map +3 -3
  11. package/dist/demo.compiled.js +72 -70
  12. package/dist/demo.compiled.map +3 -3
  13. package/dist/locales.js +30 -30
  14. package/dist/receiver.compiled.debug.js +38 -36
  15. package/dist/receiver.compiled.debug.map +3 -3
  16. package/dist/receiver.compiled.js +38 -36
  17. package/dist/receiver.compiled.map +3 -3
  18. package/dist/shaka-player.compiled.d.ts +222 -1
  19. package/dist/shaka-player.compiled.debug.d.ts +222 -1
  20. package/dist/shaka-player.compiled.debug.externs.js +271 -1
  21. package/dist/shaka-player.compiled.debug.js +233 -228
  22. package/dist/shaka-player.compiled.debug.map +3 -3
  23. package/dist/shaka-player.compiled.externs.js +271 -1
  24. package/dist/shaka-player.compiled.js +1035 -1028
  25. package/dist/shaka-player.compiled.map +3 -3
  26. package/dist/shaka-player.ui.d.ts +222 -1
  27. package/dist/shaka-player.ui.debug.d.ts +222 -1
  28. package/dist/shaka-player.ui.debug.externs.js +271 -1
  29. package/dist/shaka-player.ui.debug.js +256 -251
  30. package/dist/shaka-player.ui.debug.map +3 -3
  31. package/dist/shaka-player.ui.externs.js +271 -1
  32. package/dist/shaka-player.ui.js +1162 -1155
  33. package/dist/shaka-player.ui.map +3 -3
  34. package/docs/tutorials/offline.md +1 -1
  35. package/lib/dash/dash_parser.js +4 -1
  36. package/lib/hls/hls_parser.js +60 -24
  37. package/lib/media/segment_index.js +4 -4
  38. package/lib/media/segment_reference.js +6 -1
  39. package/lib/media/streaming_engine.js +14 -24
  40. package/lib/player.js +1 -1
  41. package/lib/text/cue.js +47 -47
  42. package/lib/text/vtt_text_parser.js +2 -2
  43. package/lib/util/cmcd_manager.js +6 -1
  44. package/lib/util/manifest_parser_utils.js +10 -4
  45. package/lib/util/platform.js +14 -0
  46. package/lib/util/ts_parser.js +3 -3
  47. package/package.json +1 -1
  48. package/test/dash/dash_parser_manifest_unit.js +5 -0
  49. package/test/dash/dash_parser_segment_base_unit.js +5 -0
  50. package/test/dash/dash_parser_segment_list_unit.js +21 -17
  51. package/test/dash/dash_parser_segment_template_unit.js +47 -0
  52. package/test/hls/hls_live_unit.js +33 -0
  53. package/test/hls/hls_parser_unit.js +2 -57
  54. package/test/test/util/dash_parser_util.js +16 -7
  55. package/test/text/vtt_text_parser_unit.js +2 -17
  56. package/test/transmuxer/transmuxer_integration.js +4 -4
  57. package/test/util/ts_parser_unit.js +1 -1
@@ -537,7 +537,7 @@ function downloadContent(manifestUri, title) {
537
537
  'downloaded': Date()
538
538
  };
539
539
 
540
- return window.storage.store(manifestUri, metadata);
540
+ return window.storage.store(manifestUri, metadata).promise;
541
541
  }
542
542
 
543
543
  /*
@@ -533,7 +533,10 @@ shaka.dash.DashParser = class {
533
533
  // Just update the variants and text streams, which may change as periods
534
534
  // are added or removed.
535
535
  this.manifest_.variants = this.periodCombiner_.getVariants();
536
- this.manifest_.textStreams = this.periodCombiner_.getTextStreams();
536
+ const textStreams = this.periodCombiner_.getTextStreams();
537
+ if (textStreams.length > 0) {
538
+ this.manifest_.textStreams = textStreams;
539
+ }
537
540
  this.manifest_.imageStreams = this.periodCombiner_.getImageStreams();
538
541
 
539
542
  // Re-filter the manifest. This will check any configured restrictions on
@@ -787,7 +787,7 @@ shaka.hls.HlsParser = class {
787
787
  // enough info here to determine the presentation type and duration.
788
788
  if (playlist.type == shaka.hls.PlaylistType.MEDIA) {
789
789
  if (this.isLive_()) {
790
- this.changePresentationTimelineToLive_();
790
+ this.changePresentationTimelineToLive_(playlist);
791
791
  const delay = this.getUpdatePlaylistDelay_();
792
792
  this.updatePlaylistTimer_.tickAfter(/* seconds= */ delay);
793
793
  }
@@ -856,7 +856,7 @@ shaka.hls.HlsParser = class {
856
856
 
857
857
  let segmentUris = [firstSegment.absoluteUri];
858
858
  const initSegmentRef = this.getInitSegmentReference_(
859
- playlist.absoluteUri, firstSegment.tags, new Map());
859
+ playlist, firstSegment.tags, new Map());
860
860
  if (initSegmentRef) {
861
861
  segmentUris = initSegmentRef.getUris();
862
862
  }
@@ -1781,7 +1781,13 @@ shaka.hls.HlsParser = class {
1781
1781
  goog.asserts.assert(tag.name == 'EXT-X-MEDIA',
1782
1782
  'Should only be called on media tags!');
1783
1783
  const languageValue = tag.getAttributeValue('LANGUAGE');
1784
- const language = this.getLanguage_(languageValue);
1784
+ let language = this.getLanguage_(languageValue);
1785
+ if (!languageValue) {
1786
+ const nameValue = tag.getAttributeValue('NAME');
1787
+ if (nameValue) {
1788
+ language = nameValue;
1789
+ }
1790
+ }
1785
1791
 
1786
1792
  // The GROUP-ID value is a quoted-string that specifies the group to which
1787
1793
  // the Rendition belongs.
@@ -1790,7 +1796,9 @@ shaka.hls.HlsParser = class {
1790
1796
  // The value of INSTREAM-ID is a quoted-string that specifies a Rendition
1791
1797
  // within the segments in the Media Playlist. This attribute is REQUIRED
1792
1798
  // if the TYPE attribute is CLOSED-CAPTIONS.
1793
- const instreamId = tag.getRequiredAttrValue('INSTREAM-ID');
1799
+ // We need replace SERVICE string by our internal svc string.
1800
+ const instreamId = tag.getRequiredAttrValue('INSTREAM-ID')
1801
+ .replace('SERVICE', 'svc');
1794
1802
  if (!this.groupIdToClosedCaptionsMap_.get(groupId)) {
1795
1803
  this.groupIdToClosedCaptionsMap_.set(groupId, new Map());
1796
1804
  }
@@ -2054,7 +2062,7 @@ shaka.hls.HlsParser = class {
2054
2062
  if (this.isLive_() && !wasLive) {
2055
2063
  // Now that we know that the presentation is live, convert the timeline
2056
2064
  // to live.
2057
- this.changePresentationTimelineToLive_();
2065
+ this.changePresentationTimelineToLive_(playlist);
2058
2066
  }
2059
2067
 
2060
2068
  // Copy values from the real stream info to our initial one.
@@ -2770,9 +2778,10 @@ shaka.hls.HlsParser = class {
2770
2778
 
2771
2779
 
2772
2780
  /**
2781
+ * @param {!shaka.hls.Playlist} playlist
2773
2782
  * @private
2774
2783
  */
2775
- changePresentationTimelineToLive_() {
2784
+ changePresentationTimelineToLive_(playlist) {
2776
2785
  // The live edge will be calculated from segments, so we don't need to
2777
2786
  // set a presentation start time. We will assert later that this is
2778
2787
  // working as expected.
@@ -2795,8 +2804,12 @@ shaka.hls.HlsParser = class {
2795
2804
  } else if (this.presentationDelay_) {
2796
2805
  presentationDelay = this.presentationDelay_;
2797
2806
  } else {
2798
- const numberOfSegments = this.config_.hls.liveSegmentsDelay;
2799
- presentationDelay = this.maxTargetDuration_ * numberOfSegments;
2807
+ const playlistSegments = playlist.segments.length;
2808
+ let delaySegments = this.config_.hls.liveSegmentsDelay;
2809
+ if (delaySegments > (playlistSegments - 2)) {
2810
+ delaySegments = Math.max(1, playlistSegments - 2);
2811
+ }
2812
+ presentationDelay = this.maxTargetDuration_ * delaySegments;
2800
2813
  }
2801
2814
 
2802
2815
  this.presentationTimeline_.setPresentationStartTime(0);
@@ -2806,13 +2819,13 @@ shaka.hls.HlsParser = class {
2806
2819
 
2807
2820
  /**
2808
2821
  * Get the InitSegmentReference for a segment if it has a EXT-X-MAP tag.
2809
- * @param {string} playlistUri The absolute uri of the media playlist.
2822
+ * @param {!shaka.hls.Playlist} playlist
2810
2823
  * @param {!Array.<!shaka.hls.Tag>} tags Segment tags
2811
2824
  * @param {!Map.<string, string>} variables
2812
2825
  * @return {shaka.media.InitSegmentReference}
2813
2826
  * @private
2814
2827
  */
2815
- getInitSegmentReference_(playlistUri, tags, variables) {
2828
+ getInitSegmentReference_(playlist, tags, variables) {
2816
2829
  /** @type {?shaka.hls.Tag} */
2817
2830
  const mapTag = shaka.hls.Utils.getFirstTagWithName(tags, 'EXT-X-MAP');
2818
2831
 
@@ -2823,7 +2836,7 @@ shaka.hls.HlsParser = class {
2823
2836
  const verbatimInitSegmentUri = mapTag.getRequiredAttrValue('URI');
2824
2837
  const absoluteInitSegmentUri = this.variableSubstitution_(
2825
2838
  shaka.hls.Utils.constructAbsoluteUri(
2826
- playlistUri, verbatimInitSegmentUri),
2839
+ playlist.absoluteUri, verbatimInitSegmentUri),
2827
2840
  variables);
2828
2841
 
2829
2842
  const mapTagKey = [
@@ -2831,8 +2844,18 @@ shaka.hls.HlsParser = class {
2831
2844
  mapTag.getAttributeValue('BYTERANGE', ''),
2832
2845
  ].join('-');
2833
2846
  if (!this.mapTagToInitSegmentRefMap_.has(mapTagKey)) {
2847
+ /** @type {shaka.extern.HlsAes128Key|undefined} */
2848
+ let aes128Key = undefined;
2849
+ for (const tag of tags) {
2850
+ if (tag.name == 'EXT-X-KEY') {
2851
+ if (tag.getRequiredAttrValue('METHOD') == 'AES-128' &&
2852
+ tag.id < mapTag.id) {
2853
+ aes128Key = this.parseAES128DrmTag_(tag, playlist);
2854
+ }
2855
+ }
2856
+ }
2834
2857
  const initSegmentRef = this.createInitSegmentReference_(
2835
- absoluteInitSegmentUri, mapTag);
2858
+ absoluteInitSegmentUri, mapTag, aes128Key);
2836
2859
  this.mapTagToInitSegmentRefMap_.set(mapTagKey, initSegmentRef);
2837
2860
  }
2838
2861
  return this.mapTagToInitSegmentRefMap_.get(mapTagKey);
@@ -2843,10 +2866,11 @@ shaka.hls.HlsParser = class {
2843
2866
  * playlist.
2844
2867
  * @param {string} absoluteInitSegmentUri
2845
2868
  * @param {!shaka.hls.Tag} mapTag EXT-X-MAP
2869
+ * @param {shaka.extern.HlsAes128Key=} aes128Key
2846
2870
  * @return {!shaka.media.InitSegmentReference}
2847
2871
  * @private
2848
2872
  */
2849
- createInitSegmentReference_(absoluteInitSegmentUri, mapTag) {
2873
+ createInitSegmentReference_(absoluteInitSegmentUri, mapTag, aes128Key) {
2850
2874
  let startByte = 0;
2851
2875
  let endByte = null;
2852
2876
  const byterange = mapTag.getAttributeValue('BYTERANGE');
@@ -2857,12 +2881,26 @@ shaka.hls.HlsParser = class {
2857
2881
  const byteLength = Number(blocks[0]);
2858
2882
  startByte = Number(blocks[1]);
2859
2883
  endByte = startByte + byteLength - 1;
2884
+
2885
+ if (aes128Key) {
2886
+ // MAP segment encrypted with method 'AES-128', when served with
2887
+ // HTTP Range, has the unencrypted size specified in the range.
2888
+ // See: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6
2889
+ const length = (endByte + 1) - startByte;
2890
+ if (length % 16) {
2891
+ endByte += (16 - (length % 16));
2892
+ }
2893
+ }
2860
2894
  }
2861
2895
 
2862
2896
  const initSegmentRef = new shaka.media.InitSegmentReference(
2863
2897
  () => [absoluteInitSegmentUri],
2864
2898
  startByte,
2865
- endByte);
2899
+ endByte,
2900
+ /* mediaQuality= */ null,
2901
+ /* timescale= */ null,
2902
+ /* segmentData= */ null,
2903
+ aes128Key);
2866
2904
  return initSegmentRef;
2867
2905
  }
2868
2906
 
@@ -2877,15 +2915,13 @@ shaka.hls.HlsParser = class {
2877
2915
  * @param {string} absoluteMediaPlaylistUri
2878
2916
  * @param {string} type
2879
2917
  * @param {string} mimeType
2880
- * @param {number} timestampOffset
2881
2918
  * @param {shaka.extern.HlsAes128Key=} hlsAes128Key
2882
2919
  * @return {shaka.media.SegmentReference}
2883
2920
  * @private
2884
2921
  */
2885
2922
  createSegmentReference_(
2886
2923
  initSegmentReference, previousReference, hlsSegment, startTime,
2887
- variables, absoluteMediaPlaylistUri, type, mimeType, timestampOffset,
2888
- hlsAes128Key) {
2924
+ variables, absoluteMediaPlaylistUri, type, mimeType, hlsAes128Key) {
2889
2925
  const tags = hlsSegment.tags;
2890
2926
  const extinfTag =
2891
2927
  shaka.hls.Utils.getFirstTagWithName(tags, 'EXTINF');
@@ -3022,7 +3058,7 @@ shaka.hls.HlsParser = class {
3022
3058
  pStartByte,
3023
3059
  pEndByte,
3024
3060
  initSegmentReference,
3025
- /* timestampOffset= */ 0, // This value is ignored in sequence mode.
3061
+ /* timestampOffset= */ 0,
3026
3062
  /* appendWindowStart= */ 0,
3027
3063
  /* appendWindowEnd= */ Infinity,
3028
3064
  /* partialReferences= */ [],
@@ -3067,8 +3103,11 @@ shaka.hls.HlsParser = class {
3067
3103
  return null;
3068
3104
  }
3069
3105
  endTime = startTime + duration;
3070
- } else {
3106
+ } else if (partialSegmentRefs.length) {
3071
3107
  endTime = partialSegmentRefs[partialSegmentRefs.length - 1].endTime;
3108
+ } else {
3109
+ // Skip segments without duration and without partialsegments
3110
+ return null;
3072
3111
  }
3073
3112
 
3074
3113
  if (segmentWithByteRangeOptimization) {
@@ -3131,7 +3170,7 @@ shaka.hls.HlsParser = class {
3131
3170
  startByte,
3132
3171
  endByte,
3133
3172
  initSegmentReference,
3134
- timestampOffset, // This value is ignored in sequence mode.
3173
+ /* timestampOffset= */ 0,
3135
3174
  /* appendWindowStart= */ 0,
3136
3175
  /* appendWindowEnd= */ Infinity,
3137
3176
  partialSegmentRefs,
@@ -3230,7 +3269,6 @@ shaka.hls.HlsParser = class {
3230
3269
  const references = [];
3231
3270
 
3232
3271
  let previousReference = null;
3233
- let lastDiscontinuityStartTime = firstStartTime;
3234
3272
 
3235
3273
  /** @type {!Array.<{bitrate: number, duration: number}>} */
3236
3274
  const bitrates = [];
@@ -3245,7 +3283,6 @@ shaka.hls.HlsParser = class {
3245
3283
  item.tags, 'EXT-X-DISCONTINUITY');
3246
3284
  if (discontinuityTag) {
3247
3285
  discontinuitySequence++;
3248
- lastDiscontinuityStartTime = startTime;
3249
3286
  }
3250
3287
 
3251
3288
  // Apply new AES-128 tags as you see them, keeping a running total.
@@ -3261,7 +3298,7 @@ shaka.hls.HlsParser = class {
3261
3298
 
3262
3299
  mediaSequenceToStartTime.set(position, startTime);
3263
3300
 
3264
- initSegmentRef = this.getInitSegmentReference_(playlist.absoluteUri,
3301
+ initSegmentRef = this.getInitSegmentReference_(playlist,
3265
3302
  item.tags, variables);
3266
3303
 
3267
3304
  // If the stream is low latency and the user has not configured the
@@ -3285,7 +3322,6 @@ shaka.hls.HlsParser = class {
3285
3322
  playlist.absoluteUri,
3286
3323
  type,
3287
3324
  mimeType,
3288
- lastDiscontinuityStartTime,
3289
3325
  hlsAes128Key);
3290
3326
 
3291
3327
  if (reference) {
@@ -708,7 +708,7 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
708
708
  }
709
709
 
710
710
  numPassedInEarlierIndexes += index.numEvicted_ +
711
- index.references.length;
711
+ index.getNumReferences();
712
712
  }
713
713
 
714
714
  return null;
@@ -732,9 +732,9 @@ shaka.media.MetaSegmentIndex = class extends shaka.media.SegmentIndex {
732
732
  return reference;
733
733
  }
734
734
 
735
- numPassedInEarlierIndexes += index.numEvicted_ +
736
- index.references.length;
737
- sawSegments = sawSegments || index.references.length != 0;
735
+ const num = index.getNumReferences();
736
+ numPassedInEarlierIndexes += index.numEvicted_ + num;
737
+ sawSegments = sawSegments || num != 0;
738
738
  }
739
739
 
740
740
  return null;
@@ -32,9 +32,11 @@ shaka.media.InitSegmentReference = class {
32
32
  * the quality of the media associated with this init segment.
33
33
  * @param {(null|number)=} timescale
34
34
  * @param {(null|BufferSource)=} segmentData
35
+ * @param {?shaka.extern.HlsAes128Key=} aes128Key
36
+ * The segment's AES-128-CBC full segment encryption key and iv.
35
37
  */
36
38
  constructor(uris, startByte, endByte, mediaQuality = null, timescale = null,
37
- segmentData = null) {
39
+ segmentData = null, aes128Key = null) {
38
40
  /** @type {function():!Array.<string>} */
39
41
  this.getUris = uris;
40
42
 
@@ -52,6 +54,9 @@ shaka.media.InitSegmentReference = class {
52
54
 
53
55
  /** @type {BufferSource|null} */
54
56
  this.segmentData = segmentData;
57
+
58
+ /** @type {?shaka.extern.HlsAes128Key} */
59
+ this.hlsAes128Key = aes128Key;
55
60
  }
56
61
 
57
62
  /**
@@ -1409,15 +1409,11 @@ shaka.media.StreamingEngine = class {
1409
1409
  'ReadableStream is not supported by the browser.');
1410
1410
  }
1411
1411
  const fetchSegment = this.fetch_(mediaState, reference);
1412
- let result = await fetchSegment;
1412
+ const result = await fetchSegment;
1413
1413
  this.destroyer_.ensureNotDestroyed();
1414
1414
  if (this.fatalError_) {
1415
1415
  return;
1416
1416
  }
1417
- if (reference.hlsAes128Key) {
1418
- goog.asserts.assert(iter, 'mediaState.segmentIterator should exist');
1419
- result = await this.aes128Decrypt_(result, reference, iter);
1420
- }
1421
1417
  this.destroyer_.ensureNotDestroyed();
1422
1418
 
1423
1419
  // If the text stream gets switched between fetch_() and append_(), the
@@ -1502,13 +1498,13 @@ shaka.media.StreamingEngine = class {
1502
1498
 
1503
1499
  /**
1504
1500
  * @param {!BufferSource} rawResult
1505
- * @param {!shaka.media.SegmentReference} reference
1501
+ * @param {shaka.extern.HlsAes128Key} aes128Key
1506
1502
  * @param {!shaka.media.SegmentIterator} iter
1507
1503
  * @return {!Promise.<!BufferSource>} finalResult
1508
1504
  * @private
1509
1505
  */
1510
- async aes128Decrypt_(rawResult, reference, iter) {
1511
- const key = reference.hlsAes128Key;
1506
+ async aes128Decrypt_(rawResult, aes128Key, iter) {
1507
+ const key = aes128Key;
1512
1508
  if (!key.cryptoKey) {
1513
1509
  goog.asserts.assert(key.fetchKey, 'If AES-128 cryptoKey was not ' +
1514
1510
  'preloaded, fetchKey function should be provided');
@@ -1818,10 +1814,10 @@ shaka.media.StreamingEngine = class {
1818
1814
  }
1819
1815
  }
1820
1816
 
1821
- const lastDiscontinuitySequence =
1822
- mediaState.lastSegmentReference ?
1823
- mediaState.lastSegmentReference.discontinuitySequence : null;
1824
1817
  if (this.manifest_.sequenceMode) {
1818
+ const lastDiscontinuitySequence =
1819
+ mediaState.lastSegmentReference ?
1820
+ mediaState.lastSegmentReference.discontinuitySequence : null;
1825
1821
  // Across discontinuity bounds, we should resync timestamps for
1826
1822
  // sequence mode playbacks. The next segment appended should
1827
1823
  // land at its theoretical timestamp from the segment index.
@@ -1831,18 +1827,6 @@ shaka.media.StreamingEngine = class {
1831
1827
  operations.push(this.playerInterface_.mediaSourceEngine.resync(
1832
1828
  mediaState.type, reference.startTime));
1833
1829
  }
1834
- } else if (this.manifest_.ignoreManifestTimestampsInSegmentsMode) {
1835
- // In segments mode, we need to resync to set the timestampOffset
1836
- // to the start of the current discontinuity sequence. This is
1837
- // because individual discontinuity sequences may have internal
1838
- // timestamps that overlap, so we adjust the timestampOffset to avoid
1839
- // having the SourceBuffer get overwritten.
1840
- if (reference.discontinuitySequence != lastDiscontinuitySequence) {
1841
- operations.push(
1842
- this.playerInterface_.mediaSourceEngine.resync(
1843
- mediaState.type,
1844
- reference.timestampOffset));
1845
- }
1846
1830
  }
1847
1831
 
1848
1832
  await Promise.all(operations);
@@ -2200,7 +2184,13 @@ shaka.media.StreamingEngine = class {
2200
2184
  mediaState.operation = op;
2201
2185
  const response = await op.promise;
2202
2186
  mediaState.operation = null;
2203
- return response.data;
2187
+ let result = response.data;
2188
+ if (reference.hlsAes128Key) {
2189
+ const iter = mediaState.segmentIterator;
2190
+ goog.asserts.assert(iter, 'mediaState.segmentIterator should exist');
2191
+ result = await this.aes128Decrypt_(result, reference.hlsAes128Key, iter);
2192
+ }
2193
+ return result;
2204
2194
  }
2205
2195
 
2206
2196
  /**
package/lib/player.js CHANGED
@@ -7175,7 +7175,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7175
7175
  * @define {string} A version number taken from git at compile time.
7176
7176
  * @export
7177
7177
  */
7178
- shaka.Player.version = 'v4.4.2-uncompiled';
7178
+ shaka.Player.version = 'v4.4.3-uncompiled';
7179
7179
 
7180
7180
  // Initialize the deprecation system using the version string we just set
7181
7181
  // on the player.