shaka-player 3.3.0 → 3.3.1

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 (53) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/build/conformance.textproto +183 -169
  3. package/dist/controls.css +1 -1
  4. package/dist/deps.js +1 -1
  5. package/dist/locales.js +30 -30
  6. package/dist/shaka-player.compiled.d.ts +25 -2
  7. package/dist/shaka-player.compiled.debug.d.ts +25 -2
  8. package/dist/shaka-player.compiled.debug.externs.js +22 -1
  9. package/dist/shaka-player.compiled.debug.js +226 -222
  10. package/dist/shaka-player.compiled.debug.map +3 -3
  11. package/dist/shaka-player.compiled.externs.js +22 -1
  12. package/dist/shaka-player.compiled.js +814 -812
  13. package/dist/shaka-player.compiled.map +4 -4
  14. package/dist/shaka-player.ui.d.ts +25 -2
  15. package/dist/shaka-player.ui.debug.d.ts +25 -2
  16. package/dist/shaka-player.ui.debug.externs.js +22 -1
  17. package/dist/shaka-player.ui.debug.js +244 -240
  18. package/dist/shaka-player.ui.debug.map +3 -3
  19. package/dist/shaka-player.ui.externs.js +22 -1
  20. package/dist/shaka-player.ui.js +995 -992
  21. package/dist/shaka-player.ui.map +4 -4
  22. package/externs/cmcd.js +186 -0
  23. package/externs/shaka/player.js +8 -0
  24. package/externs/shaka/text.js +10 -0
  25. package/lib/hls/hls_parser.js +30 -0
  26. package/lib/media/segment_index.js +2 -1
  27. package/lib/media/segment_reference.js +19 -1
  28. package/lib/media/streaming_engine.js +5 -2
  29. package/lib/player.js +23 -17
  30. package/lib/polyfill/media_capabilities.js +30 -15
  31. package/lib/text/cue.js +6 -0
  32. package/lib/text/ttml_text_parser.js +84 -58
  33. package/lib/text/ui_text_displayer.js +17 -11
  34. package/lib/util/cmcd_manager.js +47 -201
  35. package/lib/util/stream_utils.js +86 -10
  36. package/lib/util/xml_utils.js +19 -0
  37. package/package.json +2 -2
  38. package/test/media/drm_engine_unit.js +3 -3
  39. package/test/media/streaming_engine_integration.js +2 -1
  40. package/test/media/streaming_engine_unit.js +145 -0
  41. package/test/player_integration.js +41 -8
  42. package/test/player_src_equals_integration.js +41 -8
  43. package/test/player_unit.js +19 -9
  44. package/test/test/assets/chapters.vtt +2 -2
  45. package/test/test/assets/chapters2.vtt +10 -0
  46. package/test/test/util/ttml_utils.js +81 -0
  47. package/test/text/mp4_ttml_parser_unit.js +11 -18
  48. package/test/text/ttml_text_parser_unit.js +499 -230
  49. package/test/util/cmcd_manager_unit.js +29 -15
  50. package/test/util/stream_utils_unit.js +36 -30
  51. package/third_party/closure-uri/uri.js +21 -0
  52. package/ui/text_selection.js +1 -0
  53. package/externs/media_capabilities.js +0 -16
@@ -6,6 +6,7 @@
6
6
 
7
7
  goog.provide('shaka.util.CmcdManager');
8
8
 
9
+ goog.require('goog.Uri');
9
10
  goog.require('shaka.log');
10
11
 
11
12
 
@@ -31,7 +32,7 @@ shaka.util.CmcdManager = class {
31
32
  *
32
33
  * @private {string}
33
34
  */
34
- this.sid_ = config.sessionId || window.crypto.randomUUID();
35
+ this.sid_ = '';
35
36
 
36
37
  /**
37
38
  * Streaming format
@@ -118,10 +119,11 @@ shaka.util.CmcdManager = class {
118
119
 
119
120
  data.ot = this.getObjectType_(segmentInfo);
120
121
 
121
- const isMedia = data.ot === shaka.util.CmcdManager.ObjectType.VIDEO ||
122
- data.ot === shaka.util.CmcdManager.ObjectType.AUDIO ||
123
- data.ot === shaka.util.CmcdManager.ObjectType.MUXED ||
124
- data.ot === shaka.util.CmcdManager.ObjectType.TIMED_TEXT;
122
+ const ObjectType = shaka.util.CmcdManager.ObjectType;
123
+ const isMedia = data.ot === ObjectType.VIDEO ||
124
+ data.ot === ObjectType.AUDIO ||
125
+ data.ot === ObjectType.MUXED ||
126
+ data.ot === ObjectType.TIMED_TEXT;
125
127
 
126
128
  if (isMedia) {
127
129
  data.bl = this.getBufferLength_(segmentInfo.type);
@@ -131,7 +133,9 @@ shaka.util.CmcdManager = class {
131
133
  data.br = segmentInfo.bandwidth / 1000;
132
134
  }
133
135
 
134
- data.tb = this.getTopBandwidth_(segmentInfo.type) / 1000;
136
+ if (isMedia && data.ot !== ObjectType.TIMED_TEXT) {
137
+ data.tb = this.getTopBandwidth_(data.ot) / 1000;
138
+ }
135
139
 
136
140
  this.apply_(request, data);
137
141
  } catch (error) {
@@ -147,6 +151,10 @@ shaka.util.CmcdManager = class {
147
151
  */
148
152
  applyTextData(request) {
149
153
  try {
154
+ if (!this.config_.enabled) {
155
+ return;
156
+ }
157
+
150
158
  this.apply_(request, {
151
159
  ot: shaka.util.CmcdManager.ObjectType.CAPTION,
152
160
  su: true,
@@ -213,10 +221,13 @@ shaka.util.CmcdManager = class {
213
221
  /**
214
222
  * Create baseline CMCD data
215
223
  *
216
- * @return {shaka.util.CmcdManager.Data}
224
+ * @return {CmcdData}
217
225
  * @private
218
226
  */
219
227
  createData_() {
228
+ if (!this.sid_) {
229
+ this.sid_ = this.config_.sessionId || window.crypto.randomUUID();
230
+ }
220
231
  return {
221
232
  v: shaka.util.CmcdManager.Version,
222
233
  sf: this.sf_,
@@ -230,7 +241,7 @@ shaka.util.CmcdManager = class {
230
241
  * Apply CMCD data to a request.
231
242
  *
232
243
  * @param {!shaka.extern.Request} request The request to apply CMCD data to
233
- * @param {!shaka.util.CmcdManager.Data} data The data object
244
+ * @param {!CmcdData} data The data object
234
245
  * @param {boolean} useHeaders Send data via request headers
235
246
  * @private
236
247
  */
@@ -380,23 +391,31 @@ shaka.util.CmcdManager = class {
380
391
  * @private
381
392
  */
382
393
  getTopBandwidth_(type) {
383
- const manifest = this.playerInterface_.getManifest();
384
- if (!manifest) {
394
+ const variants = this.playerInterface_.getVariantTracks();
395
+ if (!variants.length) {
385
396
  return NaN;
386
397
  }
387
398
 
388
- const variants = (type === 'text') ?
389
- manifest.textStreams : manifest.variants;
390
- let top = variants[0][type] || variants[0];
399
+ let top = variants[0];
391
400
 
392
401
  for (const variant of variants) {
393
- const stream = variant[type] || variant;
394
- if (stream.bandwidth > top.bandwidth) {
395
- top = stream;
402
+ if (variant.type === 'variant' && variant.bandwidth > top.bandwidth) {
403
+ top = variant;
396
404
  }
397
405
  }
398
406
 
399
- return top.bandwidth;
407
+ const ObjectType = shaka.util.CmcdManager.ObjectType;
408
+
409
+ switch (type) {
410
+ case ObjectType.VIDEO:
411
+ return top.videoBandwidth || NaN;
412
+
413
+ case ObjectType.AUDIO:
414
+ return top.audioBandwidth || NaN;
415
+
416
+ default:
417
+ return top.bandwidth;
418
+ }
400
419
  }
401
420
 
402
421
  /**
@@ -404,7 +423,7 @@ shaka.util.CmcdManager = class {
404
423
  * section 3.2 of
405
424
  * [CTA-5004](https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf).
406
425
  *
407
- * @param {shaka.util.CmcdManager.Data} data The CMCD data object
426
+ * @param {CmcdData} data The CMCD data object
408
427
  * @return {string}
409
428
  */
410
429
  static serialize(data) {
@@ -456,7 +475,7 @@ shaka.util.CmcdManager = class {
456
475
  let result;
457
476
 
458
477
  if (type === 'string' && key !== 'ot' && key !== 'sf' && key !== 'st') {
459
- result = `${key}="${value.replace(/"/g, '"')}"`;
478
+ result = `${key}=${JSON.stringify(value)}`;
460
479
  } else if (type === 'boolean') {
461
480
  result = key;
462
481
  } else if (type === 'symbol') {
@@ -476,7 +495,7 @@ shaka.util.CmcdManager = class {
476
495
  * defined in the section 2.1 and 3.2 of
477
496
  * [CTA-5004](https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf).
478
497
  *
479
- * @param {shaka.util.CmcdManager.Data} data The CMCD data object
498
+ * @param {CmcdData} data The CMCD data object
480
499
  * @return {!Object}
481
500
  */
482
501
  static toHeaders(data) {
@@ -512,11 +531,11 @@ shaka.util.CmcdManager = class {
512
531
  * defined in the section 2.2 and 3.2 of
513
532
  * [CTA-5004](https://cdn.cta.tech/cta/media/media/resources/standards/pdfs/cta-5004-final.pdf).
514
533
  *
515
- * @param {shaka.util.CmcdManager.Data} data The CMCD data object
534
+ * @param {CmcdData} data The CMCD data object
516
535
  * @return {string}
517
536
  */
518
537
  static toQuery(data) {
519
- return `CMCD=${encodeURIComponent(shaka.util.CmcdManager.serialize(data))}`;
538
+ return shaka.util.CmcdManager.serialize(data);
520
539
  }
521
540
 
522
541
  /**
@@ -535,8 +554,9 @@ shaka.util.CmcdManager = class {
535
554
  return uri;
536
555
  }
537
556
 
538
- const separator = uri.includes('?') ? '&' : '?';
539
- return `${uri}${separator}${query}`;
557
+ const url = new goog.Uri(uri);
558
+ url.getQueryData().set('CMCD', query);
559
+ return url.toString();
540
560
  }
541
561
  };
542
562
 
@@ -546,7 +566,7 @@ shaka.util.CmcdManager = class {
546
566
  * getBandwidthEstimate: function():number,
547
567
  * getBufferedInfo: function():shaka.extern.BufferedInfo,
548
568
  * getCurrentTime: function():number,
549
- * getManifest: function():shaka.extern.Manifest,
569
+ * getVariantTracks: function():Array.<shaka.extern.Track>,
550
570
  * getPlaybackRate: function():number,
551
571
  * isLive: function():boolean
552
572
  * }}
@@ -557,8 +577,8 @@ shaka.util.CmcdManager = class {
557
577
  * Get information about what the player has buffered.
558
578
  * @property {function():number} getCurrentTime
559
579
  * Get the current time
560
- * @property {function():shaka.extern.Manifest} getManifest
561
- * Get the manifest
580
+ * @property {function():Array.<shaka.extern.Track>} getVariantTracks
581
+ * Get the variant tracks
562
582
  * @property {function():number} getPlaybackRate
563
583
  * Get the playback rate
564
584
  * @property {function():boolean} isLive
@@ -608,180 +628,6 @@ shaka.util.CmcdManager.SegmentInfo;
608
628
  shaka.util.CmcdManager.ManifestInfo;
609
629
 
610
630
 
611
- /**
612
- * @typedef {{
613
- * br: (number|undefined),
614
- * d: (number|undefined),
615
- * ot: (shaka.util.CmcdManager.ObjectType|undefined),
616
- * tb: (number|undefined),
617
- * bl: (number|undefined),
618
- * dl: (number|undefined),
619
- * mtp: (number|undefined),
620
- * nor: (string|undefined),
621
- * nrr: (string|undefined),
622
- * su: (boolean|undefined),
623
- * cid: (string|undefined),
624
- * pr: (number|undefined),
625
- * sf: (shaka.util.CmcdManager.StreamingFormat|undefined),
626
- * sid: (string|undefined),
627
- * st: (shaka.util.CmcdManager.StreamType|undefined),
628
- * v: (number|undefined),
629
- * bs: (boolean|undefined),
630
- * rtp: (number|undefined)
631
- * }}
632
- *
633
- * @description
634
- * Client Media Common Data (CMCD) data.
635
- *
636
- * @property {number} br
637
- * The encoded bitrate of the audio or video object being requested. This may
638
- * not be known precisely by the player; however, it MAY be estimated based
639
- * upon playlist/manifest declarations. If the playlist declares both peak and
640
- * average bitrate values, the peak value should be transmitted.
641
- *
642
- * @property {number} d
643
- * The playback duration in milliseconds of the object being requested. If a
644
- * partial segment is being requested, then this value MUST indicate the
645
- * playback duration of that part and not that of its parent segment. This
646
- * value can be an approximation of the estimated duration if the explicit
647
- * value is not known.
648
- *
649
- * @property {shaka.util.CmcdManager.ObjectType} ot
650
- * The media type of the current object being requested:
651
- * - `m` = text file, such as a manifest or playlist
652
- * - `a` = audio only
653
- * - `v` = video only
654
- * - `av` = muxed audio and video
655
- * - `i` = init segment
656
- * - `c` = caption or subtitle
657
- * - `tt` = ISOBMFF timed text track
658
- * - `k` = cryptographic key, license or certificate.
659
- * - `o` = other
660
- *
661
- * If the object type being requested is unknown, then this key MUST NOT be
662
- * used.
663
- *
664
- * @property {number} tb
665
- * The highest bitrate rendition in the manifest or playlist that the client
666
- * is allowed to play, given current codec, licensing and sizing constraints.
667
- *
668
- * @property {number} bl
669
- * The buffer length associated with the media object being requested. This
670
- * value MUST be rounded to the nearest 100 ms. This key SHOULD only be sent
671
- * with an object type of ‘a’, ‘v’ or ‘av’.
672
- *
673
- * @property {number} dl
674
- * Deadline from the request time until the first sample of this
675
- * Segment/Object needs to be available in order to not create a buffer
676
- * underrun or any other playback problems. This value MUST be rounded to the
677
- * nearest 100ms. For a playback rate of 1, this may be equivalent to the
678
- * player’s remaining buffer length.
679
- *
680
- * @property {number} mtp
681
- * The throughput between client and server, as measured by the client and
682
- * MUST be rounded to the nearest 100 kbps. This value, however derived,
683
- * SHOULD be the value that the client is using to make its next Adaptive
684
- * Bitrate switching decision. If the client is connected to multiple
685
- * servers concurrently, it must take care to report only the throughput
686
- * measured against the receiving server. If the client has multiple
687
- * concurrent connections to the server, then the intent is that this value
688
- * communicates the aggregate throughput the client sees across all those
689
- * connections.
690
- *
691
- * @property {string} nor
692
- * Relative path of the next object to be requested. This can be used to
693
- * trigger pre-fetching by the CDN. This MUST be a path relative to the
694
- * current request. This string MUST be URLEncoded. The client SHOULD NOT
695
- * depend upon any pre-fetch action being taken - it is merely a request for
696
- * such a pre-fetch to take place.
697
- *
698
- * @property {string} nrr
699
- * If the next request will be a partial object request, then this string
700
- * denotes the byte range to be requested. If the ‘nor’ field is not set, then
701
- * the object is assumed to match the object currently being requested. The
702
- * client SHOULD NOT depend upon any pre-fetch action being taken – it is
703
- * merely a request for such a pre-fetch to take place. Formatting is similar
704
- * to the HTTP Range header, except that the unit MUST be ‘byte’, the ‘Range:’
705
- * prefix is NOT required and specifying multiple ranges is NOT allowed. Valid
706
- * combinations are:
707
- *
708
- * - `"\<range-start\>-"`
709
- * - `"\<range-start\>-\<range-end\>"`
710
- * - `"-\<suffix-length\>"`
711
- *
712
- * @property {boolean} su
713
- * Key is included without a value if the object is needed urgently due to
714
- * startup, seeking or recovery after a buffer-empty event. The media SHOULD
715
- * not be rendering when this request is made. This key MUST not be sent if it
716
- * is FALSE.
717
- *
718
- * @property {string} cid
719
- * A unique string identifying the current content. Maximum length is 64
720
- * characters. This value is consistent across multiple different sessions and
721
- * devices and is defined and updated at the discretion of the service
722
- * provider.
723
- *
724
- * @property {number} pr
725
- * The playback rate. `1` if real-time, `2` if double speed, `0` if not
726
- * playing. SHOULD only be sent if not equal to `1`.
727
- *
728
- * @property {shaka.util.CmcdManager.StreamingFormat} sf
729
- * The streaming format that defines the current request.
730
- *
731
- * - `d` = MPEG DASH
732
- * - `h` = HTTP Live Streaming (HLS)
733
- * - `s` = Smooth Streaming
734
- * - `o` = other
735
- *
736
- * If the streaming format being requested is unknown, then this key MUST NOT
737
- * be used.
738
- *
739
- * @property {string} sid
740
- * A GUID identifying the current playback session. A playback session
741
- * typically ties together segments belonging to a single media asset. Maximum
742
- * length is 64 characters. It is RECOMMENDED to conform to the UUID
743
- * specification.
744
- *
745
- * @property {shaka.util.CmcdManager.StreamType} st
746
- * Stream type
747
- * - `v` = all segments are available – e.g., VOD
748
- * - `l` = segments become available over time – e.g., LIVE
749
- *
750
- * @property {number} v
751
- * The version of this specification used for interpreting the defined key
752
- * names and values. If this key is omitted, the client and server MUST
753
- * interpret the values as being defined by version 1. Client SHOULD omit this
754
- * field if the version is 1.
755
- *
756
- * @property {boolean} bs
757
- * Buffer starvation key is included without a value if the buffer was starved
758
- * at some point between the prior request and this object request, resulting
759
- * in the player being in a rebuffering state and the video or audio playback
760
- * being stalled. This key MUST NOT be sent if the buffer was not starved
761
- * since the prior request.
762
- *
763
- * If the object type `ot` key is sent along with this key, then the `bs` key
764
- * refers to the buffer associated with the particular object type. If no
765
- * object type is communicated, then the buffer state applies to the current
766
- * session.
767
- *
768
- * @property {number} rtp
769
- * Requested maximum throughput
770
- *
771
- * The requested maximum throughput that the client considers sufficient for
772
- * delivery of the asset. Values MUST be rounded to the nearest 100kbps. For
773
- * example, a client would indicate that the current segment, encoded at
774
- * 2Mbps, is to be delivered at no more than 10Mbps, by using rtp=10000.
775
- *
776
- * Note: This can benefit clients by preventing buffer saturation through
777
- * over-delivery and can also deliver a community benefit through fair-share
778
- * delivery. The concept is that each client receives the throughput necessary
779
- * for great performance, but no more. The CDN may not support the rtp
780
- * feature.
781
- */
782
- shaka.util.CmcdManager.Data;
783
-
784
-
785
631
  /**
786
632
  * @enum {string}
787
633
  */
@@ -419,13 +419,48 @@ shaka.util.StreamUtils = class {
419
419
  */
420
420
  static async filterManifestByMediaCapabilities(
421
421
  manifest, usePersistentLicenses) {
422
- goog.asserts.assert(window.shakaMediaCapabilities,
422
+ goog.asserts.assert(navigator.mediaCapabilities,
423
423
  'MediaCapabilities should be valid.');
424
424
 
425
425
  await shaka.util.StreamUtils.getDecodingInfosForVariants(
426
426
  manifest.variants, usePersistentLicenses, /* srcEquals= */ false);
427
427
  manifest.variants = manifest.variants.filter((variant) => {
428
+ // See: https://github.com/google/shaka-player/issues/3860
428
429
  const video = variant.video;
430
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
431
+ if (video) {
432
+ let videoCodecs = shaka.util.StreamUtils.patchVp9(video.codecs);
433
+ // For multiplexed streams. Here we must check the audio of the
434
+ // stream to see if it is compatible.
435
+ if (video.codecs.includes(',')) {
436
+ const allCodecs = video.codecs.split(',');
437
+ videoCodecs = shaka.util.ManifestParserUtils.guessCodecs(
438
+ ContentType.VIDEO, allCodecs);
439
+ const audioCodecs = shaka.util.ManifestParserUtils.guessCodecs(
440
+ ContentType.AUDIO, allCodecs);
441
+ const audioFullType = shaka.util.MimeUtils.getFullOrConvertedType(
442
+ video.mimeType, audioCodecs, ContentType.AUDIO);
443
+ if (!MediaSource.isTypeSupported(audioFullType)) {
444
+ return false;
445
+ }
446
+ }
447
+ const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
448
+ video.mimeType, videoCodecs, ContentType.VIDEO);
449
+ if (!MediaSource.isTypeSupported(fullType)) {
450
+ return false;
451
+ }
452
+ }
453
+ const audio = variant.audio;
454
+ if (audio) {
455
+ const codecs =
456
+ shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
457
+ const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
458
+ audio.mimeType, codecs, ContentType.AUDIO);
459
+ if (!MediaSource.isTypeSupported(fullType)) {
460
+ return false;
461
+ }
462
+ }
463
+
429
464
  // See: https://github.com/google/shaka-player/issues/3380
430
465
  if (shaka.util.Platform.isXboxOne() && video &&
431
466
  ((video.width && video.width > 1920) ||
@@ -468,7 +503,7 @@ shaka.util.StreamUtils = class {
468
503
  return;
469
504
  }
470
505
 
471
- const mediaCapabilities = window.shakaMediaCapabilities;
506
+ const mediaCapabilities = navigator.mediaCapabilities;
472
507
 
473
508
  const operations = [];
474
509
  const getVariantDecodingInfos = (async (variant, decodingConfig) => {
@@ -562,12 +597,8 @@ shaka.util.StreamUtils = class {
562
597
  }
563
598
  }
564
599
  if (audio) {
565
- // Some Tizen devices seem to misreport AC-3 support, but correctly
566
- // report EC-3 support. So query EC-3 as a fallback for AC-3.
567
- // See https://github.com/google/shaka-player/issues/2989 for details.
568
600
  const codecs =
569
- (audio.codecs.toLowerCase() == 'ac-3' &&
570
- shaka.util.Platform.isTizen()) ? 'ec-3' : audio.codecs;
601
+ shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
571
602
  const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
572
603
  audio.mimeType, codecs, ContentType.AUDIO);
573
604
 
@@ -678,6 +709,26 @@ shaka.util.StreamUtils = class {
678
709
  return configs;
679
710
  }
680
711
 
712
+
713
+ /**
714
+ * Generates the correct audio codec for MediaDecodingConfiguration and
715
+ * for MediaSource.isTypeSupported.
716
+ * @param {string} codecs
717
+ * @return {string}
718
+ * @private
719
+ */
720
+ static getCorrectAudioCodecs_(codecs) {
721
+ // Some Tizen devices seem to misreport AC-3 support, but correctly
722
+ // report EC-3 support. So query EC-3 as a fallback for AC-3.
723
+ // See https://github.com/google/shaka-player/issues/2989 for details.
724
+ if (shaka.util.Platform.isTizen()) {
725
+ return codecs.toLowerCase() == 'ac-3' ? 'ec-3' : codecs;
726
+ } else {
727
+ return codecs;
728
+ }
729
+ }
730
+
731
+
681
732
  /**
682
733
  * MediaCapabilities supports 'vp09...' codecs, but not 'vp9'. Translate vp9
683
734
  * codec strings into 'vp09...', to allow such content to play with
@@ -989,6 +1040,31 @@ shaka.util.StreamUtils = class {
989
1040
  static imageStreamToTrack(stream) {
990
1041
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
991
1042
 
1043
+ let width = stream.width || null;
1044
+ let height = stream.height || null;
1045
+
1046
+ // The stream width and height represent the size of the entire thumbnail
1047
+ // sheet, so divide by the layout.
1048
+ let reference = null;
1049
+ // Note: segmentIndex is built by default for HLS, but not for DASH, but
1050
+ // in DASH this information comes at the stream level and not at the
1051
+ // segment level.
1052
+ if (stream.segmentIndex) {
1053
+ reference = stream.segmentIndex.get(0);
1054
+ }
1055
+ let layout = stream.tilesLayout;
1056
+ if (reference) {
1057
+ layout = reference.getTilesLayout() || layout;
1058
+ }
1059
+ if (layout && width != null) {
1060
+ width /= Number(layout.split('x')[0]);
1061
+ }
1062
+ if (layout && height != null) {
1063
+ height /= Number(layout.split('x')[1]);
1064
+ }
1065
+ // TODO: What happens if there are multiple grids, with different
1066
+ // layout sizes, inside this image stream?
1067
+
992
1068
  /** @type {shaka.extern.Track} */
993
1069
  const track = {
994
1070
  id: stream.id,
@@ -998,8 +1074,8 @@ shaka.util.StreamUtils = class {
998
1074
  language: '',
999
1075
  label: null,
1000
1076
  kind: null,
1001
- width: stream.width || null,
1002
- height: stream.height || null,
1077
+ width,
1078
+ height,
1003
1079
  frameRate: null,
1004
1080
  pixelAspectRatio: null,
1005
1081
  hdr: null,
@@ -1016,7 +1092,7 @@ shaka.util.StreamUtils = class {
1016
1092
  channelsCount: null,
1017
1093
  audioSamplingRate: null,
1018
1094
  spatialAudio: false,
1019
- tilesLayout: stream.tilesLayout || null,
1095
+ tilesLayout: layout || null,
1020
1096
  audioBandwidth: null,
1021
1097
  videoBandwidth: null,
1022
1098
  originalVideoId: null,
@@ -103,6 +103,25 @@ shaka.util.XmlUtils = class {
103
103
  }
104
104
 
105
105
 
106
+ /**
107
+ * Gets a namespace-qualified attribute.
108
+ * @param {!Element} elem The element to get from.
109
+ * @param {!Array.<string>} nsList The lis of namespace URIs.
110
+ * @param {string} name The local name of the attribute.
111
+ * @return {?string} The attribute's value, or null if not present.
112
+ */
113
+ static getAttributeNSList(elem, nsList, name) {
114
+ // Some browsers return the empty string when the attribute is missing,
115
+ // so check if it exists first. See: https://mzl.la/2L7F0UK
116
+ for (const ns of nsList) {
117
+ if (elem.hasAttributeNS(ns, name)) {
118
+ return elem.getAttributeNS(ns, name);
119
+ }
120
+ }
121
+ return null;
122
+ }
123
+
124
+
106
125
  /**
107
126
  * Gets the text contents of a node.
108
127
  * @param {!Node} elem The XML element.
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": "3.3.0",
4
+ "version": "3.3.1",
5
5
  "homepage": "https://github.com/google/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -12,6 +12,7 @@
12
12
  ],
13
13
  "devDependencies": {
14
14
  "@chiragrupani/karma-chromium-edge-launcher": "^2.1.0",
15
+ "@onslip/karma-safari-launcher": "^1.0.1",
15
16
  "@teppeis/clutz": "^1.0.29-4c95e12.v20190929",
16
17
  "awesomplete": "^1.1.4",
17
18
  "babel-core": "^6.26.3",
@@ -44,7 +45,6 @@
44
45
  "karma-jasmine": "^3.3.1",
45
46
  "karma-jasmine-ajax": "^0.1.13",
46
47
  "karma-opera-launcher": "^1.0.0",
47
- "karma-safari-launcher": "^1.0.0",
48
48
  "karma-sourcemap-loader": "^0.3.7",
49
49
  "karma-spec-reporter": "0.0.32",
50
50
  "karma-webdriver-launcher": "^1.0.8",
@@ -31,7 +31,7 @@ describe('DrmEngine', () => {
31
31
  navigator.requestMediaKeySystemAccess;
32
32
  const originalLogError = shaka.log.error;
33
33
  const originalBatchTime = shaka.media.DrmEngine.KEY_STATUS_BATCH_TIME;
34
- const originalDecodingInfo = window.shakaMediaCapabilities.decodingInfo;
34
+ const originalDecodingInfo = navigator.mediaCapabilities.decodingInfo;
35
35
 
36
36
  /** @type {!jasmine.Spy} */
37
37
  let decodingInfoSpy;
@@ -76,7 +76,7 @@ describe('DrmEngine', () => {
76
76
 
77
77
  beforeEach(() => {
78
78
  decodingInfoSpy = jasmine.createSpy('decodingInfo');
79
- window.shakaMediaCapabilities.decodingInfo =
79
+ navigator.mediaCapabilities.decodingInfo =
80
80
  shaka.test.Util.spyFunc(decodingInfoSpy);
81
81
 
82
82
  logErrorSpy = jasmine.createSpy('shaka.log.error');
@@ -153,7 +153,7 @@ describe('DrmEngine', () => {
153
153
 
154
154
  navigator.requestMediaKeySystemAccess =
155
155
  originalRequestMediaKeySystemAccess;
156
- window.shakaMediaCapabilities.decodingInfo = originalDecodingInfo;
156
+ navigator.mediaCapabilities.decodingInfo = originalDecodingInfo;
157
157
  shaka.log.error = originalLogError;
158
158
  });
159
159
 
@@ -420,8 +420,9 @@ describe('StreamingEngine', () => {
420
420
  // 3. Playhead seeks to force us back inside the window
421
421
  // 4. (maybe) seek if there is a gap at the period boundary
422
422
  // 5. (maybe) seek to flush a pipeline stall
423
+ // 6. (maybe) on slower platforms (e.g. GitHub actions)
423
424
  expect(seekCount).toBeGreaterThan(2);
424
- expect(seekCount).toBeLessThan(6);
425
+ expect(seekCount).toBeLessThan(7);
425
426
  });
426
427
  });
427
428