shaka-player 4.4.0 → 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 (75) hide show
  1. package/.github/workflows/deflake.yaml +8 -5
  2. package/.github/workflows/release-please.yaml +64 -16
  3. package/.github/workflows/report-incremental-coverage.yaml +2 -2
  4. package/.github/workflows/shaka-bot-commands/command-help.sh +27 -0
  5. package/.github/workflows/shaka-bot-commands/command-test.sh +56 -0
  6. package/.github/workflows/shaka-bot-commands/lib.sh +83 -0
  7. package/.github/workflows/shaka-bot-commands/main.sh +51 -0
  8. package/.github/workflows/talk-to-shaka-bot.yaml +29 -0
  9. package/.github/workflows/update-issues.yaml +2 -1
  10. package/.github/workflows/validate-pr-title.yaml +3 -11
  11. package/CHANGELOG.md +33 -0
  12. package/CONTRIBUTORS +1 -0
  13. package/demo/custom.js +5 -5
  14. package/dist/demo.compiled.debug.js +3 -3
  15. package/dist/demo.compiled.debug.map +1 -1
  16. package/dist/demo.compiled.js +3 -3
  17. package/dist/demo.compiled.map +1 -1
  18. package/dist/deps.js +2 -2
  19. package/dist/locales.js +32 -32
  20. package/dist/shaka-player.compiled.d.ts +18 -2
  21. package/dist/shaka-player.compiled.debug.d.ts +18 -2
  22. package/dist/shaka-player.compiled.debug.externs.js +38 -1
  23. package/dist/shaka-player.compiled.debug.js +289 -280
  24. package/dist/shaka-player.compiled.debug.map +3 -3
  25. package/dist/shaka-player.compiled.externs.js +38 -1
  26. package/dist/shaka-player.compiled.js +944 -940
  27. package/dist/shaka-player.compiled.map +4 -4
  28. package/dist/shaka-player.ui.d.ts +18 -2
  29. package/dist/shaka-player.ui.debug.d.ts +18 -2
  30. package/dist/shaka-player.ui.debug.externs.js +38 -1
  31. package/dist/shaka-player.ui.debug.js +352 -343
  32. package/dist/shaka-player.ui.debug.map +3 -3
  33. package/dist/shaka-player.ui.externs.js +38 -1
  34. package/dist/shaka-player.ui.js +1037 -1034
  35. package/dist/shaka-player.ui.map +4 -4
  36. package/docs/tutorials/fairplay.md +8 -0
  37. package/docs/tutorials/faq.md +1 -1
  38. package/externs/shaka/namespace.js +2 -2
  39. package/externs/shaka/text.js +7 -0
  40. package/externs/shaka/transmuxer.js +2 -1
  41. package/lib/dash/segment_template.js +3 -0
  42. package/lib/hls/hls_parser.js +9 -4
  43. package/lib/media/media_source_engine.js +23 -3
  44. package/lib/mss/mss_parser.js +1 -0
  45. package/lib/player.js +8 -57
  46. package/lib/text/lrc_text_parser.js +8 -0
  47. package/lib/text/mp4_ttml_parser.js +8 -0
  48. package/lib/text/mp4_vtt_parser.js +8 -0
  49. package/lib/text/sbv_text_parser.js +8 -0
  50. package/lib/text/simple_text_displayer.js +0 -1
  51. package/lib/text/srt_text_parser.js +8 -0
  52. package/lib/text/ssa_text_parser.js +8 -0
  53. package/lib/text/text_engine.js +8 -1
  54. package/lib/text/ttml_text_parser.js +10 -1
  55. package/lib/text/ui_text_displayer.js +49 -3
  56. package/lib/text/vtt_text_parser.js +21 -5
  57. package/lib/transmuxer/aac_transmuxer.js +1 -1
  58. package/lib/transmuxer/ac3_transmuxer.js +1 -0
  59. package/lib/transmuxer/ec3_transmuxer.js +1 -0
  60. package/lib/transmuxer/mp3_transmuxer.js +1 -0
  61. package/lib/transmuxer/ts_transmuxer.js +84 -100
  62. package/lib/util/error.js +16 -10
  63. package/lib/util/mp4_generator.js +27 -20
  64. package/lib/util/player_configuration.js +2 -2
  65. package/lib/util/stream_utils.js +13 -11
  66. package/lib/util/ts_parser.js +137 -117
  67. package/package.json +2 -2
  68. package/test/hls/hls_parser_unit.js +12 -13
  69. package/test/media/media_source_engine_integration.js +2 -2
  70. package/test/mss/mss_player_integration.js +1 -1
  71. package/test/text/text_engine_unit.js +4 -2
  72. package/test/text/ttml_text_parser_unit.js +3 -4
  73. package/test/text/vtt_text_parser_unit.js +17 -2
  74. package/test/text/web_vtt_layout_integration.js +0 -1
  75. 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>
@@ -6,8 +6,8 @@
6
6
 
7
7
  /**
8
8
  * @externs
9
- * @suppress {duplicate} To prevent compiler errors with the namespace
10
- * being declared both here and by goog.provide in the library.
9
+ * @suppress {constantProperty, duplicate} To prevent compiler errors with the
10
+ * namespace being declared both here and by goog.provide in the library.
11
11
  */
12
12
 
13
13
  /** @namespace */
@@ -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
 
@@ -46,9 +46,10 @@ shaka.extern.Transmuxer = class {
46
46
  * @param {?shaka.media.SegmentReference} reference The segment reference, or
47
47
  * null for init segments
48
48
  * @param {number} duration
49
+ * @param {string} contentType
49
50
  * @return {!Promise.<!Uint8Array>}
50
51
  */
51
- transmux(data, stream, reference, duration) {}
52
+ transmux(data, stream, reference, duration, contentType) {}
52
53
  };
53
54
 
54
55
 
@@ -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
  };
@@ -1466,6 +1470,7 @@ shaka.hls.HlsParser = class {
1466
1470
  // and no video codec, so it should be audio.
1467
1471
  type = ContentType.AUDIO;
1468
1472
  shaka.log.debug('Guessing audio-only.');
1473
+ ignoreStream = res.audio.length > 0;
1469
1474
  } else if (!streamInfos.length && audioCodecs && videoCodecs) {
1470
1475
  // There are both audio and video codecs, so assume multiplexed content.
1471
1476
  // Note that the default used when CODECS is missing assumes multiple
@@ -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
  /**
@@ -821,7 +821,7 @@ shaka.media.MediaSourceEngine = class {
821
821
 
822
822
  if (this.transmuxers_[contentType]) {
823
823
  data = await this.transmuxers_[contentType].transmux(
824
- data, stream, reference, this.mediaSource_.duration);
824
+ data, stream, reference, this.mediaSource_.duration, contentType);
825
825
  }
826
826
 
827
827
  data = this.workAroundBrokenPlatforms_(
@@ -1562,16 +1562,34 @@ shaka.media.MediaSourceEngine = class {
1562
1562
  * @private
1563
1563
  */
1564
1564
  async reset_(streamsByType) {
1565
+ const Functional = shaka.util.Functional;
1565
1566
  this.reloadingMediaSource_ = true;
1566
1567
  const currentTime = this.video_.currentTime;
1567
1568
  try {
1568
1569
  this.eventManager_.removeAll();
1569
- this.sourceBuffers_ = {};
1570
1570
 
1571
1571
  const cleanup = [];
1572
1572
  for (const contentType in this.transmuxers_) {
1573
1573
  cleanup.push(this.transmuxers_[contentType].destroy());
1574
1574
  }
1575
+ for (const contentType in this.queues_) {
1576
+ // Make a local copy of the queue and the first item.
1577
+ const q = this.queues_[contentType];
1578
+ const inProgress = q[0];
1579
+
1580
+ // Drop everything else out of the original queue.
1581
+ this.queues_[contentType] = q.slice(0, 1);
1582
+
1583
+ // We will wait for this item to complete/fail.
1584
+ if (inProgress) {
1585
+ cleanup.push(inProgress.p.catch(Functional.noop));
1586
+ }
1587
+
1588
+ // The rest will be rejected silently if possible.
1589
+ for (const item of q.slice(1)) {
1590
+ item.p.reject(shaka.util.Destroyer.destroyedError());
1591
+ }
1592
+ }
1575
1593
  for (const contentType in this.sourceBuffers_) {
1576
1594
  const sourceBuffer = this.sourceBuffers_[contentType];
1577
1595
  try {
@@ -1580,6 +1598,8 @@ shaka.media.MediaSourceEngine = class {
1580
1598
  }
1581
1599
  await Promise.all(cleanup);
1582
1600
  this.transmuxers_ = {};
1601
+ this.queues_ = {};
1602
+ this.sourceBuffers_ = {};
1583
1603
 
1584
1604
  const previousDuration = this.mediaSource_.duration;
1585
1605
  this.mediaSourceOpen_ = new shaka.util.PublicPromise();
@@ -635,6 +635,7 @@ shaka.mss.MssParser = class {
635
635
  const streamInfo = {
636
636
  id: stream.id,
637
637
  type: stream.type,
638
+ codecs: stream.codecs,
638
639
  encrypted: stream.encrypted,
639
640
  timescale: stream.mssPrivateData.timescale,
640
641
  duration: stream.mssPrivateData.duration,
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.0-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
@@ -51,7 +51,6 @@ shaka.text.SimpleTextDisplayer = class {
51
51
  // kinds which can be displayed in the app's UI.
52
52
  this.textTrack_ = video.addTextTrack(
53
53
  'subtitles', shaka.Player.TextTrackLabel);
54
- console.log(this.textTrack_);
55
54
  }
56
55
  this.textTrack_.mode = 'hidden';
57
56
  }
@@ -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
  }
@@ -145,7 +145,6 @@ shaka.transmuxer.AacTransmuxer = class {
145
145
  }
146
146
  stream.audioSamplingRate = info.sampleRate;
147
147
  stream.channelsCount = info.channelCount;
148
- stream.codecs = info.codec;
149
148
 
150
149
  /** @type {!Array.<shaka.util.Mp4Generator.Mp4Sample>} */
151
150
  const samples = [];
@@ -189,6 +188,7 @@ shaka.transmuxer.AacTransmuxer = class {
189
188
  const streamInfo = {
190
189
  id: stream.id,
191
190
  type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
191
+ codecs: info.codec,
192
192
  encrypted: stream.encrypted && stream.drmInfos.length > 0,
193
193
  timescale: sampleRate,
194
194
  duration: duration,
@@ -177,6 +177,7 @@ shaka.transmuxer.Ac3Transmuxer = class {
177
177
  const streamInfo = {
178
178
  id: stream.id,
179
179
  type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
180
+ codecs: 'ac-3',
180
181
  encrypted: stream.encrypted && stream.drmInfos.length > 0,
181
182
  timescale: sampleRate,
182
183
  duration: duration,
@@ -177,6 +177,7 @@ shaka.transmuxer.Ec3Transmuxer = class {
177
177
  const streamInfo = {
178
178
  id: stream.id,
179
179
  type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
180
+ codecs: 'ec-3',
180
181
  encrypted: stream.encrypted && stream.drmInfos.length > 0,
181
182
  timescale: sampleRate,
182
183
  duration: duration,
@@ -169,6 +169,7 @@ shaka.transmuxer.Mp3Transmuxer = class {
169
169
  const streamInfo = {
170
170
  id: stream.id,
171
171
  type: shaka.util.ManifestParserUtils.ContentType.AUDIO,
172
+ codecs: 'mp3',
172
173
  encrypted: stream.encrypted && stream.drmInfos.length > 0,
173
174
  timescale: sampleRate,
174
175
  duration: duration,