shaka-player 4.2.3 → 4.3.0

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 (147) hide show
  1. package/.github/workflows/sync-labels.yaml +1 -1
  2. package/.github/workflows/update-issues.yaml +1 -1
  3. package/AUTHORS +5 -0
  4. package/CHANGELOG.md +85 -39
  5. package/CONTRIBUTORS +5 -0
  6. package/README.md +14 -0
  7. package/demo/common/assets.js +58 -0
  8. package/demo/common/message_ids.js +22 -1
  9. package/demo/config.js +89 -5
  10. package/demo/demo.less +7 -1
  11. package/demo/index.html +8 -1
  12. package/demo/locales/en.json +21 -1
  13. package/demo/locales/source.json +82 -2
  14. package/demo/main.js +57 -1
  15. package/demo/search.js +2 -0
  16. package/demo/visualizer.js +337 -0
  17. package/demo/visualizer_button.js +103 -0
  18. package/dist/controls.css +1 -1
  19. package/dist/controls.css.map +1 -1
  20. package/dist/demo.compiled.debug.js +83 -58
  21. package/dist/demo.compiled.debug.map +4 -4
  22. package/dist/demo.compiled.js +83 -58
  23. package/dist/demo.compiled.map +4 -4
  24. package/dist/demo.css +1 -1
  25. package/dist/demo.css.map +1 -1
  26. package/dist/deps.js +18 -11
  27. package/dist/locales.js +32 -32
  28. package/dist/receiver.compiled.debug.js +23 -19
  29. package/dist/receiver.compiled.debug.map +3 -3
  30. package/dist/receiver.compiled.js +23 -19
  31. package/dist/receiver.compiled.map +3 -3
  32. package/dist/shaka-player.compiled.d.ts +178 -27
  33. package/dist/shaka-player.compiled.debug.d.ts +178 -27
  34. package/dist/shaka-player.compiled.debug.externs.js +163 -32
  35. package/dist/shaka-player.compiled.debug.js +488 -422
  36. package/dist/shaka-player.compiled.debug.map +4 -4
  37. package/dist/shaka-player.compiled.externs.js +163 -32
  38. package/dist/shaka-player.compiled.js +895 -857
  39. package/dist/shaka-player.compiled.map +4 -4
  40. package/dist/shaka-player.ui.d.ts +178 -27
  41. package/dist/shaka-player.ui.debug.d.ts +178 -27
  42. package/dist/shaka-player.ui.debug.externs.js +163 -32
  43. package/dist/shaka-player.ui.debug.js +519 -450
  44. package/dist/shaka-player.ui.debug.map +4 -4
  45. package/dist/shaka-player.ui.externs.js +163 -32
  46. package/dist/shaka-player.ui.js +1053 -1015
  47. package/dist/shaka-player.ui.map +4 -4
  48. package/docs/design/lcevc-architecture.png +0 -0
  49. package/docs/design/lcevc-demo.png +0 -0
  50. package/docs/design/lcevc-integration.md +72 -0
  51. package/docs/tutorials/fairplay.md +7 -0
  52. package/docs/tutorials/faq.md +1 -1
  53. package/docs/tutorials/ui.md +7 -7
  54. package/docs/tutorials/upgrade.md +6 -0
  55. package/externs/ima.js +13 -1
  56. package/externs/lcevc.js +81 -0
  57. package/externs/mux.js +4 -42
  58. package/externs/shaka/ads.js +11 -1
  59. package/externs/shaka/manifest_parser.js +5 -1
  60. package/externs/shaka/player.js +178 -15
  61. package/lib/abr/simple_abr_manager.js +7 -0
  62. package/lib/ads/ad_manager.js +0 -8
  63. package/lib/ads/client_side_ad.js +15 -0
  64. package/lib/ads/client_side_ad_manager.js +21 -8
  65. package/lib/ads/server_side_ad.js +15 -0
  66. package/lib/ads/server_side_ad_manager.js +3 -3
  67. package/lib/cast/cast_proxy.js +2 -1
  68. package/lib/cast/cast_receiver.js +19 -5
  69. package/lib/cast/cast_sender.js +2 -1
  70. package/lib/cast/cast_utils.js +18 -6
  71. package/lib/cea/dummy_cea_parser.js +28 -0
  72. package/lib/cea/i_cea_parser.js +14 -2
  73. package/lib/cea/mp4_cea_parser.js +87 -5
  74. package/lib/config/auto_show_text.js +33 -0
  75. package/lib/dash/dash_parser.js +1 -1
  76. package/lib/hls/hls_parser.js +799 -376
  77. package/lib/lcevc/lcevc_dil.js +152 -0
  78. package/lib/media/closed_caption_parser.js +9 -6
  79. package/lib/media/drm_engine.js +14 -6
  80. package/lib/media/media_source_engine.js +112 -26
  81. package/lib/media/playhead.js +23 -1
  82. package/lib/media/presentation_timeline.js +26 -11
  83. package/lib/media/segment_reference.js +49 -1
  84. package/lib/media/streaming_engine.js +145 -24
  85. package/lib/media/transmuxer.js +79 -18
  86. package/lib/offline/storage.js +1 -0
  87. package/lib/player.js +477 -85
  88. package/lib/polyfill/patchedmediakeys_apple.js +55 -1
  89. package/lib/text/ui_text_displayer.js +5 -5
  90. package/lib/text/vtt_text_parser.js +35 -1
  91. package/lib/util/dom_utils.js +0 -1
  92. package/lib/util/error.js +17 -0
  93. package/lib/util/fake_event.js +2 -0
  94. package/lib/util/id3_utils.js +300 -0
  95. package/lib/util/manifest_parser_utils.js +4 -0
  96. package/lib/util/mime_utils.js +3 -6
  97. package/lib/util/mp4_box_parsers.js +15 -5
  98. package/lib/util/platform.js +9 -1
  99. package/lib/util/player_configuration.js +23 -1
  100. package/lib/util/ts_parser.js +499 -0
  101. package/maintained-branches.md +2 -2
  102. package/package.json +4 -1
  103. package/roadmap.md +27 -7
  104. package/test/ads/ad_manager_unit.js +180 -29
  105. package/test/cast/cast_receiver_integration.js +97 -16
  106. package/test/cast/cast_receiver_unit.js +17 -12
  107. package/test/cast/cast_utils_unit.js +4 -1
  108. package/test/dash/dash_parser_content_protection_unit.js +1 -0
  109. package/test/dash/dash_parser_live_unit.js +1 -0
  110. package/test/dash/dash_parser_manifest_unit.js +93 -1
  111. package/test/dash/dash_parser_segment_base_unit.js +1 -0
  112. package/test/dash/dash_parser_segment_list_unit.js +1 -0
  113. package/test/dash/dash_parser_segment_template_unit.js +1 -0
  114. package/test/demo/demo_unit.js +2 -1
  115. package/test/hls/hls_live_unit.js +253 -19
  116. package/test/hls/hls_parser_unit.js +500 -12
  117. package/test/media/closed_caption_parser_unit.js +2 -1
  118. package/test/media/drm_engine_integration.js +3 -1
  119. package/test/media/media_source_engine_integration.js +102 -6
  120. package/test/media/media_source_engine_unit.js +10 -5
  121. package/test/media/playhead_unit.js +41 -0
  122. package/test/media/streaming_engine_integration.js +17 -1
  123. package/test/media/streaming_engine_unit.js +180 -24
  124. package/test/media/transmuxer_integration.js +21 -10
  125. package/test/player_integration.js +224 -82
  126. package/test/player_unit.js +92 -2
  127. package/test/polyfill/patchedmediakeys_apple_unit.js +124 -0
  128. package/test/test/assets/id3-metadata.aac +0 -0
  129. package/test/test/assets/id3-metadata.ts +0 -0
  130. package/test/test/assets/thumbnails-sprites.vtt +10 -0
  131. package/test/test/assets/thumbnails.vtt +10 -0
  132. package/test/test/util/dash_parser_util.js +2 -0
  133. package/test/test/util/fake_ad.js +21 -0
  134. package/test/test/util/fake_media_source_engine.js +7 -0
  135. package/test/test/util/id3_generator.js +100 -0
  136. package/test/test/util/manifest_generator.js +3 -6
  137. package/test/test/util/simple_fakes.js +7 -0
  138. package/test/test/util/stream_generator.js +38 -0
  139. package/test/test/util/test_scheme.js +43 -14
  140. package/test/text/vtt_text_parser_unit.js +11 -0
  141. package/test/ui/text_displayer_layout_unit.js +14 -0
  142. package/test/util/id3_utils_unit.js +116 -0
  143. package/test/util/ts_parser_unit.js +77 -0
  144. package/ui/controls.js +4 -1
  145. package/ui/less/containers.less +7 -0
  146. package/ui/settings_menu.js +11 -2
  147. package/ui/ui.js +43 -4
@@ -0,0 +1,152 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.lcevc.Dil');
8
+ goog.require('shaka.log');
9
+ goog.require('shaka.util.IReleasable');
10
+
11
+ /**
12
+ * @summary
13
+ * lcevcDil - (MPEG-5 Part 2 LCEVC - Decoder Integration Layer) provides
14
+ * all the operations related to the enhancement and rendering
15
+ * of LCEVC enabled streams and on to a canvas.
16
+ * @implements {shaka.util.IReleasable}
17
+ * @export
18
+ */
19
+ shaka.lcevc.Dil = class {
20
+ /**
21
+ * @param {HTMLVideoElement} media The video element that will be attached to
22
+ * LCEVC Dil for input.
23
+ * @param {HTMLCanvasElement} canvas The canvas element that will be attached
24
+ * to LCEVC Dil to render the enhanced frames.
25
+ * @param {shaka.extern.LcevcConfiguration} dilConfig The LCEVC DIL
26
+ * config object to initialize the LCEVC DIL.
27
+ */
28
+ constructor(media, canvas, dilConfig) {
29
+ /** @private {?LcevcDil.LcevcDIL} */
30
+ this.dil_ = null;
31
+
32
+ /** @private {number} */
33
+ this.variantId_ = -1;
34
+
35
+ /** @private {HTMLVideoElement} */
36
+ this.media_ = media;
37
+
38
+ /** @private {HTMLCanvasElement} */
39
+ this.canvas_ = canvas;
40
+
41
+ /** @private {shaka.extern.LcevcConfiguration} */
42
+ this.dilConfig_ = dilConfig;
43
+
44
+ this.create_();
45
+ }
46
+
47
+ /**
48
+ * Append data to the LCEVC Dil.
49
+ * @param {BufferSource} data
50
+ */
51
+ appendBuffer(data) {
52
+ if (this.dil_) {
53
+ this.dil_.appendBuffer(data, 'video', this.variantId_);
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Hide the canvas specifically in the case of a DRM Content
59
+ */
60
+ hideCanvas() {
61
+ if (this.dil_) {
62
+ this.canvas_.classList.add('shaka-hidden');
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Create LCEVC Dil.
68
+ * @private
69
+ */
70
+ create_() {
71
+ if (this.isSupported_() && !this.dil_) {
72
+ if (LcevcDil.SupportObject.webGLSupport(this.canvas_)) {
73
+ // Make sure the canvas is not hidden from a previous playback session.
74
+ this.canvas_.classList.remove('shaka-hidden');
75
+ this.dil_ = new LcevcDil.LcevcDil(
76
+ this.media_,
77
+ this.canvas_,
78
+ this.dilConfig_);
79
+ }
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Close LCEVC Dil.
85
+ * @override
86
+ * @export
87
+ */
88
+ release() {
89
+ if (this.dil_) {
90
+ this.dil_.close();
91
+ this.dil_ = null;
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Check if the LCEVC Dil lib is present and is supported by the browser.
97
+ * @return {boolean}
98
+ * @private
99
+ */
100
+ isSupported_() {
101
+ if (typeof libDPIModule === 'undefined') {
102
+ shaka.log.alwaysWarn(
103
+ 'Could not Find LCEVC Library dependencies on this page');
104
+ }
105
+
106
+ if (typeof LcevcDil === 'undefined') {
107
+ shaka.log.alwaysWarn('Could not Find LCEVC Library on this page');
108
+ } else {
109
+ if (!LcevcDil.SupportObject.SupportStatus) {
110
+ shaka.log.alwaysWarn(LcevcDil.SupportObject.SupportError);
111
+ }
112
+ }
113
+
114
+ return typeof LcevcDil !== 'undefined' &&
115
+ typeof libDPIModule !== 'undefined' &&
116
+ this.canvas_ instanceof HTMLCanvasElement &&
117
+ LcevcDil.SupportObject.SupportStatus;
118
+ }
119
+
120
+ /**
121
+ * Update current active variant
122
+ * @param {shaka.extern.Track} track
123
+ */
124
+ updateVariant(track) {
125
+ let containerFormat = shaka.lcevc.Dil.ContainerFormat.MPEG2_TS;
126
+ switch (track.mimeType) {
127
+ case 'video/webm': {
128
+ containerFormat = shaka.lcevc.Dil.ContainerFormat.WEBM;
129
+ break;
130
+ }
131
+ case 'video/mp4': {
132
+ containerFormat = shaka.lcevc.Dil.ContainerFormat.MP4;
133
+ break;
134
+ }
135
+ }
136
+ if (this.dil_) {
137
+ this.variantId_ = track.id;
138
+ this.dil_.setLevelSwitching(track.id, true);
139
+ this.dil_.setContainerFormat(containerFormat);
140
+ }
141
+ }
142
+ };
143
+
144
+ /**
145
+ * Container Formats.
146
+ * @const @enum {number}
147
+ */
148
+ shaka.lcevc.Dil.ContainerFormat = {
149
+ MPEG2_TS: 0,
150
+ WEBM: 1,
151
+ MP4: 2,
152
+ };
@@ -8,6 +8,7 @@ goog.provide('shaka.media.ClosedCaptionParser');
8
8
  goog.provide('shaka.media.IClosedCaptionParser');
9
9
 
10
10
  goog.require('shaka.cea.CeaDecoder');
11
+ goog.require('shaka.cea.DummyCeaParser');
11
12
  goog.require('shaka.cea.Mp4CeaParser');
12
13
  goog.require('shaka.util.BufferUtils');
13
14
  goog.requireType('shaka.cea.ICaptionDecoder');
@@ -54,12 +55,14 @@ shaka.media.IClosedCaptionParser = class {
54
55
  */
55
56
  shaka.media.ClosedCaptionParser = class {
56
57
  /** */
57
- constructor() {
58
- /**
59
- * MP4 Parser to extract closed caption packets from H.264 video.
60
- * @private {!shaka.cea.ICeaParser}
61
- */
62
- this.ceaParser_ = new shaka.cea.Mp4CeaParser();
58
+ constructor(mimeType) {
59
+ /** @private {!shaka.cea.ICeaParser} */
60
+ this.ceaParser_ = new shaka.cea.DummyCeaParser();
61
+
62
+ if (mimeType.includes('video/mp4')) {
63
+ // MP4 Parser to extract closed caption packets from H.264 video.
64
+ this.ceaParser_ = new shaka.cea.Mp4CeaParser();
65
+ }
63
66
 
64
67
  /**
65
68
  * Decoder for decoding CEA-X08 data from closed caption packets.
@@ -845,7 +845,7 @@ shaka.media.DrmEngine = class {
845
845
  if (shaka.media.Transmuxer.isSupported(realMimeType)) {
846
846
  // This will be handled by the Transmuxer, so use the MIME type that the
847
847
  // Transmuxer will produce.
848
- return shaka.media.Transmuxer.convertTsCodecs(stream.type, realMimeType);
848
+ return shaka.media.Transmuxer.convertCodecs(stream.type, realMimeType);
849
849
  }
850
850
  return realMimeType;
851
851
  }
@@ -1113,13 +1113,21 @@ shaka.media.DrmEngine = class {
1113
1113
  const keys = [];
1114
1114
  const keyIds = [];
1115
1115
 
1116
- clearKeys.forEach((keyHex, keyIdHex) => {
1117
- const keyId = Uint8ArrayUtils.fromHex(keyIdHex);
1118
- const key = Uint8ArrayUtils.fromHex(keyHex);
1116
+ clearKeys.forEach((key, keyId) => {
1117
+ let kid = keyId;
1118
+ if (kid.length != 22) {
1119
+ kid = Uint8ArrayUtils.toBase64(
1120
+ Uint8ArrayUtils.fromHex(keyId), false);
1121
+ }
1122
+ let k = key;
1123
+ if (k.length != 22) {
1124
+ k = Uint8ArrayUtils.toBase64(
1125
+ Uint8ArrayUtils.fromHex(key), false);
1126
+ }
1119
1127
  const keyObj = {
1120
1128
  kty: 'oct',
1121
- kid: Uint8ArrayUtils.toBase64(keyId, false),
1122
- k: Uint8ArrayUtils.toBase64(key, false),
1129
+ kid: kid,
1130
+ k: k,
1123
1131
  };
1124
1132
 
1125
1133
  keys.push(keyObj);
@@ -9,20 +9,25 @@ goog.provide('shaka.media.MediaSourceEngine');
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.log');
11
11
  goog.require('shaka.media.ContentWorkarounds');
12
+ goog.require('shaka.media.ClosedCaptionParser');
12
13
  goog.require('shaka.media.IClosedCaptionParser');
13
14
  goog.require('shaka.media.SegmentReference');
14
15
  goog.require('shaka.media.TimeRangesUtils');
15
16
  goog.require('shaka.media.Transmuxer');
16
17
  goog.require('shaka.text.TextEngine');
18
+ goog.require('shaka.util.BufferUtils');
17
19
  goog.require('shaka.util.Destroyer');
18
20
  goog.require('shaka.util.Error');
19
21
  goog.require('shaka.util.EventManager');
20
22
  goog.require('shaka.util.Functional');
21
23
  goog.require('shaka.util.IDestroyable');
24
+ goog.require('shaka.util.Id3Utils');
22
25
  goog.require('shaka.util.ManifestParserUtils');
23
26
  goog.require('shaka.util.MimeUtils');
24
27
  goog.require('shaka.util.Platform');
25
28
  goog.require('shaka.util.PublicPromise');
29
+ goog.require('shaka.util.TsParser');
30
+ goog.require('shaka.lcevc.Dil');
26
31
 
27
32
 
28
33
  /**
@@ -38,21 +43,22 @@ shaka.media.MediaSourceEngine = class {
38
43
  /**
39
44
  * @param {HTMLMediaElement} video The video element, whose source is tied to
40
45
  * MediaSource during the lifetime of the MediaSourceEngine.
41
- * @param {!shaka.media.IClosedCaptionParser} closedCaptionParser
42
- * The closed caption parser that should be used to parser closed captions
43
- * from the video stream. MediaSourceEngine takes ownership of the parser.
44
- * When MediaSourceEngine is destroyed, it will destroy the parser.
45
46
  * @param {!shaka.extern.TextDisplayer} textDisplayer
46
47
  * The text displayer that will be used with the text engine.
47
48
  * MediaSourceEngine takes ownership of the displayer. When
48
49
  * MediaSourceEngine is destroyed, it will destroy the displayer.
49
50
  * @param {!function(!Array.<shaka.extern.ID3Metadata>, number, ?number)=}
50
51
  * onMetadata
52
+ * @param {?shaka.lcevc.Dil} [lcevcDil] Optional - LCEVC Dil Object
53
+ *
51
54
  */
52
- constructor(video, closedCaptionParser, textDisplayer, onMetadata) {
55
+ constructor(video, textDisplayer, onMetadata, lcevcDil) {
53
56
  /** @private {HTMLMediaElement} */
54
57
  this.video_ = video;
55
58
 
59
+ /** @private {?shaka.extern.MediaSourceConfiguration} */
60
+ this.config_ = null;
61
+
56
62
  /** @private {shaka.extern.TextDisplayer} */
57
63
  this.textDisplayer_ = textDisplayer;
58
64
 
@@ -81,6 +87,9 @@ shaka.media.MediaSourceEngine = class {
81
87
  number, ?number)} */
82
88
  this.onMetadata_ = onMetadata || onMetadataNoOp;
83
89
 
90
+ /** @private {?shaka.lcevc.Dil} */
91
+ this.lcevcDil_ = lcevcDil || null;
92
+
84
93
  /**
85
94
  * @private {!Object.<string,
86
95
  * !Array.<shaka.media.MediaSourceEngine.Operation>>}
@@ -93,8 +102,8 @@ shaka.media.MediaSourceEngine = class {
93
102
  /** @private {!Object.<string, !shaka.media.Transmuxer>} */
94
103
  this.transmuxers_ = {};
95
104
 
96
- /** @private {shaka.media.IClosedCaptionParser} */
97
- this.captionParser_ = closedCaptionParser;
105
+ /** @private {?shaka.media.IClosedCaptionParser} */
106
+ this.captionParser_ = null;
98
107
 
99
108
  /** @private {!shaka.util.PublicPromise} */
100
109
  this.mediaSourceOpen_ = new shaka.util.PublicPromise();
@@ -291,6 +300,7 @@ shaka.media.MediaSourceEngine = class {
291
300
  this.video_ = null;
292
301
  }
293
302
 
303
+ this.config_ = null;
294
304
  this.mediaSource_ = null;
295
305
  this.textEngine_ = null;
296
306
  this.textDisplayer_ = null;
@@ -305,6 +315,9 @@ shaka.media.MediaSourceEngine = class {
305
315
  }
306
316
  }
307
317
  this.queues_ = {};
318
+
319
+ // This object is owned by Player
320
+ this.lcevcDil_ = null;
308
321
  }
309
322
 
310
323
  /**
@@ -325,15 +338,16 @@ shaka.media.MediaSourceEngine = class {
325
338
  * shaka.extern.Stream>} streamsByType
326
339
  * A map of content types to streams. All streams must be supported
327
340
  * according to MediaSourceEngine.isStreamSupported.
328
- * @param {boolean} forceTransmuxTS
329
- * If true, this will transmux TS content even if it is natively supported.
341
+ * @param {boolean} forceTransmux
342
+ * If true, this will transmux AAC and TS content even if it is natively
343
+ * supported.
330
344
  * @param {boolean=} sequenceMode
331
345
  * If true, the media segments are appended to the SourceBuffer in strict
332
346
  * sequence.
333
347
  *
334
348
  * @return {!Promise}
335
349
  */
336
- async init(streamsByType, forceTransmuxTS, sequenceMode=false) {
350
+ async init(streamsByType, forceTransmux, sequenceMode=false) {
337
351
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
338
352
 
339
353
  await this.mediaSourceOpen_;
@@ -351,14 +365,15 @@ shaka.media.MediaSourceEngine = class {
351
365
  if (contentType == ContentType.TEXT) {
352
366
  this.reinitText(mimeType, sequenceMode);
353
367
  } else {
354
- if ((forceTransmuxTS || !MediaSource.isTypeSupported(mimeType)) &&
368
+ if ((forceTransmux || !MediaSource.isTypeSupported(mimeType)) &&
355
369
  shaka.media.Transmuxer.isSupported(mimeType, contentType)) {
356
- this.transmuxers_[contentType] = new shaka.media.Transmuxer();
370
+ this.transmuxers_[contentType] =
371
+ new shaka.media.Transmuxer(mimeType);
357
372
  mimeType =
358
- shaka.media.Transmuxer.convertTsCodecs(contentType, mimeType);
373
+ shaka.media.Transmuxer.convertCodecs(contentType, mimeType);
359
374
  }
360
-
361
- const sourceBuffer = this.mediaSource_.addSourceBuffer(mimeType);
375
+ const type = mimeType + this.config_.sourceBufferExtraFeatures;
376
+ const sourceBuffer = this.mediaSource_.addSourceBuffer(type);
362
377
 
363
378
  this.eventManager_.listen(
364
379
  sourceBuffer, 'error',
@@ -374,6 +389,16 @@ shaka.media.MediaSourceEngine = class {
374
389
  }
375
390
  }
376
391
 
392
+ /**
393
+ * Called by the Player to provide an updated configuration any time it
394
+ * changes. Must be called at least once before init().
395
+ *
396
+ * @param {shaka.extern.MediaSourceConfiguration} config
397
+ */
398
+ configure(config) {
399
+ this.config_ = config;
400
+ }
401
+
377
402
  /**
378
403
  * Reinitialize the TextEngine for a new text type.
379
404
  * @param {string} mimeType
@@ -509,6 +534,17 @@ shaka.media.MediaSourceEngine = class {
509
534
  }
510
535
  }
511
536
 
537
+ /**
538
+ * Create a new closed caption parser. This will ONLY be replaced by tests as
539
+ * a way to inject fake closed caption parser instances.
540
+ *
541
+ * @param {string} mimeType
542
+ * @return {!shaka.media.IClosedCaptionParser}
543
+ */
544
+ getCaptionParser(mimeType) {
545
+ return new shaka.media.ClosedCaptionParser(mimeType);
546
+ }
547
+
512
548
  /**
513
549
  * Enqueue an operation to append data to the SourceBuffer.
514
550
  * Start and end times are needed for TextEngine, but not for MediaSource.
@@ -544,7 +580,44 @@ shaka.media.MediaSourceEngine = class {
544
580
  return;
545
581
  }
546
582
 
583
+ const uint8ArrayData = shaka.util.BufferUtils.toUint8(data);
584
+ let mimeType = this.sourceBufferTypes_[contentType];
585
+ if (this.transmuxers_[contentType]) {
586
+ mimeType = this.transmuxers_[contentType].getOrginalMimeType();
587
+ }
588
+ if (shaka.util.TsParser.probe(uint8ArrayData)) {
589
+ const metadata = new shaka.util.TsParser().parse(uint8ArrayData)
590
+ .getMetadata();
591
+ if (metadata.length) {
592
+ const timestampOffset =
593
+ this.sourceBuffers_[contentType].timestampOffset;
594
+ this.onMetadata_(metadata, timestampOffset,
595
+ reference ? reference.endTime : null);
596
+ }
597
+ } else {
598
+ if (shaka.media.MediaSourceEngine.RAW_FORMATS.includes(mimeType)) {
599
+ const frames = shaka.util.Id3Utils.getID3Frames(uint8ArrayData);
600
+ if (frames.length && reference) {
601
+ /** @private {shaka.extern.ID3Metadata} */
602
+ const metadata = {
603
+ cueTime: reference.startTime,
604
+ data: uint8ArrayData,
605
+ frames: frames,
606
+ dts: reference.startTime,
607
+ pts: reference.startTime,
608
+ };
609
+ this.onMetadata_([metadata], /* offset= */ 0, reference.endTime);
610
+ }
611
+ }
612
+ }
613
+
547
614
  if (this.transmuxers_[contentType]) {
615
+ // When seeked we should reset the transmuxer captionstreams
616
+ // so it does not ignores the captions from previous segments
617
+ if (seeked) {
618
+ this.transmuxers_[contentType].resetCaptions();
619
+ }
620
+
548
621
  const transmuxedData =
549
622
  await this.transmuxers_[contentType].transmux(data);
550
623
  // For HLS CEA-608/708 CLOSED-CAPTIONS, text data is embedded in
@@ -552,15 +625,6 @@ shaka.media.MediaSourceEngine = class {
552
625
  if (!this.textEngine_) {
553
626
  this.reinitText('text/vtt', this.sequenceMode_);
554
627
  }
555
-
556
- if (transmuxedData.metadata) {
557
- const timestampOffset =
558
- this.sourceBuffers_[contentType].timestampOffset;
559
- this.onMetadata_(
560
- transmuxedData.metadata,
561
- timestampOffset,
562
- reference ? reference.endTime : null);
563
- }
564
628
  // This doesn't work for native TS support (ex. Edge/Chromecast),
565
629
  // since no transmuxing is needed for native TS.
566
630
  if (transmuxedData.captions && transmuxedData.captions.length) {
@@ -576,10 +640,13 @@ shaka.media.MediaSourceEngine = class {
576
640
  }
577
641
 
578
642
  data = transmuxedData.data;
579
- } else if (hasClosedCaptions) {
643
+ } else if (hasClosedCaptions && contentType == ContentType.VIDEO) {
580
644
  if (!this.textEngine_) {
581
645
  this.reinitText('text/vtt', this.sequenceMode_);
582
646
  }
647
+ if (!this.captionParser_) {
648
+ this.captionParser_ = this.getCaptionParser(mimeType);
649
+ }
583
650
  // If it is the init segment for closed captions, initialize the closed
584
651
  // caption parser.
585
652
  if (!reference) {
@@ -773,7 +840,9 @@ shaka.media.MediaSourceEngine = class {
773
840
  * Fully reset the state of the caption parser owned by MediaSourceEngine.
774
841
  */
775
842
  resetCaptionParser() {
776
- this.captionParser_.reset();
843
+ if (this.captionParser_) {
844
+ this.captionParser_.reset();
845
+ }
777
846
  }
778
847
 
779
848
  /**
@@ -912,6 +981,15 @@ shaka.media.MediaSourceEngine = class {
912
981
  * @private
913
982
  */
914
983
  append_(contentType, data) {
984
+ const ContentType = shaka.util.ManifestParserUtils.ContentType;
985
+
986
+ // Append only video data to the LCEVC Dil.
987
+ if (contentType == ContentType.VIDEO && this.lcevcDil_) {
988
+ // Append video buffers to the LCEVC Dil for parsing and storing
989
+ // of LCEVC data.
990
+ this.lcevcDil_.appendBuffer(data);
991
+ }
992
+
915
993
  // This will trigger an 'updateend' event.
916
994
  this.sourceBuffers_[contentType].appendBuffer(data);
917
995
  }
@@ -1277,6 +1355,14 @@ shaka.media.MediaSourceEngine = class {
1277
1355
 
1278
1356
  return segment;
1279
1357
  }
1358
+
1359
+ /**
1360
+ * Update LCEVC DIL object when ready for LCEVC Decodes
1361
+ * @param {?shaka.lcevc.Dil} lcevcDil
1362
+ */
1363
+ updateLcevcDil(lcevcDil) {
1364
+ this.lcevcDil_ = lcevcDil;
1365
+ }
1280
1366
  };
1281
1367
 
1282
1368
 
@@ -33,6 +33,14 @@ goog.requireType('shaka.media.PresentationTimeline');
33
33
  * @interface
34
34
  */
35
35
  shaka.media.Playhead = class {
36
+ /**
37
+ * Called when the Player is ready to begin playback. Anything that depends
38
+ * on setStartTime() should be done here, not in the constructor.
39
+ *
40
+ * @see https://github.com/shaka-project/shaka-player/issues/4244
41
+ */
42
+ ready() {}
43
+
36
44
  /**
37
45
  * Set the start time. If the content has already started playback, this will
38
46
  * be ignored.
@@ -90,6 +98,14 @@ shaka.media.SrcEqualsPlayhead = class {
90
98
 
91
99
  /** @private {shaka.util.EventManager} */
92
100
  this.eventManager_ = new shaka.util.EventManager();
101
+ }
102
+
103
+ /** @override */
104
+ ready() {
105
+ goog.asserts.assert(
106
+ this.mediaElement_ != null,
107
+ 'Playhead should not be released before calling ready()',
108
+ );
93
109
 
94
110
  // We listen for the loaded-data-event so that we know when we can
95
111
  // interact with |currentTime|.
@@ -102,6 +118,7 @@ shaka.media.SrcEqualsPlayhead = class {
102
118
  this.eventManager_.listenOnce(this.mediaElement_, 'seeking', () => {
103
119
  this.started_ = true;
104
120
  });
121
+
105
122
  const currentTime = this.mediaElement_.currentTime;
106
123
  // Using the currentTime allows using a negative number in Live HLS
107
124
  const newTime = Math.max(0, currentTime + this.startTime_);
@@ -239,7 +256,12 @@ shaka.media.MediaSourcePlayhead = class {
239
256
  /** @type {shaka.util.Timer} */
240
257
  this.checkWindowTimer_ = new shaka.util.Timer(() => {
241
258
  this.onPollWindow_();
242
- }).tickEvery(/* seconds= */ 0.25);
259
+ });
260
+ }
261
+
262
+ /** @override */
263
+ ready() {
264
+ this.checkWindowTimer_.tickEvery(/* seconds= */ 0.25);
243
265
  }
244
266
 
245
267
  /** @override */
@@ -118,6 +118,20 @@ shaka.media.PresentationTimeline = class {
118
118
  }
119
119
 
120
120
 
121
+ /**
122
+ * Sets the presentation's start time.
123
+ *
124
+ * @param {number} presentationStartTime The wall-clock time, in seconds,
125
+ * when the presentation started or will start. Only required for live.
126
+ * @export
127
+ */
128
+ setPresentationStartTime(presentationStartTime) {
129
+ goog.asserts.assert(presentationStartTime >= 0,
130
+ 'presentationStartTime must be >= 0');
131
+ this.presentationStartTime_ = presentationStartTime;
132
+ }
133
+
134
+
121
135
  /**
122
136
  * Sets the presentation's duration.
123
137
  *
@@ -207,10 +221,10 @@ shaka.media.PresentationTimeline = class {
207
221
 
208
222
 
209
223
  /**
210
- * Gives PresentationTimeline a Stream's segments so it can size and position
224
+ * Gives PresentationTimeline an array of segments so it can size and position
211
225
  * the segment availability window, and account for missing segment
212
- * information. This function should be called once for each Stream (no more,
213
- * no less).
226
+ * information. These segments do not necessarily need to all be from the
227
+ * same stream.
214
228
  *
215
229
  * @param {!Array.<!shaka.media.SegmentReference>} references
216
230
  * @export
@@ -220,15 +234,16 @@ shaka.media.PresentationTimeline = class {
220
234
  return;
221
235
  }
222
236
 
223
- const firstReferenceStartTime = references[0].startTime;
224
- const lastReferenceEndTime = references[references.length - 1].endTime;
225
-
237
+ let firstReferenceStartTime = references[0].startTime;
238
+ let lastReferenceEndTime = references[0].endTime;
239
+ for (const reference of references) {
240
+ firstReferenceStartTime = Math.min(
241
+ firstReferenceStartTime, reference.startTime);
242
+ lastReferenceEndTime = Math.max(lastReferenceEndTime, reference.endTime);
243
+ this.maxSegmentDuration_ = Math.max(
244
+ this.maxSegmentDuration_, reference.endTime - reference.startTime);
245
+ }
226
246
  this.notifyMinSegmentStartTime(firstReferenceStartTime);
227
-
228
- this.maxSegmentDuration_ = references.reduce(
229
- (max, r) => { return Math.max(max, r.endTime - r.startTime); },
230
- this.maxSegmentDuration_);
231
-
232
247
  this.maxSegmentEndTime_ =
233
248
  Math.max(this.maxSegmentEndTime_, lastReferenceEndTime);
234
249
 
@@ -28,8 +28,9 @@ shaka.media.InitSegmentReference = class {
28
28
  * segment extends to the end of the resource.
29
29
  * @param {null|shaka.extern.MediaQualityInfo=} mediaQuality Information about
30
30
  * the quality of the media associated with this init segment.
31
+ * @param {number=} timescale
31
32
  */
32
- constructor(uris, startByte, endByte, mediaQuality = null) {
33
+ constructor(uris, startByte, endByte, mediaQuality = null, timescale) {
33
34
  /** @type {function():!Array.<string>} */
34
35
  this.getUris = uris;
35
36
 
@@ -41,6 +42,9 @@ shaka.media.InitSegmentReference = class {
41
42
 
42
43
  /** @const {shaka.extern.MediaQualityInfo|null} */
43
44
  this.mediaQuality = mediaQuality;
45
+
46
+ /** @type {number|undefined} */
47
+ this.timescale = timescale;
44
48
  }
45
49
 
46
50
  /**
@@ -239,6 +243,9 @@ shaka.media.SegmentReference = class {
239
243
 
240
244
  /** @type {?shaka.extern.HlsAes128Key} */
241
245
  this.hlsAes128Key = hlsAes128Key;
246
+
247
+ /** @type {?shaka.media.SegmentReference.ThumbnailSprite} */
248
+ this.thumbnailSprite = null;
242
249
  }
243
250
 
244
251
  /**
@@ -353,6 +360,26 @@ shaka.media.SegmentReference = class {
353
360
  markAsUnavailable() {
354
361
  this.status = shaka.media.SegmentReference.Status.UNAVAILABLE;
355
362
  }
363
+
364
+ /**
365
+ * Set the segment's thumbnail sprite.
366
+ *
367
+ * @param {shaka.media.SegmentReference.ThumbnailSprite} thumbnailSprite
368
+ * @export
369
+ */
370
+ setThumbnailSprite(thumbnailSprite) {
371
+ this.thumbnailSprite = thumbnailSprite;
372
+ }
373
+
374
+ /**
375
+ * Returns the segment's thumbnail sprite.
376
+ *
377
+ * @return {?shaka.media.SegmentReference.ThumbnailSprite}
378
+ * @export
379
+ */
380
+ getThumbnailSprite() {
381
+ return this.thumbnailSprite;
382
+ }
356
383
  };
357
384
 
358
385
 
@@ -376,3 +403,24 @@ shaka.media.SegmentReference.Status = {
376
403
  * @typedef {shaka.media.InitSegmentReference|shaka.media.SegmentReference}
377
404
  */
378
405
  shaka.media.AnySegmentReference;
406
+
407
+
408
+ /**
409
+ * @typedef {{
410
+ * height: number,
411
+ * positionX: number,
412
+ * positionY: number,
413
+ * width: number
414
+ * }}
415
+ *
416
+ * @property {number} height
417
+ * The thumbnail height in px.
418
+ * @property {number} positionX
419
+ * The thumbnail left position in px.
420
+ * @property {number} positionY
421
+ * The thumbnail top position in px.
422
+ * @property {number} width
423
+ * The thumbnail width in px.
424
+ * @export
425
+ */
426
+ shaka.media.SegmentReference.ThumbnailSprite;