shaka-player 4.2.5 → 4.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 (143) hide show
  1. package/.github/workflows/build-and-test.yaml +7 -0
  2. package/.github/workflows/sync-labels.yaml +1 -1
  3. package/.github/workflows/update-issues.yaml +1 -1
  4. package/AUTHORS +4 -0
  5. package/CHANGELOG.md +114 -74
  6. package/CONTRIBUTORS +4 -0
  7. package/README.md +14 -0
  8. package/conditional/dummy_cast_proxy.js +120 -0
  9. package/demo/common/assets.js +58 -0
  10. package/demo/common/message_ids.js +22 -1
  11. package/demo/config.js +89 -5
  12. package/demo/demo.less +7 -1
  13. package/demo/index.html +8 -1
  14. package/demo/locales/en.json +21 -1
  15. package/demo/locales/source.json +82 -2
  16. package/demo/main.js +57 -1
  17. package/demo/search.js +2 -0
  18. package/demo/visualizer.js +337 -0
  19. package/demo/visualizer_button.js +103 -0
  20. package/dist/controls.css +1 -1
  21. package/dist/controls.css.map +1 -1
  22. package/dist/demo.compiled.debug.js +107 -88
  23. package/dist/demo.compiled.debug.map +4 -4
  24. package/dist/demo.compiled.js +107 -88
  25. package/dist/demo.compiled.map +4 -4
  26. package/dist/demo.css +1 -1
  27. package/dist/demo.css.map +1 -1
  28. package/dist/deps.js +21 -13
  29. package/dist/locales.js +31 -31
  30. package/dist/receiver.compiled.debug.js +75 -77
  31. package/dist/receiver.compiled.debug.map +3 -3
  32. package/dist/receiver.compiled.js +75 -77
  33. package/dist/receiver.compiled.map +3 -3
  34. package/dist/shaka-player.compiled.d.ts +181 -27
  35. package/dist/shaka-player.compiled.debug.d.ts +181 -27
  36. package/dist/shaka-player.compiled.debug.externs.js +169 -31
  37. package/dist/shaka-player.compiled.debug.js +510 -450
  38. package/dist/shaka-player.compiled.debug.map +4 -4
  39. package/dist/shaka-player.compiled.externs.js +169 -31
  40. package/dist/shaka-player.compiled.js +912 -874
  41. package/dist/shaka-player.compiled.map +4 -4
  42. package/dist/shaka-player.ui.d.ts +181 -27
  43. package/dist/shaka-player.ui.debug.d.ts +181 -27
  44. package/dist/shaka-player.ui.debug.externs.js +169 -31
  45. package/dist/shaka-player.ui.debug.js +533 -471
  46. package/dist/shaka-player.ui.debug.map +4 -4
  47. package/dist/shaka-player.ui.externs.js +169 -31
  48. package/dist/shaka-player.ui.js +1073 -1036
  49. package/dist/shaka-player.ui.map +4 -4
  50. package/docs/design/lcevc-architecture.png +0 -0
  51. package/docs/design/lcevc-demo.png +0 -0
  52. package/docs/design/lcevc-integration.md +72 -0
  53. package/docs/tutorials/fairplay.md +7 -0
  54. package/docs/tutorials/faq.md +1 -1
  55. package/docs/tutorials/ui.md +7 -7
  56. package/docs/tutorials/upgrade.md +6 -0
  57. package/externs/ima.js +13 -1
  58. package/externs/lcevc.js +81 -0
  59. package/externs/mux.js +1 -42
  60. package/externs/shaka/ads.js +11 -1
  61. package/externs/shaka/manifest_parser.js +5 -1
  62. package/externs/shaka/mp4_parser.js +3 -0
  63. package/externs/shaka/player.js +178 -15
  64. package/lib/abr/simple_abr_manager.js +7 -0
  65. package/lib/ads/ad_manager.js +0 -8
  66. package/lib/ads/client_side_ad.js +15 -0
  67. package/lib/ads/client_side_ad_manager.js +21 -8
  68. package/lib/ads/server_side_ad.js +15 -0
  69. package/lib/ads/server_side_ad_manager.js +3 -3
  70. package/lib/cast/cast_utils.js +2 -0
  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 +108 -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 +808 -384
  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_capabilities.js +36 -0
  81. package/lib/media/media_source_engine.js +113 -28
  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 +142 -12
  85. package/lib/media/transmuxer.js +78 -21
  86. package/lib/offline/storage.js +1 -0
  87. package/lib/player.js +456 -77
  88. package/lib/polyfill/media_capabilities.js +9 -5
  89. package/lib/polyfill/patchedmediakeys_apple.js +63 -4
  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/mime_utils.js +3 -6
  96. package/lib/util/mp4_box_parsers.js +39 -5
  97. package/lib/util/mp4_parser.js +35 -0
  98. package/lib/util/player_configuration.js +23 -1
  99. package/lib/util/stream_utils.js +5 -3
  100. package/lib/util/ts_parser.js +499 -0
  101. package/maintained-branches.md +2 -2
  102. package/package.json +7 -4
  103. package/roadmap.md +5 -3
  104. package/test/ads/ad_manager_unit.js +182 -29
  105. package/test/cast/cast_utils_unit.js +3 -1
  106. package/test/dash/dash_parser_content_protection_unit.js +1 -0
  107. package/test/dash/dash_parser_live_unit.js +1 -0
  108. package/test/dash/dash_parser_manifest_unit.js +93 -1
  109. package/test/dash/dash_parser_segment_base_unit.js +1 -0
  110. package/test/dash/dash_parser_segment_list_unit.js +1 -0
  111. package/test/dash/dash_parser_segment_template_unit.js +1 -0
  112. package/test/demo/demo_unit.js +2 -1
  113. package/test/hls/hls_live_unit.js +253 -19
  114. package/test/hls/hls_parser_unit.js +631 -12
  115. package/test/media/closed_caption_parser_unit.js +2 -1
  116. package/test/media/drm_engine_integration.js +3 -1
  117. package/test/media/media_source_engine_integration.js +71 -6
  118. package/test/media/media_source_engine_unit.js +10 -5
  119. package/test/media/streaming_engine_integration.js +4 -1
  120. package/test/media/streaming_engine_unit.js +144 -1
  121. package/test/media/transmuxer_integration.js +21 -10
  122. package/test/player_integration.js +224 -82
  123. package/test/player_unit.js +5 -2
  124. package/test/polyfill/media_capabilities_unit.js +21 -23
  125. package/test/polyfill/patchedmediakeys_apple_unit.js +130 -0
  126. package/test/test/assets/id3-metadata.aac +0 -0
  127. package/test/test/assets/id3-metadata.ts +0 -0
  128. package/test/test/assets/thumbnails-sprites.vtt +10 -0
  129. package/test/test/assets/thumbnails.vtt +10 -0
  130. package/test/test/util/dash_parser_util.js +2 -0
  131. package/test/test/util/fake_ad.js +21 -0
  132. package/test/test/util/fake_media_source_engine.js +7 -0
  133. package/test/test/util/id3_generator.js +100 -0
  134. package/test/test/util/manifest_generator.js +3 -6
  135. package/test/test/util/simple_fakes.js +4 -0
  136. package/test/test/util/stream_generator.js +38 -0
  137. package/test/test/util/test_scheme.js +43 -14
  138. package/test/text/vtt_text_parser_unit.js +11 -0
  139. package/test/util/id3_utils_unit.js +116 -0
  140. package/test/util/ts_parser_unit.js +77 -0
  141. package/third_party/closure-uri/uri.js +1 -1
  142. package/ui/less/containers.less +7 -0
  143. package/ui/ui.js +43 -4
@@ -172,6 +172,7 @@ shaka.util.Mp4Parser = class {
172
172
 
173
173
  /** @type {shaka.extern.ParsedBox} */
174
174
  const box = {
175
+ name,
175
176
  parser: this,
176
177
  partialOkay: partialOkay || false,
177
178
  version,
@@ -236,6 +237,40 @@ shaka.util.Mp4Parser = class {
236
237
  }
237
238
 
238
239
 
240
+ /**
241
+ * A callback that tells the Mp4 parser to treat the body of a box as a visual
242
+ * sample entry. A visual sample entry has some fixed-sized fields
243
+ * describing the video codec parameters, followed by an arbitrary number of
244
+ * appended children. Each child is a box.
245
+ *
246
+ * @param {!shaka.extern.ParsedBox} box
247
+ * @export
248
+ */
249
+ static visualSampleEntry(box) {
250
+ // The "reader" starts at the payload, so we need to add the header to the
251
+ // start position. The header size varies.
252
+ const headerSize = shaka.util.Mp4Parser.headerSize(box);
253
+
254
+ // Skip 6 reserved bytes.
255
+ // Skip 2-byte data reference index.
256
+ // Skip 16 more reserved bytes.
257
+ // Skip 4 bytes for width/height.
258
+ // Skip 8 bytes for horizontal/vertical resolution.
259
+ // Skip 4 more reserved bytes (0)
260
+ // Skip 2-byte frame count.
261
+ // Skip 32-byte compressor name (length byte, then name, then 0-padding).
262
+ // Skip 2-byte depth.
263
+ // Skip 2 more reserved bytes (0xff)
264
+ // 78 bytes total.
265
+ // See also https://github.com/shaka-project/shaka-packager/blob/d5ca6e84/packager/media/formats/mp4/box_definitions.cc#L1544
266
+ box.reader.skip(78);
267
+
268
+ while (box.reader.hasMoreData() && !box.parser.done_) {
269
+ box.parser.parseNext(box.start + headerSize, box.reader, box.partialOkay);
270
+ }
271
+ }
272
+
273
+
239
274
  /**
240
275
  * Create a callback that tells the Mp4 parser to treat the body of a box as a
241
276
  * binary blob and to parse the body's contents using the provided callback.
@@ -8,6 +8,7 @@ goog.provide('shaka.util.PlayerConfiguration');
8
8
 
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.abr.SimpleAbrManager');
11
+ goog.require('shaka.config.AutoShowText');
11
12
  goog.require('shaka.log');
12
13
  goog.require('shaka.net.NetworkingEngine');
13
14
  goog.require('shaka.util.ConfigUtils');
@@ -127,6 +128,7 @@ shaka.util.PlayerConfiguration = class {
127
128
  [element],
128
129
  element);
129
130
  },
131
+ sequenceMode: false,
130
132
  },
131
133
  hls: {
132
134
  ignoreTextStreamFailures: false,
@@ -136,6 +138,8 @@ shaka.util.PlayerConfiguration = class {
136
138
  ignoreManifestProgramDateTime: false,
137
139
  mediaPlaylistFullMimeType:
138
140
  'video/mp2t; codecs="avc1.42E01E, mp4a.40.2"',
141
+ useSafariBehaviorForLive: true,
142
+ liveSegmentsDelay: 3,
139
143
  },
140
144
  };
141
145
 
@@ -160,7 +164,7 @@ shaka.util.PlayerConfiguration = class {
160
164
  startAtSegmentBoundary: false,
161
165
  gapDetectionThreshold: 0.5,
162
166
  durationBackoff: 1,
163
- forceTransmuxTS: false,
167
+ forceTransmux: false,
164
168
  // Offset by 5 seconds since Chromecast takes a few seconds to start
165
169
  // playing after a seek, even when buffered.
166
170
  safeSeekOffset: 5,
@@ -182,6 +186,7 @@ shaka.util.PlayerConfiguration = class {
182
186
  dispatchAllEmsgBoxes: false,
183
187
  observeQualityChanges: false,
184
188
  maxDisabledTime: 30,
189
+ parsePrftBox: false,
185
190
  };
186
191
 
187
192
  // WebOS, Tizen, and Chromecast have long hardware pipelines that respond
@@ -256,6 +261,7 @@ shaka.util.PlayerConfiguration = class {
256
261
  slowHalfLife: 5,
257
262
  },
258
263
  restrictToElementSize: false,
264
+ restrictToScreenSize: false,
259
265
  ignoreDevicePixelRatio: false,
260
266
  };
261
267
 
@@ -266,14 +272,29 @@ shaka.util.PlayerConfiguration = class {
266
272
  useHeaders: false,
267
273
  };
268
274
 
275
+ const lcevc = {
276
+ enabled: false,
277
+ dynamicPerformanceScaling: true,
278
+ logLevel: 0,
279
+ drawLogo: false,
280
+ };
281
+
282
+ const mediaSource = {
283
+ sourceBufferExtraFeatures: '',
284
+ };
285
+
286
+ const AutoShowText = shaka.config.AutoShowText;
287
+
269
288
  /** @type {shaka.extern.PlayerConfiguration} */
270
289
  const config = {
271
290
  drm: drm,
272
291
  manifest: manifest,
273
292
  streaming: streaming,
293
+ mediaSource: mediaSource,
274
294
  offline: offline,
275
295
  abrFactory: () => new shaka.abr.SimpleAbrManager(),
276
296
  abr: abr,
297
+ autoShowText: AutoShowText.IF_SUBTITLES_MAY_BE_NEEDED,
277
298
  preferredAudioLanguage: '',
278
299
  preferredTextLanguage: '',
279
300
  preferredVariantRole: '',
@@ -299,6 +320,7 @@ shaka.util.PlayerConfiguration = class {
299
320
  playRangeEnd: Infinity,
300
321
  textDisplayFactory: () => null,
301
322
  cmcd: cmcd,
323
+ lcevc: lcevc,
302
324
  };
303
325
 
304
326
  // Add this callback so that we can reference the preferred audio language
@@ -8,6 +8,7 @@ goog.provide('shaka.util.StreamUtils');
8
8
 
9
9
  goog.require('goog.asserts');
10
10
  goog.require('shaka.log');
11
+ goog.require('shaka.media.Capabilities');
11
12
  goog.require('shaka.text.TextEngine');
12
13
  goog.require('shaka.util.Functional');
13
14
  goog.require('shaka.util.LanguageUtils');
@@ -437,6 +438,7 @@ shaka.util.StreamUtils = class {
437
438
  // See: https://github.com/shaka-project/shaka-player/issues/3860
438
439
  const video = variant.video;
439
440
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
441
+ const Capabilities = shaka.media.Capabilities;
440
442
  if (video) {
441
443
  let videoCodecs =
442
444
  shaka.util.StreamUtils.getCorrectVideoCodecs_(video.codecs);
@@ -454,13 +456,13 @@ shaka.util.StreamUtils = class {
454
456
  shaka.util.StreamUtils.getCorrectAudioCodecs_(audioCodecs);
455
457
  const audioFullType = shaka.util.MimeUtils.getFullOrConvertedType(
456
458
  video.mimeType, audioCodecs, ContentType.AUDIO);
457
- if (!MediaSource.isTypeSupported(audioFullType)) {
459
+ if (!Capabilities.isTypeSupported(audioFullType)) {
458
460
  return false;
459
461
  }
460
462
  }
461
463
  const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
462
464
  video.mimeType, videoCodecs, ContentType.VIDEO);
463
- if (!MediaSource.isTypeSupported(fullType)) {
465
+ if (!Capabilities.isTypeSupported(fullType)) {
464
466
  return false;
465
467
  }
466
468
  }
@@ -470,7 +472,7 @@ shaka.util.StreamUtils = class {
470
472
  shaka.util.StreamUtils.getCorrectAudioCodecs_(audio.codecs);
471
473
  const fullType = shaka.util.MimeUtils.getFullOrConvertedType(
472
474
  audio.mimeType, codecs, ContentType.AUDIO);
473
- if (!MediaSource.isTypeSupported(fullType)) {
475
+ if (!Capabilities.isTypeSupported(fullType)) {
474
476
  return false;
475
477
  }
476
478
  }
@@ -0,0 +1,499 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.util.TsParser');
8
+
9
+ goog.require('goog.asserts');
10
+ goog.require('shaka.log');
11
+ goog.require('shaka.util.Id3Utils');
12
+ goog.require('shaka.util.Uint8ArrayUtils');
13
+
14
+
15
+ /**
16
+ * @see https://en.wikipedia.org/wiki/MPEG_transport_stream
17
+ * @export
18
+ */
19
+ shaka.util.TsParser = class {
20
+ /** */
21
+ constructor() {
22
+ /** @private {?number} */
23
+ this.pmtId_ = null;
24
+
25
+ /** @private {boolean} */
26
+ this.pmtParsed_ = false;
27
+
28
+ /** @private {?number} */
29
+ this.videoStartTime_ = null;
30
+
31
+ /** @private {?number} */
32
+ this.videoPid_ = null;
33
+
34
+ /** @private {?string} */
35
+ this.videoCodec_ = null;
36
+
37
+ /** @private {!Array.<Uint8Array>} */
38
+ this.videoData_ = [];
39
+
40
+ /** @private {?number} */
41
+ this.audioStartTime_ = null;
42
+
43
+ /** @private {?number} */
44
+ this.audioPid_ = null;
45
+
46
+ /** @private {?string} */
47
+ this.audioCodec_ = null;
48
+
49
+ /** @private {!Array.<Uint8Array>} */
50
+ this.audioData_ = [];
51
+
52
+ /** @private {?number} */
53
+ this.id3Pid_ = null;
54
+
55
+ /** @private {!Array.<Uint8Array>} */
56
+ this.id3Data_ = [];
57
+ }
58
+
59
+ /**
60
+ * Parse the given data
61
+ *
62
+ * @param {Uint8Array} data
63
+ * @return {!shaka.util.TsParser}
64
+ */
65
+ parse(data) {
66
+ const timescale = shaka.util.TsParser.Timescale_;
67
+ const packetLength = shaka.util.TsParser.PacketLength_;
68
+
69
+ // A TS fragment should contain at least 3 TS packets, a PAT, a PMT, and
70
+ // one PID.
71
+ if (data.length < 3 * packetLength) {
72
+ return this;
73
+ }
74
+ const syncOffset = Math.max(0, shaka.util.TsParser.syncOffset(data));
75
+
76
+ const length = data.length - (data.length + syncOffset) % packetLength;
77
+
78
+ let unknownPIDs = false;
79
+
80
+ // loop through TS packets
81
+ for (let start = syncOffset; start < length; start += packetLength) {
82
+ if (data[start] === 0x47) {
83
+ const payloadUnitStartIndicator = !!(data[start + 1] & 0x40);
84
+ // pid is a 13-bit field starting at the last 5 bits of TS[1]
85
+ const pid = ((data[start + 1] & 0x1f) << 8) + data[start + 2];
86
+ const adaptationFieldControl = (data[start + 3] & 0x30) >> 4;
87
+
88
+ // if an adaption field is present, its length is specified by the
89
+ // fifth byte of the TS packet header.
90
+ let offset;
91
+ if (adaptationFieldControl > 1) {
92
+ offset = start + 5 + data[start + 4];
93
+ // continue if there is only adaptation field
94
+ if (offset === start + packetLength) {
95
+ continue;
96
+ }
97
+ } else {
98
+ offset = start + 4;
99
+ }
100
+ switch (pid) {
101
+ case 0:
102
+ if (payloadUnitStartIndicator) {
103
+ offset += data[offset] + 1;
104
+ }
105
+
106
+ this.pmtId_ = this.getPmtId_(data, offset);
107
+ break;
108
+ case 17:
109
+ case 0x1fff:
110
+ break;
111
+ case this.pmtId_: {
112
+ if (payloadUnitStartIndicator) {
113
+ offset += data[offset] + 1;
114
+ }
115
+
116
+ const parsedPIDs = this.parsePMT(data, offset);
117
+
118
+ // only update track id if track PID found while parsing PMT
119
+ // this is to avoid resetting the PID to -1 in case
120
+ // track PID transiently disappears from the stream
121
+ // this could happen in case of transient missing audio samples
122
+ // for example
123
+ // NOTE this is only the PID of the track as found in TS,
124
+ // but we are not using this for MP4 track IDs.
125
+ if (this.videoPid_ == null) {
126
+ this.videoPid_ = parsedPIDs.video;
127
+ this.videoCodec_ = parsedPIDs.videoCodec;
128
+ }
129
+ if (this.audioPid_ == null) {
130
+ this.audioPid_ = parsedPIDs.audio;
131
+ this.audioCodec_ = parsedPIDs.audioCodec;
132
+ }
133
+ if (this.id3Pid_ == null) {
134
+ this.id3Pid_ = parsedPIDs.id3;
135
+ }
136
+
137
+ if (unknownPIDs && !this.pmtParsed_) {
138
+ shaka.log.debug('reparse from beginning');
139
+ unknownPIDs = false;
140
+ // we set it to -188, the += 188 in the for loop will reset
141
+ // start to 0
142
+ start = syncOffset - packetLength;
143
+ }
144
+ this.pmtParsed_ = true;
145
+ break;
146
+ }
147
+ case this.videoPid_: {
148
+ const videoData = data.subarray(offset, start + packetLength);
149
+ if (this.videoStartTime_ == null) {
150
+ const pes = this.parsePES(videoData);
151
+ if (pes && pes.pts != null) {
152
+ this.videoStartTime_ = pes.pts / timescale;
153
+ }
154
+ }
155
+ this.videoData_.push(videoData);
156
+ break;
157
+ }
158
+ case this.audioPid_: {
159
+ const audioData = data.subarray(offset, start + packetLength);
160
+ if (this.audioStartTime_ == null) {
161
+ const pes = this.parsePES(audioData);
162
+ if (pes && pes.pts != null) {
163
+ this.audioStartTime_ = pes.pts / timescale;
164
+ }
165
+ }
166
+ this.audioData_.push(audioData);
167
+ break;
168
+ }
169
+ case this.id3Pid_:
170
+ this.id3Data_.push(data.subarray(offset, start + packetLength));
171
+ break;
172
+ default:
173
+ unknownPIDs = true;
174
+ break;
175
+ }
176
+ } else {
177
+ shaka.log.warning('Found TS packet that do not start with 0x47');
178
+ }
179
+ }
180
+ return this;
181
+ }
182
+
183
+ /**
184
+ * Get the PMT ID from the PAT
185
+ *
186
+ * @param {Uint8Array} data
187
+ * @param {number} offset
188
+ * @return {number}
189
+ * @private
190
+ */
191
+ getPmtId_(data, offset) {
192
+ // skip the PSI header and parse the first PMT entry
193
+ return ((data[offset + 10] & 0x1f) << 8) | data[offset + 11];
194
+ }
195
+
196
+ /**
197
+ * Parse PMT
198
+ *
199
+ * @param {Uint8Array} data
200
+ * @param {number} offset
201
+ * @return {!shaka.util.TsParser.PMT}
202
+ */
203
+ parsePMT(data, offset) {
204
+ const result = {
205
+ audio: -1,
206
+ video: -1,
207
+ id3: -1,
208
+ audioCodec: '',
209
+ videoCodec: '',
210
+ };
211
+ const sectionLength = ((data[offset + 1] & 0x0f) << 8) | data[offset + 2];
212
+ const tableEnd = offset + 3 + sectionLength - 4;
213
+ // to determine where the table is, we have to figure out how
214
+ // long the program info descriptors are
215
+ const programInfoLength =
216
+ ((data[offset + 10] & 0x0f) << 8) | data[offset + 11];
217
+ // advance the offset to the first entry in the mapping table
218
+ offset += 12 + programInfoLength;
219
+ while (offset < tableEnd) {
220
+ const pid = ((data[offset + 1] & 0x1f) << 8) | data[offset + 2];
221
+ switch (data[offset]) {
222
+ // SAMPLE-AES AAC
223
+ case 0xcf:
224
+ break;
225
+ // ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio)
226
+ case 0x0f:
227
+ if (result.audio === -1) {
228
+ result.audio = pid;
229
+ result.audioCodec = 'aac';
230
+ }
231
+ break;
232
+ // Packetized metadata (ID3)
233
+ case 0x15:
234
+ if (result.id3 === -1) {
235
+ result.id3 = pid;
236
+ }
237
+ break;
238
+ // SAMPLE-AES AVC
239
+ case 0xdb:
240
+ break;
241
+ // ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video)
242
+ case 0x1b:
243
+ if (result.video === -1) {
244
+ result.video = pid;
245
+ result.videoCodec = 'avc';
246
+ }
247
+ break;
248
+ // ISO/IEC 11172-3 (MPEG-1 audio)
249
+ // or ISO/IEC 13818-3 (MPEG-2 halved sample rate audio)
250
+ case 0x03:
251
+ case 0x04:
252
+ if (result.audio === -1) {
253
+ result.audio = pid;
254
+ result.audioCodec = 'mp3';
255
+ }
256
+ break;
257
+ // HEVC
258
+ case 0x24:
259
+ if (result.video === -1) {
260
+ result.video = pid;
261
+ result.videoCodec = 'hvc';
262
+ }
263
+ break;
264
+ default:
265
+ // shaka.log.warning('Unknown stream type:', data[offset]);
266
+ break;
267
+ }
268
+ // move to the next table entry
269
+ // skip past the elementary stream descriptors, if present
270
+ offset += (((data[offset + 3] & 0x0f) << 8) | data[offset + 4]) + 5;
271
+ }
272
+ return result;
273
+ }
274
+
275
+ /**
276
+ * Parse PES
277
+ *
278
+ * @param {Uint8Array} data
279
+ * @return {?shaka.util.TsParser.PES}
280
+ */
281
+ parsePES(data) {
282
+ const startPrefix = (data[0] << 16) | (data[1] << 8) | data[2];
283
+ // In certain live streams, the start of a TS fragment has ts packets
284
+ // that are frame data that is continuing from the previous fragment. This
285
+ // is to check that the pes data is the start of a new pes data
286
+ if (startPrefix !== 1) {
287
+ return null;
288
+ }
289
+ /** @type {shaka.util.TsParser.PES} */
290
+ const pes = {
291
+ data: new Uint8Array(0),
292
+ // get the packet length, this will be 0 for video
293
+ packetLength: 6 + ((data[4] << 8) | data[5]),
294
+ pts: null,
295
+ dts: null,
296
+ };
297
+
298
+ // PES packets may be annotated with a PTS value, or a PTS value
299
+ // and a DTS value. Determine what combination of values is
300
+ // available to work with.
301
+ const ptsDtsFlags = data[7];
302
+
303
+ // PTS and DTS are normally stored as a 33-bit number. Javascript
304
+ // performs all bitwise operations on 32-bit integers but javascript
305
+ // supports a much greater range (52-bits) of integer using standard
306
+ // mathematical operations.
307
+ // We construct a 31-bit value using bitwise operators over the 31
308
+ // most significant bits and then multiply by 4 (equal to a left-shift
309
+ // of 2) before we add the final 2 least significant bits of the
310
+ // timestamp (equal to an OR.)
311
+ if (ptsDtsFlags & 0xC0) {
312
+ // the PTS and DTS are not written out directly. For information
313
+ // on how they are encoded, see
314
+ // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
315
+ pes.pts =
316
+ (data[9] & 0x0e) * 536870912 + // 1 << 29
317
+ (data[10] & 0xff) * 4194304 + // 1 << 22
318
+ (data[11] & 0xfe) * 16384 + // 1 << 14
319
+ (data[12] & 0xff) * 128 + // 1 << 7
320
+ (data[13] & 0xfe) / 2;
321
+
322
+ pes.dts = pes.pts;
323
+ if (ptsDtsFlags & 0x40) {
324
+ pes.dts =
325
+ (data[14] & 0x0e) * 536870912 + // 1 << 29
326
+ (data[15] & 0xff) * 4194304 + // 1 << 22
327
+ (data[16] & 0xfe) * 16384 + // 1 << 14
328
+ (data[17] & 0xff) * 128 + // 1 << 7
329
+ (data[18] & 0xfe) / 2;
330
+ }
331
+ }
332
+ // the data section starts immediately after the PES header.
333
+ // pes_header_data_length specifies the number of header bytes
334
+ // that follow the last byte of the field.
335
+ pes.data = data.subarray(9 + data[8]);
336
+
337
+ return pes;
338
+ }
339
+
340
+ /**
341
+ * Return the ID3 metadata
342
+ *
343
+ * @return {!Array.<shaka.extern.ID3Metadata>}
344
+ */
345
+ getMetadata() {
346
+ const timescale = shaka.util.TsParser.Timescale_;
347
+ const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
348
+ const metadata = [];
349
+ let prevId3Data = new Uint8Array(0);
350
+ // parsePES() only works if the data begins on a PES boundary.
351
+ // Try the last data blob first, and if it doesn't begin on a
352
+ // PES boundary, prepend the previous blob and try again.
353
+ // This way, a successful parse will always begin and end on
354
+ // the correct boundary, and no data will be skipped.
355
+ for (let i = this.id3Data_.length - 1; i >= 0; i--) {
356
+ const data = this.id3Data_[i];
357
+ goog.asserts.assert(data, 'We should have a data');
358
+ const id3Data = Uint8ArrayUtils.concat(data, prevId3Data);
359
+ const pes = this.parsePES(id3Data);
360
+ if (pes) {
361
+ metadata.unshift({
362
+ cueTime: pes.pts ? pes.pts / timescale : null,
363
+ data: pes.data,
364
+ frames: shaka.util.Id3Utils.getID3Frames(pes.data),
365
+ dts: pes.dts,
366
+ pts: pes.pts,
367
+ });
368
+ prevId3Data = new Uint8Array(0);
369
+ } else {
370
+ prevId3Data = id3Data;
371
+ }
372
+ }
373
+ return metadata;
374
+ }
375
+
376
+ /**
377
+ * Return the start time for the audio and video
378
+ *
379
+ * @return {{audio: ?number, video: ?number}}
380
+ */
381
+ getStartTime() {
382
+ return {
383
+ audio: this.audioStartTime_,
384
+ video: this.videoStartTime_,
385
+ };
386
+ }
387
+
388
+ /**
389
+ * Return the audio and video codecs
390
+ *
391
+ * @return {{audio: ?string, video: ?string}}
392
+ */
393
+ getCodecs() {
394
+ return {
395
+ audio: this.audioCodec_,
396
+ video: this.videoCodec_,
397
+ };
398
+ }
399
+
400
+ /**
401
+ * Check if the passed data corresponds to an MPEG2-TS
402
+ *
403
+ * @param {Uint8Array} data
404
+ * @return {boolean}
405
+ */
406
+ static probe(data) {
407
+ const syncOffset = shaka.util.TsParser.syncOffset(data);
408
+ if (syncOffset < 0) {
409
+ return false;
410
+ } else {
411
+ if (syncOffset > 0) {
412
+ shaka.log.warning('MPEG2-TS detected but first sync word found @ ' +
413
+ 'offset ' + syncOffset + ', junk ahead ?');
414
+ }
415
+ return true;
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Returns the synchronization offset
421
+ *
422
+ * @param {Uint8Array} data
423
+ * @return {number}
424
+ */
425
+ static syncOffset(data) {
426
+ const packetLength = shaka.util.TsParser.PacketLength_;
427
+ // scan 1000 first bytes
428
+ const scanwindow = Math.min(1000, data.length - 3 * packetLength);
429
+ let i = 0;
430
+ while (i < scanwindow) {
431
+ // a TS fragment should contain at least 3 TS packets, a PAT, a PMT, and
432
+ // one PID, each starting with 0x47
433
+ if (data[i] === 0x47 &&
434
+ data[i + packetLength] === 0x47 &&
435
+ data[i + 2 * packetLength] === 0x47) {
436
+ return i;
437
+ } else {
438
+ i++;
439
+ }
440
+ }
441
+ return -1;
442
+ }
443
+ };
444
+
445
+
446
+ /**
447
+ * @const {number}
448
+ * @private
449
+ */
450
+ shaka.util.TsParser.PacketLength_ = 188;
451
+
452
+
453
+ /**
454
+ * @const {number}
455
+ * @private
456
+ */
457
+ shaka.util.TsParser.Timescale_ = 90000;
458
+
459
+
460
+ /**
461
+ * @typedef {{
462
+ * audio: number,
463
+ * video: number,
464
+ * id3: number,
465
+ * audioCodec: string,
466
+ * videoCodec: string
467
+ * }}
468
+ *
469
+ * @summary PMT.
470
+ * @property {number} audio
471
+ * Audio PID
472
+ * @property {number} video
473
+ * Video PID
474
+ * @property {number} id3
475
+ * ID3 PID
476
+ * @property {string} audioCodec
477
+ * Audio codec
478
+ * @property {string} videoCodec
479
+ * Video codec
480
+ */
481
+ shaka.util.TsParser.PMT;
482
+
483
+
484
+ /**
485
+ * @typedef {{
486
+ * data: Uint8Array,
487
+ * packetLength: number,
488
+ * pts: ?number,
489
+ * dts: ?number
490
+ * }}
491
+ *
492
+ * @summary PES.
493
+ * @property {Uint8Array} data
494
+ * @property {number} packetLength
495
+ * @property {?number} pts
496
+ * @property {?number} dts
497
+ */
498
+ shaka.util.TsParser.PES;
499
+
@@ -2,8 +2,8 @@
2
2
 
3
3
  We are currently maintaining the following branches of Shaka Player:
4
4
 
5
- - v4.1 (latest)
6
- - v4.0 (previous)
5
+ - v4.3 (latest)
6
+ - v4.2 (previous)
7
7
  - v3.3 (LTS until April 30, 2023)
8
8
  - v3.2 (in use by the Cast Application Framework)
9
9