shaka-player 4.4.1 → 4.4.2

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 (56) hide show
  1. package/.github/workflows/deflake.yaml +8 -5
  2. package/.github/workflows/release-please.yaml +47 -7
  3. package/.github/workflows/shaka-bot-commands/command-test.sh +1 -1
  4. package/CHANGELOG.md +20 -0
  5. package/CONTRIBUTORS +1 -0
  6. package/demo/custom.js +5 -5
  7. package/dist/demo.compiled.debug.js +3 -3
  8. package/dist/demo.compiled.debug.map +1 -1
  9. package/dist/demo.compiled.js +3 -3
  10. package/dist/demo.compiled.map +1 -1
  11. package/dist/deps.js +2 -2
  12. package/dist/locales.js +31 -31
  13. package/dist/shaka-player.compiled.d.ts +311 -295
  14. package/dist/shaka-player.compiled.debug.d.ts +311 -295
  15. package/dist/shaka-player.compiled.debug.externs.js +37 -0
  16. package/dist/shaka-player.compiled.debug.js +145 -135
  17. package/dist/shaka-player.compiled.debug.map +3 -3
  18. package/dist/shaka-player.compiled.externs.js +37 -0
  19. package/dist/shaka-player.compiled.js +887 -884
  20. package/dist/shaka-player.compiled.map +4 -4
  21. package/dist/shaka-player.ui.d.ts +355 -339
  22. package/dist/shaka-player.ui.debug.d.ts +355 -339
  23. package/dist/shaka-player.ui.debug.externs.js +37 -0
  24. package/dist/shaka-player.ui.debug.js +161 -151
  25. package/dist/shaka-player.ui.debug.map +3 -3
  26. package/dist/shaka-player.ui.externs.js +37 -0
  27. package/dist/shaka-player.ui.js +968 -967
  28. package/dist/shaka-player.ui.map +4 -4
  29. package/docs/tutorials/fairplay.md +8 -0
  30. package/docs/tutorials/faq.md +1 -1
  31. package/externs/shaka/text.js +7 -0
  32. package/lib/dash/segment_template.js +3 -0
  33. package/lib/hls/hls_parser.js +8 -4
  34. package/lib/media/media_source_engine.js +1 -1
  35. package/lib/player.js +8 -57
  36. package/lib/text/lrc_text_parser.js +8 -0
  37. package/lib/text/mp4_ttml_parser.js +8 -0
  38. package/lib/text/mp4_vtt_parser.js +8 -0
  39. package/lib/text/sbv_text_parser.js +8 -0
  40. package/lib/text/srt_text_parser.js +8 -0
  41. package/lib/text/ssa_text_parser.js +8 -0
  42. package/lib/text/text_engine.js +8 -1
  43. package/lib/text/ttml_text_parser.js +10 -1
  44. package/lib/text/ui_text_displayer.js +49 -3
  45. package/lib/text/vtt_text_parser.js +21 -5
  46. package/lib/transmuxer/ts_transmuxer.js +44 -43
  47. package/lib/util/error.js +16 -10
  48. package/lib/util/player_configuration.js +2 -2
  49. package/lib/util/stream_utils.js +13 -11
  50. package/lib/util/ts_parser.js +70 -13
  51. package/package.json +2 -2
  52. package/test/text/text_engine_unit.js +4 -2
  53. package/test/text/ttml_text_parser_unit.js +3 -4
  54. package/test/text/vtt_text_parser_unit.js +17 -2
  55. package/test/text/web_vtt_layout_integration.js +0 -1
  56. package/ui/language_utils.js +0 -15
@@ -232,3 +232,11 @@ player.getNetworkingEngine()
232
232
  player.configure('drm.initDataTransform',
233
233
  FairPlayUtils.expressplayInitDataTransform);
234
234
  ```
235
+
236
+ #### Nagra (legacy Apple Media Keys)
237
+
238
+ For integration with Nagra the following can be used:
239
+
240
+ ```js
241
+ shaka.polyfill.PatchedMediaKeysApple.install();
242
+ ```
@@ -153,7 +153,7 @@ In other environments, for example Electron, it is appropriate.
153
153
  In those cases, before Shaka Player loads a manifest, you can register the
154
154
  existing http plugin for `file://` requests:
155
155
  ```js
156
- shaka.net.NetworkingEngine.registerScheme('file', shaka.net.HttpXHRPlugin);
156
+ shaka.net.NetworkingEngine.registerScheme('file', shaka.net.HttpXHRPlugin.parse);
157
157
  ```
158
158
 
159
159
  <hr>
@@ -48,6 +48,13 @@ shaka.extern.TextParser = class {
48
48
  * @param {boolean} sequenceMode
49
49
  */
50
50
  setSequenceMode(sequenceMode) {}
51
+
52
+ /**
53
+ * Notifies the manifest type.
54
+ *
55
+ * @param {string} manifestType
56
+ */
57
+ setManifestType(manifestType) {}
51
58
  };
52
59
 
53
60
 
@@ -868,6 +868,9 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
868
868
  .unscaledPresentationTimeOffset + range.unscaledStart;
869
869
 
870
870
  const createUrisCb = () => {
871
+ if (!this.templateInfo_) {
872
+ return [];
873
+ }
871
874
  return shaka.dash.TimelineSegmentIndex
872
875
  .createUris_(
873
876
  this.templateInfo_.mediaTemplate,
@@ -900,6 +900,7 @@ shaka.hls.HlsParser = class {
900
900
  const uint8ArrayData = shaka.util.BufferUtils.toUint8(response.data);
901
901
  const tsParser = new shaka.util.TsParser().parse(uint8ArrayData);
902
902
  const tsCodecs = tsParser.getCodecs();
903
+ const videoInfo = tsParser.getVideoInfo();
903
904
  const codecs = [];
904
905
  let hasAudio = false;
905
906
  let hasVideo = false;
@@ -923,7 +924,11 @@ shaka.hls.HlsParser = class {
923
924
  }
924
925
  switch (tsCodecs.video) {
925
926
  case 'avc':
926
- codecs.push('avc1.42E01E');
927
+ if (videoInfo.codec) {
928
+ codecs.push(videoInfo.codec);
929
+ } else {
930
+ codecs.push('avc1.42E01E');
931
+ }
927
932
  hasVideo = true;
928
933
  break;
929
934
  case 'hvc':
@@ -935,14 +940,13 @@ shaka.hls.HlsParser = class {
935
940
  return null;
936
941
  }
937
942
  const onlyAudio = hasAudio && !hasVideo;
938
- const videoResolution = tsParser.getVideoResolution();
939
943
  return {
940
944
  type: onlyAudio ? 'audio' : 'video',
941
945
  mimeType: 'video/mp2t',
942
946
  codecs: codecs.join(', '),
943
947
  language: null,
944
- height: videoResolution.height,
945
- width: videoResolution.width,
948
+ height: videoInfo.height,
949
+ width: videoInfo.width,
946
950
  channelCount: null,
947
951
  sampleRate: null,
948
952
  };
@@ -492,7 +492,7 @@ shaka.media.MediaSourceEngine = class {
492
492
  this.textEngine_ = new shaka.text.TextEngine(this.textDisplayer_);
493
493
  }
494
494
  this.textEngine_.initParser(mimeType, sequenceMode,
495
- external || this.segmentRelativeVttTiming_);
495
+ external || this.segmentRelativeVttTiming_, this.manifestType_);
496
496
  }
497
497
 
498
498
  /**
package/lib/player.js CHANGED
@@ -2663,16 +2663,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2663
2663
  return;
2664
2664
  }
2665
2665
 
2666
- this.selectAudioLanguage(preferredAudioLanguage);
2667
-
2668
2666
  const preferredVariantRole = this.config_.preferredVariantRole;
2669
-
2670
- // If the user has not selected a role preference, the previous match is
2671
- // selected.
2672
- if (preferredVariantRole == '') {
2673
- return;
2674
- }
2675
-
2676
2667
  this.selectAudioLanguage(preferredAudioLanguage, preferredVariantRole);
2677
2668
  }
2678
2669
 
@@ -2683,7 +2674,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2683
2674
  */
2684
2675
  setupPreferredTextOnSrc_() {
2685
2676
  const preferredTextLanguage = this.config_.preferredTextLanguage;
2686
- const preferForcedSubs = this.config_.preferForcedSubs;
2687
2677
 
2688
2678
  // If the user has not selected a preference, the browser preference is
2689
2679
  // left.
@@ -2691,16 +2681,9 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
2691
2681
  return;
2692
2682
  }
2693
2683
 
2694
- this.selectTextLanguage(preferredTextLanguage, '', preferForcedSubs);
2695
-
2684
+ const preferForcedSubs = this.config_.preferForcedSubs;
2696
2685
  const preferredTextRole = this.config_.preferredTextRole;
2697
2686
 
2698
- // If the user has not selected a role preference, the previous match is
2699
- // selected.
2700
- if (preferredTextRole == '') {
2701
- return;
2702
- }
2703
-
2704
2687
  this.selectTextLanguage(preferredTextLanguage, preferredTextRole,
2705
2688
  preferForcedSubs);
2706
2689
  }
@@ -4199,8 +4182,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4199
4182
  * @export
4200
4183
  */
4201
4184
  selectAudioLanguage(language, role, channelsCount = 0, safeMargin = 0) {
4202
- const LanguageUtils = shaka.util.LanguageUtils;
4203
-
4204
4185
  if (this.manifest_ && this.playhead_) {
4205
4186
  this.currentAdaptationSetCriteria_ =
4206
4187
  new shaka.media.PreferenceBasedCriteria(language, role || '',
@@ -4238,33 +4219,10 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4238
4219
  // If we haven't switched yet, just use ABR to find a new track.
4239
4220
  this.chooseVariantAndSwitch_();
4240
4221
  } else if (this.video_ && this.video_.audioTracks) {
4241
- const audioTracks = Array.from(this.video_.audioTracks);
4242
- const selectedLanguage = LanguageUtils.normalize(language);
4243
-
4244
- let languageMatch = null;
4245
- let languageAndRoleMatch = null;
4246
-
4247
- for (const audioTrack of audioTracks) {
4248
- const track = shaka.util.StreamUtils.html5AudioTrackToTrack(audioTrack);
4249
-
4250
- if (LanguageUtils.normalize(track.language) == selectedLanguage) {
4251
- languageMatch = audioTrack;
4252
-
4253
- if (role) {
4254
- if (track.roles.includes(role)) {
4255
- languageAndRoleMatch = audioTrack;
4256
- }
4257
- } else { // no role
4258
- if (track.roles.length == 0) {
4259
- languageAndRoleMatch = audioTrack;
4260
- }
4261
- }
4262
- }
4263
- }
4264
- if (languageAndRoleMatch) {
4265
- this.switchHtml5Track_(languageAndRoleMatch);
4266
- } else if (languageMatch) {
4267
- this.switchHtml5Track_(languageMatch);
4222
+ const track = shaka.util.StreamUtils.filterStreamsByLanguageAndRole(
4223
+ this.getVariantTracks(), language, role || '', false)[0];
4224
+ if (track) {
4225
+ this.selectVariantTrack(track);
4268
4226
  }
4269
4227
  }
4270
4228
  }
@@ -4280,8 +4238,6 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4280
4238
  * @export
4281
4239
  */
4282
4240
  selectTextLanguage(language, role, forced = false) {
4283
- const LanguageUtils = shaka.util.LanguageUtils;
4284
-
4285
4241
  if (this.manifest_ && this.playhead_) {
4286
4242
  this.currentTextLanguage_ = language;
4287
4243
  this.currentTextRole_ = role || '';
@@ -4302,13 +4258,8 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4302
4258
  }
4303
4259
  }
4304
4260
  } else {
4305
- const selectedLanguage = LanguageUtils.normalize(language);
4306
-
4307
- const track = this.getTextTracks().find((t) => {
4308
- return LanguageUtils.normalize(t.language) == selectedLanguage &&
4309
- (!role || t.roles.includes(role)) && t.forced == forced;
4310
- });
4311
-
4261
+ const track = shaka.util.StreamUtils.filterStreamsByLanguageAndRole(
4262
+ this.getTextTracks(), language, role || '', forced)[0];
4312
4263
  if (track) {
4313
4264
  this.selectTextTrack(track);
4314
4265
  }
@@ -7224,7 +7175,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7224
7175
  * @define {string} A version number taken from git at compile time.
7225
7176
  * @export
7226
7177
  */
7227
- shaka.Player.version = 'v4.4.1-uncompiled';
7178
+ shaka.Player.version = 'v4.4.2-uncompiled';
7228
7179
 
7229
7180
  // Initialize the deprecation system using the version string we just set
7230
7181
  // on the player.
@@ -36,6 +36,14 @@ shaka.text.LrcTextParser = class {
36
36
  // Unused.
37
37
  }
38
38
 
39
+ /**
40
+ * @override
41
+ * @export
42
+ */
43
+ setManifestType(manifestType) {
44
+ // Unused.
45
+ }
46
+
39
47
  /**
40
48
  * @override
41
49
  * @export
@@ -63,6 +63,14 @@ shaka.text.Mp4TtmlParser = class {
63
63
  // Unused.
64
64
  }
65
65
 
66
+ /**
67
+ * @override
68
+ * @export
69
+ */
70
+ setManifestType(manifestType) {
71
+ // Unused.
72
+ }
73
+
66
74
  /**
67
75
  * @override
68
76
  * @export
@@ -92,6 +92,14 @@ shaka.text.Mp4VttParser = class {
92
92
  // Unused.
93
93
  }
94
94
 
95
+ /**
96
+ * @override
97
+ * @export
98
+ */
99
+ setManifestType(manifestType) {
100
+ // Unused.
101
+ }
102
+
95
103
  /**
96
104
  * @override
97
105
  * @export
@@ -35,6 +35,14 @@ shaka.text.SbvTextParser = class {
35
35
  // Unused.
36
36
  }
37
37
 
38
+ /**
39
+ * @override
40
+ * @export
41
+ */
42
+ setManifestType(manifestType) {
43
+ // Unused.
44
+ }
45
+
38
46
  /**
39
47
  * @override
40
48
  * @export
@@ -43,6 +43,14 @@ shaka.text.SrtTextParser = class {
43
43
  // Unused.
44
44
  }
45
45
 
46
+ /**
47
+ * @override
48
+ * @export
49
+ */
50
+ setManifestType(manifestType) {
51
+ // Unused.
52
+ }
53
+
46
54
  /**
47
55
  * @override
48
56
  * @export
@@ -36,6 +36,14 @@ shaka.text.SsaTextParser = class {
36
36
  // Unused.
37
37
  }
38
38
 
39
+ /**
40
+ * @override
41
+ * @export
42
+ */
43
+ setManifestType(manifestType) {
44
+ // Unused.
45
+ }
46
+
39
47
  /**
40
48
  * @override
41
49
  * @export
@@ -132,8 +132,9 @@ shaka.text.TextEngine = class {
132
132
  * @param {string} mimeType
133
133
  * @param {boolean} sequenceMode
134
134
  * @param {boolean} segmentRelativeVttTiming
135
+ * @param {string} manifestType
135
136
  */
136
- initParser(mimeType, sequenceMode, segmentRelativeVttTiming) {
137
+ initParser(mimeType, sequenceMode, segmentRelativeVttTiming, manifestType) {
137
138
  // No parser for CEA, which is extracted from video and side-loaded
138
139
  // into TextEngine and TextDisplayer.
139
140
  if (mimeType == shaka.util.MimeUtils.CEA608_CLOSED_CAPTION_MIMETYPE ||
@@ -152,6 +153,12 @@ shaka.text.TextEngine = class {
152
153
  shaka.log.alwaysWarn(
153
154
  'Text parsers should have a "setSequenceMode" method!');
154
155
  }
156
+ if (this.parser_.setManifestType) {
157
+ this.parser_.setManifestType(manifestType);
158
+ } else {
159
+ shaka.log.alwaysWarn(
160
+ 'Text parsers should have a "setManifestType" method!');
161
+ }
155
162
  this.segmentRelativeVttTiming_ = segmentRelativeVttTiming;
156
163
  }
157
164
 
@@ -38,6 +38,14 @@ shaka.text.TtmlTextParser = class {
38
38
  // Unused.
39
39
  }
40
40
 
41
+ /**
42
+ * @override
43
+ * @export
44
+ */
45
+ setManifestType(manifestType) {
46
+ // Unused.
47
+ }
48
+
41
49
  /**
42
50
  * @override
43
51
  * @export
@@ -214,11 +222,12 @@ shaka.text.TtmlTextParser = class {
214
222
  }
215
223
  }
216
224
 
217
- const parentIsContent = isContent;
218
225
  if (cueNode.nodeName == 'p' || imageElement) {
219
226
  isContent = true;
220
227
  }
221
228
 
229
+ const parentIsContent = isContent;
230
+
222
231
  const spaceStyle = cueElement.getAttribute('xml:space') ||
223
232
  (whitespaceTrim ? 'default' : 'preserve');
224
233
 
@@ -481,6 +481,43 @@ shaka.text.UITextDisplayer = class {
481
481
  this.currentCuesMap_.set(cue, {cueElement, wrapper, regionElement});
482
482
  }
483
483
 
484
+ /**
485
+ * Compute cue position alignment
486
+ * See https://www.w3.org/TR/webvtt1/#webvtt-cue-position-alignment
487
+ *
488
+ * @param {!shaka.text.Cue} cue
489
+ * @private
490
+ */
491
+ computeCuePositionAlignment_(cue) {
492
+ const Cue = shaka.text.Cue;
493
+ const {direction, positionAlign, textAlign} = cue;
494
+
495
+ if (positionAlign !== Cue.positionAlign.AUTO) {
496
+ // Position align is not AUTO: use it
497
+ return positionAlign;
498
+ }
499
+
500
+ // Position align is AUTO: use text align to compute its value
501
+
502
+ if (textAlign === Cue.textAlign.LEFT ||
503
+ (textAlign === Cue.textAlign.START &&
504
+ direction === Cue.direction.HORIZONTAL_LEFT_TO_RIGHT) ||
505
+ (textAlign === Cue.textAlign.END &&
506
+ direction === Cue.direction.HORIZONTAL_RIGHT_TO_LEFT)) {
507
+ return Cue.positionAlign.LEFT;
508
+ }
509
+
510
+ if (textAlign === Cue.textAlign.RIGHT ||
511
+ (textAlign === Cue.textAlign.START &&
512
+ direction === Cue.direction.HORIZONTAL_RIGHT_TO_LEFT) ||
513
+ (textAlign === Cue.textAlign.END &&
514
+ direction === Cue.direction.HORIZONTAL_LEFT_TO_RIGHT)) {
515
+ return Cue.positionAlign.RIGHT;
516
+ }
517
+
518
+ return Cue.positionAlign.CENTER;
519
+ }
520
+
484
521
  /**
485
522
  * @param {!HTMLElement} cueElement
486
523
  * @param {!shaka.text.Cue} cue
@@ -617,7 +654,15 @@ shaka.text.UITextDisplayer = class {
617
654
  // TODO: Implement line alignment with line number.
618
655
  // TODO: Implement lineAlignment of 'CENTER'.
619
656
  if (cue.line != null) {
620
- if (cue.lineInterpretation == Cue.lineInterpretation.PERCENTAGE) {
657
+ let lineInterpretation = cue.lineInterpretation;
658
+ // HACK: the current implementation of UITextDisplayer only handled
659
+ // PERCENTAGE, as cue.line=0 is zero, we can interpret it as percentage
660
+ // safely.
661
+ if (cue.line == 0 &&
662
+ lineInterpretation == Cue.lineInterpretation.LINE_NUMBER) {
663
+ lineInterpretation = Cue.lineInterpretation.PERCENTAGE;
664
+ }
665
+ if (lineInterpretation == Cue.lineInterpretation.PERCENTAGE) {
621
666
  style.position = 'absolute';
622
667
  if (cue.writingMode == Cue.writingMode.HORIZONTAL_TOP_TO_BOTTOM) {
623
668
  style.width = '100%';
@@ -658,9 +703,10 @@ shaka.text.UITextDisplayer = class {
658
703
 
659
704
  // The positionAlign attribute is an alignment for the text container in
660
705
  // the dimension of the writing direction.
661
- if (cue.positionAlign == Cue.positionAlign.LEFT) {
706
+ const computedPositionAlign = this.computeCuePositionAlignment_(cue);
707
+ if (computedPositionAlign == Cue.positionAlign.LEFT) {
662
708
  style.cssFloat = 'left';
663
- } else if (cue.positionAlign == Cue.positionAlign.RIGHT) {
709
+ } else if (computedPositionAlign == Cue.positionAlign.RIGHT) {
664
710
  style.cssFloat = 'right';
665
711
  }
666
712
 
@@ -8,6 +8,7 @@ goog.provide('shaka.text.VttTextParser');
8
8
 
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.log');
11
+ goog.require('shaka.media.ManifestParser');
11
12
  goog.require('shaka.text.Cue');
12
13
  goog.require('shaka.text.CueRegion');
13
14
  goog.require('shaka.text.TextEngine');
@@ -26,6 +27,9 @@ shaka.text.VttTextParser = class {
26
27
  constructor() {
27
28
  /** @private {boolean} */
28
29
  this.sequenceMode_ = false;
30
+
31
+ /** @private {string} */
32
+ this.manifestType_ = shaka.media.ManifestParser.UNKNOWN;
29
33
  }
30
34
 
31
35
  /**
@@ -44,6 +48,14 @@ shaka.text.VttTextParser = class {
44
48
  this.sequenceMode_ = sequenceMode;
45
49
  }
46
50
 
51
+ /**
52
+ * @override
53
+ * @export
54
+ */
55
+ setManifestType(manifestType) {
56
+ this.manifestType_ = manifestType;
57
+ }
58
+
47
59
  /**
48
60
  * @override
49
61
  * @export
@@ -72,10 +84,9 @@ shaka.text.VttTextParser = class {
72
84
  // HLS content, which should use X-TIMESTAMP-MAP and periodStart instead.
73
85
  let offset = time.vttOffset;
74
86
 
75
- // Only use 'X-TIMESTAMP-MAP' in sequence mode, as that is currently
76
- // shorthand for HLS. Note that an offset based on the first video
87
+ // Only use 'X-TIMESTAMP-MAP' in sequence mode.
88
+ // Note that an offset based on the first video
77
89
  // timestamp has already been extracted, and appears in periodStart.
78
- // The relative offset from X-TIMESTAMP-MAP will be added to that for HLS.
79
90
  if (blocks[0].includes('X-TIMESTAMP-MAP') && this.sequenceMode_) {
80
91
  // https://bit.ly/2K92l7y
81
92
  // The 'X-TIMESTAMP-MAP' header is used in HLS to align text with
@@ -113,6 +124,11 @@ shaka.text.VttTextParser = class {
113
124
 
114
125
  offset = time.periodStart + mpegTime / mpegTimescale - cueTime;
115
126
  }
127
+ } else if (blocks[0].includes('X-TIMESTAMP-MAP') &&
128
+ this.manifestType_ == shaka.media.ManifestParser.HLS) {
129
+ // In we use HLS segments mode, with the presence of this tag we need
130
+ // calculate the offset from the segment startTime.
131
+ offset = time.segmentStart;
116
132
  }
117
133
 
118
134
  // Parse VTT regions.
@@ -401,8 +417,6 @@ shaka.text.VttTextParser = class {
401
417
  cue = new shaka.text.Cue(start, end, '');
402
418
  }
403
419
 
404
- VttTextParser.parseCueStyles(payload, cue, styles);
405
-
406
420
  // Parse optional settings.
407
421
  parser.skipWhitespace();
408
422
  let word = parser.readWord();
@@ -416,6 +430,8 @@ shaka.text.VttTextParser = class {
416
430
  word = parser.readWord();
417
431
  }
418
432
 
433
+ VttTextParser.parseCueStyles(payload, cue, styles);
434
+
419
435
  if (id != null) {
420
436
  cue.id = id;
421
437
  }