shaka-player 4.6.0 → 4.6.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 (39) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/demo/config.js +5 -0
  3. package/dist/demo.compiled.debug.js +26 -25
  4. package/dist/demo.compiled.debug.map +2 -2
  5. package/dist/demo.compiled.js +26 -25
  6. package/dist/demo.compiled.map +2 -2
  7. package/dist/locales.js +30 -30
  8. package/dist/shaka-player.compiled.d.ts +1 -0
  9. package/dist/shaka-player.compiled.debug.d.ts +1 -0
  10. package/dist/shaka-player.compiled.debug.externs.js +1 -0
  11. package/dist/shaka-player.compiled.debug.js +275 -273
  12. package/dist/shaka-player.compiled.debug.map +3 -3
  13. package/dist/shaka-player.compiled.externs.js +1 -0
  14. package/dist/shaka-player.compiled.js +1034 -1033
  15. package/dist/shaka-player.compiled.map +3 -3
  16. package/dist/shaka-player.ui.d.ts +1 -0
  17. package/dist/shaka-player.ui.debug.d.ts +1 -0
  18. package/dist/shaka-player.ui.debug.externs.js +1 -0
  19. package/dist/shaka-player.ui.debug.js +295 -293
  20. package/dist/shaka-player.ui.debug.map +3 -3
  21. package/dist/shaka-player.ui.externs.js +1 -0
  22. package/dist/shaka-player.ui.js +1172 -1171
  23. package/dist/shaka-player.ui.map +3 -3
  24. package/docs/tutorials/lcevc.md +2 -2
  25. package/lib/cea/cea608_memory.js +13 -2
  26. package/lib/dash/dash_parser.js +12 -11
  27. package/lib/dash/segment_template.js +9 -3
  28. package/lib/hls/hls_parser.js +9 -3
  29. package/lib/media/segment_reference.js +2 -1
  30. package/lib/media/streaming_engine.js +36 -28
  31. package/lib/player.js +6 -2
  32. package/lib/transmuxer/ts_transmuxer.js +15 -0
  33. package/lib/util/error.js +6 -0
  34. package/package.json +2 -2
  35. package/test/cea/cea608_memory_unit.js +8 -8
  36. package/test/cea/cea_decoder_unit.js +11 -11
  37. package/test/demo/demo_unit.js +0 -3
  38. package/test/hls/hls_parser_unit.js +40 -0
  39. package/ui/presentation_time.js +5 -5
@@ -24,7 +24,7 @@ LCEVC Decoder library needs to be included in the HTML page:
24
24
  ```
25
25
  - Or use `unpkg.com` for latest LCEVC Decoder libraries.
26
26
  ```html
27
- <script src="https://unpkg.com/lcevc_dec.js@1.0.0/dist/lcevc_dec.min.js"></script>
27
+ <script src="https://unpkg.com/lcevc_dec.js@1.0.1/dist/lcevc_dec.min.js"></script>
28
28
  ```
29
29
 
30
30
  Configuration to enable LCEVC enhancement:
@@ -45,7 +45,7 @@ Sample setup using the Shaka Player UI library:
45
45
  <link rel="stylesheet" href="dist/controls.css" />
46
46
 
47
47
  <!-- LCEVC decoder compiled library -->
48
- <script src="https://unpkg.com/lcevc_dec.js@1.0.0/dist/lcevc_dec.min.js"></script>
48
+ <script src="https://unpkg.com/lcevc_dec.js@1.0.1/dist/lcevc_dec.min.js"></script>
49
49
 
50
50
  <!-- Application source: -->
51
51
  <script src="app.js"></script>
@@ -84,8 +84,10 @@ shaka.cea.Cea608Memory = class {
84
84
  startTime, endTime, /* payload= */ '');
85
85
  topLevelCue.lineInterpretation =
86
86
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
87
- const maxRows = shaka.cea.Cea608Memory.CC_ROWS + 1;
88
- topLevelCue.line = this.row_ / maxRows * 100;
87
+ const line = shaka.cea.Cea608Memory.ROW_TO_LINE_CONVERSION_.get(this.row_);
88
+ if (line) {
89
+ topLevelCue.line = line;
90
+ }
89
91
  return shaka.cea.CeaUtils.getParsedCaption(
90
92
  topLevelCue, stream, this.rows_, startTime, endTime);
91
93
  }
@@ -320,3 +322,12 @@ shaka.cea.Cea608Memory.CharSet.ExtendedPortugueseGerman = new Map([
320
322
  [0x38, 'Å'], [0x39, 'å'], [0x3a, 'Ø'], [0x3b, 'ø'], [0x3c, '┌'], [0x3d, '┐'],
321
323
  [0x3e, '└'], [0x3f, '┘'],
322
324
  ]);
325
+
326
+ /**
327
+ * @private @const {!Map<number, number>}
328
+ */
329
+ shaka.cea.Cea608Memory.ROW_TO_LINE_CONVERSION_ = new Map([
330
+ [1, 10], [2, 15.33], [3, 20.66], [4, 26], [5, 31.33], [6, 36.66], [7, 42],
331
+ [8, 47.33], [9, 52.66], [10, 58], [11, 63.33], [12, 68.66], [13, 74],
332
+ [14, 79.33], [15, 84.66],
333
+ ]);
@@ -285,7 +285,8 @@ shaka.dash.DashParser = class {
285
285
 
286
286
  // For redirections add the response uri to the first entry in the
287
287
  // Manifest Uris array.
288
- if (response.uri && !this.manifestUris_.includes(response.uri)) {
288
+ if (response.uri && response.uri != response.originalUri &&
289
+ !this.manifestUris_.includes(response.uri)) {
289
290
  this.manifestUris_.unshift(response.uri);
290
291
  }
291
292
 
@@ -373,19 +374,16 @@ shaka.dash.DashParser = class {
373
374
 
374
375
  const contentSteering = XmlUtils.findChild(mpd, 'ContentSteering');
375
376
  if (contentSteering && this.playerInterface_) {
376
- const hasPrevContentSteeringManager = !!this.contentSteeringManager_;
377
+ const defaultPathwayId =
378
+ contentSteering.getAttribute('defaultServiceLocation');
377
379
  if (!this.contentSteeringManager_) {
378
380
  this.contentSteeringManager_ =
379
381
  new shaka.util.ContentSteeringManager(this.playerInterface_);
380
- }
381
- this.contentSteeringManager_.configure(this.config_);
382
- this.contentSteeringManager_.setBaseUris(manifestBaseUris);
383
- this.contentSteeringManager_.setManifestType(
384
- shaka.media.ManifestParser.DASH);
385
- const defaultPathwayId =
386
- contentSteering.getAttribute('defaultServiceLocation');
387
- this.contentSteeringManager_.setDefaultPathwayId(defaultPathwayId);
388
- if (!hasPrevContentSteeringManager) {
382
+ this.contentSteeringManager_.configure(this.config_);
383
+ this.contentSteeringManager_.setManifestType(
384
+ shaka.media.ManifestParser.DASH);
385
+ this.contentSteeringManager_.setBaseUris(manifestBaseUris);
386
+ this.contentSteeringManager_.setDefaultPathwayId(defaultPathwayId);
389
387
  const uri = XmlUtils.getContents(contentSteering);
390
388
  if (uri) {
391
389
  const queryBeforeStart =
@@ -398,6 +396,9 @@ shaka.dash.DashParser = class {
398
396
  this.contentSteeringManager_.requestInfo(uri);
399
397
  }
400
398
  }
399
+ } else {
400
+ this.contentSteeringManager_.setBaseUris(manifestBaseUris);
401
+ this.contentSteeringManager_.setDefaultPathwayId(defaultPathwayId);
401
402
  }
402
403
  }
403
404
 
@@ -141,7 +141,8 @@ shaka.dash.SegmentTemplate = class {
141
141
  );
142
142
  } else {
143
143
  const tsi = /** @type {!TimelineSegmentIndex} */(segmentIndex);
144
- tsi.appendTemplateInfo(info, periodStart, periodEnd, shouldFit);
144
+ tsi.appendTemplateInfo(
145
+ info, periodStart, periodEnd, shouldFit, initSegmentReference);
145
146
 
146
147
  const availabilityStart =
147
148
  context.presentationTimeline.getSegmentAvailabilityStart();
@@ -167,7 +168,7 @@ shaka.dash.SegmentTemplate = class {
167
168
  if (segmentIndex instanceof shaka.dash.TimelineSegmentIndex &&
168
169
  segmentIndex.isEmpty()) {
169
170
  segmentIndex.appendTemplateInfo(info, periodStart,
170
- periodEnd, shouldFit);
171
+ periodEnd, shouldFit, initSegmentReference);
171
172
  }
172
173
  return Promise.resolve(segmentIndex);
173
174
  },
@@ -684,8 +685,11 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
684
685
  * @param {number} periodStart
685
686
  * @param {number} periodEnd
686
687
  * @param {boolean} shouldFit
688
+ * @param {shaka.media.InitSegmentReference} initSegmentReference
687
689
  */
688
- appendTemplateInfo(info, periodStart, periodEnd, shouldFit) {
690
+ appendTemplateInfo(info, periodStart, periodEnd, shouldFit,
691
+ initSegmentReference) {
692
+ this.initSegmentReference_ = initSegmentReference;
689
693
  if (!this.templateInfo_) {
690
694
  this.templateInfo_ = info;
691
695
  this.periodStart_ = periodStart;
@@ -693,6 +697,8 @@ shaka.dash.TimelineSegmentIndex = class extends shaka.media.SegmentIndex {
693
697
  } else {
694
698
  const currentTimeline = this.templateInfo_.timeline;
695
699
 
700
+ this.templateInfo_.mediaTemplate = info.mediaTemplate;
701
+
696
702
  // Append timeline
697
703
  const lastCurrentEntry = currentTimeline[currentTimeline.length - 1];
698
704
  const newEntries = info.timeline.filter((entry) => {
@@ -1672,9 +1672,15 @@ shaka.hls.HlsParser = class {
1672
1672
  // the value NONE. If the value is a quoted-string, it MUST match the
1673
1673
  // value of the GROUP-ID attribute of an EXT-X-MEDIA tag elsewhere in the
1674
1674
  // Playlist whose TYPE attribute is CLOSED-CAPTIONS.
1675
- if (type == ContentType.VIDEO && closedCaptionsAttr &&
1676
- closedCaptionsAttr != 'NONE') {
1677
- return this.groupIdToClosedCaptionsMap_.get(closedCaptionsAttr);
1675
+ if (type == ContentType.VIDEO) {
1676
+ if (closedCaptionsAttr && closedCaptionsAttr != 'NONE') {
1677
+ return this.groupIdToClosedCaptionsMap_.get(closedCaptionsAttr);
1678
+ } else if (!closedCaptionsAttr &&
1679
+ this.groupIdToClosedCaptionsMap_.size) {
1680
+ for (const key of this.groupIdToClosedCaptionsMap_.keys()) {
1681
+ return this.groupIdToClosedCaptionsMap_.get(key);
1682
+ }
1683
+ }
1678
1684
  }
1679
1685
  return null;
1680
1686
  }
@@ -128,7 +128,8 @@ shaka.media.InitSegmentReference = class {
128
128
  } else {
129
129
  return reference1.getStartByte() == reference2.getStartByte() &&
130
130
  reference1.getEndByte() == reference2.getEndByte() &&
131
- ArrayUtils.equal(reference1.getUris(), reference2.getUris()) &&
131
+ ArrayUtils.equal(
132
+ reference1.getUris().sort(), reference2.getUris().sort()) &&
132
133
  BufferUtils.equal(reference1.getSegmentData(),
133
134
  reference2.getSegmentData());
134
135
  }
@@ -1351,6 +1351,7 @@ shaka.media.StreamingEngine = class {
1351
1351
  let remaining = new Uint8Array(0);
1352
1352
  let processingResult = false;
1353
1353
  let callbackCalled = false;
1354
+ let streamDataCallbackError;
1354
1355
  const streamDataCallback = async (data) => {
1355
1356
  if (processingResult) {
1356
1357
  // If the fallback result processing was triggered, don't also
@@ -1363,40 +1364,47 @@ shaka.media.StreamingEngine = class {
1363
1364
  if (this.fatalError_) {
1364
1365
  return;
1365
1366
  }
1366
- // Append the data with complete boxes.
1367
- // Every time streamDataCallback gets called, append the new data to
1368
- // the remaining data.
1369
- // Find the last fully completed Mdat box, and slice the data into two
1370
- // parts: the first part with completed Mdat boxes, and the second
1371
- // part with an incomplete box.
1372
- // Append the first part, and save the second part as remaining data,
1373
- // and handle it with the next streamDataCallback call.
1374
- remaining = this.concatArray_(remaining, data);
1375
- let sawMDAT = false;
1376
- let offset = 0;
1377
- new shaka.util.Mp4Parser()
1378
- .box('mdat', (box) => {
1379
- offset = box.size + box.start;
1380
- sawMDAT = true;
1381
- })
1382
- .parse(remaining, /* partialOkay= */ false,
1367
+ try {
1368
+ // Append the data with complete boxes.
1369
+ // Every time streamDataCallback gets called, append the new data
1370
+ // to the remaining data.
1371
+ // Find the last fully completed Mdat box, and slice the data into
1372
+ // two parts: the first part with completed Mdat boxes, and the
1373
+ // second part with an incomplete box.
1374
+ // Append the first part, and save the second part as remaining
1375
+ // data, and handle it with the next streamDataCallback call.
1376
+ remaining = this.concatArray_(remaining, data);
1377
+ let sawMDAT = false;
1378
+ let offset = 0;
1379
+ new shaka.util.Mp4Parser()
1380
+ .box('mdat', (box) => {
1381
+ offset = box.size + box.start;
1382
+ sawMDAT = true;
1383
+ })
1384
+ .parse(remaining, /* partialOkay= */ false,
1385
+ /* isChunkedData= */ true);
1386
+ if (sawMDAT) {
1387
+ const dataToAppend = remaining.subarray(0, offset);
1388
+ remaining = remaining.subarray(offset);
1389
+ await this.append_(
1390
+ mediaState, presentationTime, stream, reference, dataToAppend,
1383
1391
  /* isChunkedData= */ true);
1384
- if (sawMDAT) {
1385
- const dataToAppend = remaining.subarray(0, offset);
1386
- remaining = remaining.subarray(offset);
1387
- await this.append_(
1388
- mediaState, presentationTime, stream, reference, dataToAppend,
1389
- /* isChunkedData= */ true);
1390
-
1391
- if (mediaState.segmentPrefetch && mediaState.segmentIterator) {
1392
- mediaState.segmentPrefetch.prefetchSegments(
1393
- reference, /* skipFirst= */ true);
1392
+
1393
+ if (mediaState.segmentPrefetch && mediaState.segmentIterator) {
1394
+ mediaState.segmentPrefetch.prefetchSegments(
1395
+ reference, /* skipFirst= */ true);
1396
+ }
1394
1397
  }
1398
+ } catch (error) {
1399
+ streamDataCallbackError = error;
1395
1400
  }
1396
1401
  };
1397
1402
 
1398
1403
  const result =
1399
1404
  await this.fetch_(mediaState, reference, streamDataCallback);
1405
+ if (streamDataCallbackError) {
1406
+ throw streamDataCallbackError;
1407
+ }
1400
1408
  if (!callbackCalled) {
1401
1409
  // In some environments, we might be forced to use network plugins
1402
1410
  // that don't support streamDataCallback. In those cases, as a
@@ -1784,7 +1792,7 @@ shaka.media.StreamingEngine = class {
1784
1792
  reference.initSegmentReference, mediaState.lastInitSegmentReference)) {
1785
1793
  mediaState.lastInitSegmentReference = reference.initSegmentReference;
1786
1794
 
1787
- if (reference.initSegmentReference) {
1795
+ if (reference.isIndependent() && reference.initSegmentReference) {
1788
1796
  shaka.log.v1(logPrefix, 'fetching init segment');
1789
1797
 
1790
1798
  const fetchInit =
package/lib/player.js CHANGED
@@ -4595,9 +4595,13 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4595
4595
  }
4596
4596
  await this.addSrcTrackElement_(uri, language, kind, mimeType, label || '',
4597
4597
  adCuePoints);
4598
+
4599
+ const LanguageUtils = shaka.util.LanguageUtils;
4600
+ const languageNormalized = LanguageUtils.normalize(language);
4601
+
4598
4602
  const textTracks = this.getTextTracks();
4599
4603
  const srcTrack = textTracks.find((t) => {
4600
- return t.language == language &&
4604
+ return LanguageUtils.normalize(t.language) == languageNormalized &&
4601
4605
  t.label == (label || '') &&
4602
4606
  t.kind == kind;
4603
4607
  });
@@ -6777,7 +6781,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6777
6781
  * @export
6778
6782
  */
6779
6783
  // eslint-disable-next-line no-useless-concat
6780
- shaka.Player.version = 'v4.6.0' + '-uncompiled'; // x-release-please-version
6784
+ shaka.Player.version = 'v4.6.2' + '-uncompiled'; // x-release-please-version
6781
6785
 
6782
6786
  // Initialize the deprecation system using the version string we just set
6783
6787
  // on the player.
@@ -242,6 +242,9 @@ shaka.transmuxer.TsTransmuxer = class {
242
242
  }
243
243
  }
244
244
  } catch (e) {
245
+ if (e && e.code == shaka.util.Error.Code.TRANSMUXING_NO_VIDEO_DATA) {
246
+ return Promise.resolve(new Uint8Array([]));
247
+ }
245
248
  return Promise.reject(e);
246
249
  }
247
250
 
@@ -704,6 +707,12 @@ shaka.transmuxer.TsTransmuxer = class {
704
707
 
705
708
  let nalus = [];
706
709
  const videoData = tsParser.getVideoData();
710
+ if (!videoData.length) {
711
+ throw new shaka.util.Error(
712
+ shaka.util.Error.Severity.CRITICAL,
713
+ shaka.util.Error.Category.MEDIA,
714
+ shaka.util.Error.Code.TRANSMUXING_NO_VIDEO_DATA);
715
+ }
707
716
  for (let i = 0; i < videoData.length; i++) {
708
717
  const pes = videoData[i];
709
718
  const dataNalus = pes.nalus;
@@ -790,6 +799,12 @@ shaka.transmuxer.TsTransmuxer = class {
790
799
 
791
800
  let nalus = [];
792
801
  const videoData = tsParser.getVideoData();
802
+ if (!videoData.length) {
803
+ throw new shaka.util.Error(
804
+ shaka.util.Error.Severity.CRITICAL,
805
+ shaka.util.Error.Category.MEDIA,
806
+ shaka.util.Error.Code.TRANSMUXING_NO_VIDEO_DATA);
807
+ }
793
808
  for (let i = 0; i < videoData.length; i++) {
794
809
  const pes = videoData[i];
795
810
  const dataNalus = pes.nalus;
package/lib/util/error.js CHANGED
@@ -511,6 +511,12 @@ shaka.util.Error.Code = {
511
511
  */
512
512
  'MSS_TRANSMUXING_FAILED': 3022,
513
513
 
514
+ /**
515
+ * An internal error which indicates that transmuxing operation has no video
516
+ * data. This should not be seen by applications.
517
+ */
518
+ 'TRANSMUXING_NO_VIDEO_DATA': 3023,
519
+
514
520
 
515
521
  /**
516
522
  * The Player was unable to guess the manifest type based on file extension
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "shaka-player",
3
3
  "description": "DASH/EME video player library",
4
- "version": "4.6.0",
4
+ "version": "4.6.2",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -48,7 +48,7 @@
48
48
  "karma-sourcemap-loader": "^0.3.8",
49
49
  "karma-spec-reporter": "^0.0.34",
50
50
  "karma-webdriver-launcher": "^1.0.8",
51
- "lcevc_dec.js": "^1.0.0",
51
+ "lcevc_dec.js": "^1.0.1",
52
52
  "less": "https://gitpkg.now.sh/joeyparrish/less.js/packages/less?28c63a43",
53
53
  "less-plugin-clean-css": "github:austingardner/less-plugin-clean-css#4e9e77bf",
54
54
  "material-design-lite": "^1.3.0",
@@ -31,7 +31,7 @@ describe('Cea608Memory', () => {
31
31
  const caption = memory.forceEmit(startTime, endTime);
32
32
 
33
33
  const topLevelCue = new shaka.text.Cue(startTime, endTime, '');
34
- topLevelCue.line = 6.25;
34
+ topLevelCue.line = 10;
35
35
  topLevelCue.lineInterpretation =
36
36
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
37
37
  topLevelCue.nestedCues = [
@@ -82,7 +82,7 @@ describe('Cea608Memory', () => {
82
82
  }
83
83
 
84
84
  const topLevelCue = new shaka.text.Cue(startTime, endTime, '');
85
- topLevelCue.line = 6.25;
85
+ topLevelCue.line = 10;
86
86
  topLevelCue.lineInterpretation =
87
87
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
88
88
  topLevelCue.nestedCues = [
@@ -119,7 +119,7 @@ describe('Cea608Memory', () => {
119
119
  }
120
120
 
121
121
  const topLevelCue = new shaka.text.Cue(startTime, endTime, '');
122
- topLevelCue.line = 6.25;
122
+ topLevelCue.line = 10;
123
123
  topLevelCue.lineInterpretation =
124
124
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
125
125
  topLevelCue.nestedCues = [
@@ -173,7 +173,7 @@ describe('Cea608Memory', () => {
173
173
  // ...
174
174
  // So we expect that test\n\ntest is emitted
175
175
  const topLevelCue = new shaka.text.Cue(startTime, endTime, '');
176
- topLevelCue.line = 37.5;
176
+ topLevelCue.line = 36.66;
177
177
  topLevelCue.lineInterpretation =
178
178
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
179
179
  topLevelCue.nestedCues = [
@@ -217,7 +217,7 @@ describe('Cea608Memory', () => {
217
217
  memory.eraseChar(); // Erase the last 't' from 'testt'
218
218
 
219
219
  const topLevelCue = new shaka.text.Cue(startTime, endTime, '');
220
- topLevelCue.line = 6.25;
220
+ topLevelCue.line = 10;
221
221
  topLevelCue.lineInterpretation =
222
222
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
223
223
  topLevelCue.nestedCues = [
@@ -291,7 +291,7 @@ describe('Cea608Memory', () => {
291
291
 
292
292
  // Expected text is 's\nt\nt\ne'
293
293
  const topLevelCue = new shaka.text.Cue(startTime, endTime, '');
294
- topLevelCue.line = 31.25;
294
+ topLevelCue.line = 31.33;
295
295
  topLevelCue.lineInterpretation =
296
296
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
297
297
  topLevelCue.nestedCues = [
@@ -338,7 +338,7 @@ describe('Cea608Memory', () => {
338
338
 
339
339
  // Expected text is 't\ne\ns\nt'
340
340
  const topLevelCue = new shaka.text.Cue(startTime, endTime, '');
341
- topLevelCue.line = 31.25;
341
+ topLevelCue.line = 31.33;
342
342
  topLevelCue.lineInterpretation =
343
343
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
344
344
  topLevelCue.nestedCues = [
@@ -385,7 +385,7 @@ describe('Cea608Memory', () => {
385
385
 
386
386
  // Expected text is 't\ne\ns\nt'
387
387
  const topLevelCue = new shaka.text.Cue(startTime, endTime, '');
388
- topLevelCue.line = 31.25;
388
+ topLevelCue.line = 31.33;
389
389
  topLevelCue.lineInterpretation =
390
390
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
391
391
  topLevelCue.nestedCues = [
@@ -65,7 +65,7 @@ describe('CeaDecoder', () => {
65
65
 
66
66
  const topLevelCue = new shaka.text.Cue(
67
67
  startTimeCaption1, startTimeCaption2, '');
68
- topLevelCue.line = 81.25;
68
+ topLevelCue.line = 74;
69
69
  topLevelCue.lineInterpretation =
70
70
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
71
71
  topLevelCue.nestedCues = [
@@ -113,7 +113,7 @@ describe('CeaDecoder', () => {
113
113
  // Since there are three style changes, there should be three nested cues.
114
114
  const topLevelCue = new shaka.text.Cue(
115
115
  startTimeCaption1, startTimeCaption2, '');
116
- topLevelCue.line = 6.25;
116
+ topLevelCue.line = 10;
117
117
  topLevelCue.lineInterpretation =
118
118
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
119
119
 
@@ -166,7 +166,7 @@ describe('CeaDecoder', () => {
166
166
  // A single nested cue containing yellow, italicized text.
167
167
  const topLevelCue = new shaka.text.Cue(startTimeCaption1,
168
168
  startTimeCaption2, '');
169
- topLevelCue.line = 12.5;
169
+ topLevelCue.line = 15.33;
170
170
  topLevelCue.lineInterpretation =
171
171
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
172
172
  topLevelCue.nestedCues = [
@@ -208,7 +208,7 @@ describe('CeaDecoder', () => {
208
208
 
209
209
  const topLevelCue = new shaka.text.Cue(startTimeCaption1,
210
210
  startTimeCaption2, '');
211
- topLevelCue.line = 6.25;
211
+ topLevelCue.line = 10;
212
212
  topLevelCue.lineInterpretation =
213
213
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
214
214
  topLevelCue.nestedCues = [
@@ -243,7 +243,7 @@ describe('CeaDecoder', () => {
243
243
 
244
244
  const topLevelCue = new shaka.text.Cue(startTimeCaption1,
245
245
  startTimeCaption2, '');
246
- topLevelCue.line = 6.25;
246
+ topLevelCue.line = 10;
247
247
  topLevelCue.lineInterpretation =
248
248
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
249
249
  topLevelCue.nestedCues = [
@@ -315,7 +315,7 @@ describe('CeaDecoder', () => {
315
315
  // Top level cue corresponding to the first closed caption.
316
316
  const topLevelCue1 = new shaka.text.Cue(
317
317
  /* startTime= */ time1, /* endTime= */ time2, '');
318
- topLevelCue1.line = 93.75;
318
+ topLevelCue1.line = 84.66;
319
319
  topLevelCue1.lineInterpretation =
320
320
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
321
321
  topLevelCue1.nestedCues = [
@@ -326,7 +326,7 @@ describe('CeaDecoder', () => {
326
326
  // Top level cue corresponding to the second closed caption.
327
327
  const topLevelCue2 = new shaka.text.Cue(
328
328
  /* startTime= */ time2, /* endTime= */ time3, '');
329
- topLevelCue2.line = 93.75;
329
+ topLevelCue2.line = 84.66;
330
330
  topLevelCue2.lineInterpretation =
331
331
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
332
332
  topLevelCue2.nestedCues = [
@@ -343,7 +343,7 @@ describe('CeaDecoder', () => {
343
343
  // Top level cue corresponding to the third closed caption.
344
344
  const topLevelCue3 = new shaka.text.Cue(
345
345
  /* startTime= */ time3, /* endTime= */ time4, '');
346
- topLevelCue3.line = 93.75;
346
+ topLevelCue3.line = 84.66;
347
347
  topLevelCue3.lineInterpretation =
348
348
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
349
349
  topLevelCue3.nestedCues = [
@@ -360,7 +360,7 @@ describe('CeaDecoder', () => {
360
360
  // Top level cue corresponding to the fourth closed caption.
361
361
  const topLevelCue4 = new shaka.text.Cue(
362
362
  /* startTime= */ time4, /* endTime= */ time5, '');
363
- topLevelCue4.line = 93.75;
363
+ topLevelCue4.line = 84.66;
364
364
  topLevelCue4.lineInterpretation =
365
365
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
366
366
  topLevelCue4.nestedCues = [
@@ -433,7 +433,7 @@ describe('CeaDecoder', () => {
433
433
  // Top level cue corresponding to the first closed caption.
434
434
  const topLevelCue1 = new shaka.text.Cue(/* startTime= */ 1,
435
435
  /* endTime= */ 2, '');
436
- topLevelCue1.line = 93.75;
436
+ topLevelCue1.line = 84.66;
437
437
  topLevelCue1.lineInterpretation =
438
438
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
439
439
  topLevelCue1.nestedCues = [
@@ -444,7 +444,7 @@ describe('CeaDecoder', () => {
444
444
  // Top level cue corresponding to the second closed caption.
445
445
  const topLevelCue2 = new shaka.text.Cue(/* startTime= */ 2,
446
446
  /* endTime= */ 3, '');
447
- topLevelCue2.line = 25;
447
+ topLevelCue2.line = 26;
448
448
  topLevelCue2.lineInterpretation =
449
449
  shaka.text.Cue.lineInterpretation.PERCENTAGE;
450
450
  topLevelCue2.nestedCues = [
@@ -74,14 +74,11 @@ describe('Demo', () => {
74
74
  function checkConfig(checkValueNameFn) {
75
75
  const configPrimitives = new Set(['number', 'string', 'boolean']);
76
76
  const exceptions = new Set()
77
- .add('preferredVariantRole')
78
77
  .add('playRangeStart')
79
78
  .add('playRangeEnd')
80
79
  .add('manifest.dash.keySystemsByURI')
81
- .add('manifest.hls.mediaPlaylistFullMimeType')
82
80
  .add('manifest.mss.keySystemsBySystemId')
83
81
  .add('drm.keySystemsMapping')
84
- .add('streaming.parsePrftBox')
85
82
  .add('manifest.raiseFatalErrorOnManifestUpdateRequestFailure')
86
83
  .add('drm.persistentSessionOnlinePlayback')
87
84
  .add('drm.persistentSessionsMetadata');
@@ -750,6 +750,46 @@ describe('HlsParser', () => {
750
750
  await testHlsParser(master, media, manifest);
751
751
  });
752
752
 
753
+ it('parses audio+video variant with global closed captions', async () => {
754
+ const master = [
755
+ '#EXTM3U\n',
756
+ '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",CHANNELS="2",',
757
+ 'URI="audio"\n',
758
+ '#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,GROUP-ID="cap1",LANGUAGE="eng",',
759
+ 'INSTREAM-ID="CC1"\n',
760
+ '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
761
+ 'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
762
+ 'video\n',
763
+ ].join('');
764
+
765
+ const media = [
766
+ '#EXTM3U\n',
767
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
768
+ '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
769
+ '#EXTINF:5,\n',
770
+ '#EXT-X-BYTERANGE:121090@616\n',
771
+ 'main.mp4',
772
+ ].join('');
773
+
774
+ const closedCaptions = new Map([['CC1', 'en']]);
775
+ const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
776
+ manifest.anyTimeline();
777
+ manifest.addPartialVariant((variant) => {
778
+ variant.addPartialStream(ContentType.VIDEO, (stream) => {
779
+ stream.closedCaptions = closedCaptions;
780
+ stream.mime('video/mp4', 'avc1');
781
+ });
782
+ variant.addPartialStream(ContentType.AUDIO, (stream) => {
783
+ stream.mime('audio/mp4', 'mp4a');
784
+ });
785
+ });
786
+ manifest.sequenceMode = sequenceMode;
787
+ manifest.type = shaka.media.ManifestParser.HLS;
788
+ });
789
+
790
+ await testHlsParser(master, media, manifest);
791
+ });
792
+
753
793
  it('parses audio+video variant with no closed captions', async () => {
754
794
  const master = [
755
795
  '#EXTM3U\n',
@@ -64,7 +64,6 @@ shaka.ui.PresentationTimeTracker = class extends shaka.ui.Element {
64
64
  updateTime_() {
65
65
  const isSeeking = this.controls.isSeeking();
66
66
  let displayTime = this.controls.getDisplayTime();
67
- const duration = this.video.duration;
68
67
  const seekRange = this.player.seekRange();
69
68
  const seekRangeSize = seekRange.end - seekRange.start;
70
69
  const Utils = shaka.ui.Utils;
@@ -89,11 +88,12 @@ shaka.ui.PresentationTimeTracker = class extends shaka.ui.Element {
89
88
  this.currentTime_.disabled = true;
90
89
  }
91
90
  } else {
92
- const showHour = duration >= 3600;
91
+ const showHour = seekRangeSize >= 3600;
93
92
 
94
- let value = Utils.buildTimeString(displayTime, showHour);
95
- if (duration) {
96
- value += ' / ' + Utils.buildTimeString(duration, showHour);
93
+ const currentTime = Math.max(0, displayTime - seekRange.start);
94
+ let value = Utils.buildTimeString(currentTime, showHour);
95
+ if (seekRangeSize) {
96
+ value += ' / ' + Utils.buildTimeString(seekRangeSize, showHour);
97
97
  }
98
98
  this.setValue_(value);
99
99
  this.currentTime_.disabled = true;