shaka-player 4.7.11 → 4.7.12

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 (44) hide show
  1. package/README.md +2 -4
  2. package/dist/deps.js +3 -4
  3. package/dist/locales.js +31 -31
  4. package/dist/shaka-player.compiled.d.ts +12 -10
  5. package/dist/shaka-player.compiled.debug.d.ts +12 -10
  6. package/dist/shaka-player.compiled.debug.externs.js +78 -64
  7. package/dist/shaka-player.compiled.debug.js +168 -163
  8. package/dist/shaka-player.compiled.debug.map +4 -4
  9. package/dist/shaka-player.compiled.externs.js +78 -64
  10. package/dist/shaka-player.compiled.js +1134 -1128
  11. package/dist/shaka-player.compiled.map +4 -4
  12. package/dist/shaka-player.ui.d.ts +12 -10
  13. package/dist/shaka-player.ui.debug.d.ts +12 -10
  14. package/dist/shaka-player.ui.debug.externs.js +78 -64
  15. package/dist/shaka-player.ui.debug.js +178 -174
  16. package/dist/shaka-player.ui.debug.map +4 -4
  17. package/dist/shaka-player.ui.externs.js +78 -64
  18. package/dist/shaka-player.ui.js +1264 -1258
  19. package/dist/shaka-player.ui.map +4 -4
  20. package/externs/shaka/player.js +32 -19
  21. package/lib/abr/simple_abr_manager.js +21 -26
  22. package/lib/cast/cast_proxy.js +1 -3
  23. package/lib/cast/cast_sender.js +8 -11
  24. package/lib/cea/cea608_memory.js +1 -1
  25. package/lib/cea/ts_cea_parser.js +10 -1
  26. package/lib/dash/dash_parser.js +3 -0
  27. package/lib/hls/hls_parser.js +18 -16
  28. package/lib/media/drm_engine.js +6 -3
  29. package/lib/media/media_source_engine.js +25 -12
  30. package/lib/media/presentation_timeline.js +23 -0
  31. package/lib/media/segment_utils.js +10 -1
  32. package/lib/player.js +15 -2
  33. package/lib/polyfill/media_capabilities.js +20 -1
  34. package/lib/polyfill/mediasource.js +37 -14
  35. package/lib/text/vtt_text_parser.js +51 -51
  36. package/lib/transmuxer/h264.js +3 -3
  37. package/lib/transmuxer/h265.js +3 -3
  38. package/lib/transmuxer/ts_transmuxer.js +42 -3
  39. package/lib/util/manifest_parser_utils.js +1 -0
  40. package/lib/util/mp4_parser.js +57 -0
  41. package/lib/util/platform.js +4 -0
  42. package/lib/util/ts_parser.js +99 -10
  43. package/package.json +1 -1
  44. package/lib/polyfill/storage_estimate.js +0 -57
@@ -30,14 +30,8 @@ shaka.polyfill.MediaSource = class {
30
30
  // platform detection tricks to decide which patches to install.
31
31
  const safariVersion = shaka.util.Platform.safariVersion();
32
32
 
33
- if (!window.MediaSource) {
33
+ if (!window.MediaSource && !window.ManagedMediaSource) {
34
34
  shaka.log.info('No MSE implementation available.');
35
- } else if (shaka.util.Platform.isChromecast()) {
36
- shaka.log.info('Patching Chromecast MSE bugs.');
37
- // Chromecast fails on some TS content, even though it is supposed to
38
- // support it. Better to transmux.
39
- // See https://github.com/shaka-project/shaka-player/issues/5278
40
- shaka.polyfill.MediaSource.rejectContainer_('mp2t');
41
35
  } else if (safariVersion) {
42
36
  // NOTE: shaka.Player.isBrowserSupported() has its own restrictions on
43
37
  // Safari version.
@@ -73,11 +67,6 @@ shaka.polyfill.MediaSource = class {
73
67
  // Bug filed: https://bugs.webkit.org/show_bug.cgi?id=165342
74
68
  shaka.polyfill.MediaSource.stubAbort_();
75
69
  }
76
- } else if (shaka.util.Platform.isEdge()) {
77
- shaka.log.info('Rejecting TS container.');
78
- // TS content is broken on Edge in general.
79
- // See https://github.com/shaka-project/shaka-player/issues/4955
80
- shaka.polyfill.MediaSource.rejectContainer_('mp2t');
81
70
  } else if (shaka.util.Platform.isTizen2() ||
82
71
  shaka.util.Platform.isTizen3() ||
83
72
  shaka.util.Platform.isTizen4()) {
@@ -90,6 +79,14 @@ shaka.polyfill.MediaSource = class {
90
79
  shaka.log.info('Using native MSE as-is.');
91
80
  }
92
81
 
82
+ if (window.MediaSource || window.ManagedMediaSource) {
83
+ // TS content is broken on all browsers in general.
84
+ // See https://github.com/shaka-project/shaka-player/issues/4955
85
+ // See https://github.com/shaka-project/shaka-player/issues/5278
86
+ // See https://github.com/shaka-project/shaka-player/issues/6334
87
+ shaka.polyfill.MediaSource.rejectContainer_('mp2t');
88
+ }
89
+
93
90
  if (window.MediaSource &&
94
91
  MediaSource.isTypeSupported('video/webm; codecs="vp9"') &&
95
92
  !MediaSource.isTypeSupported('video/webm; codecs="vp09.00.10.08"')) {
@@ -158,12 +155,25 @@ shaka.polyfill.MediaSource = class {
158
155
  * @private
159
156
  */
160
157
  static rejectContainer_(container) {
161
- const isTypeSupported = MediaSource.isTypeSupported;
158
+ const isTypeSupported =
159
+ // eslint-disable-next-line no-restricted-syntax
160
+ MediaSource.isTypeSupported.bind(MediaSource);
162
161
 
163
162
  MediaSource.isTypeSupported = (mimeType) => {
164
163
  const actualContainer = shaka.util.MimeUtils.getContainerType(mimeType);
165
164
  return actualContainer != container && isTypeSupported(mimeType);
166
165
  };
166
+
167
+ if (window.ManagedMediaSource) {
168
+ const isTypeSupportedManaged =
169
+ // eslint-disable-next-line no-restricted-syntax
170
+ ManagedMediaSource.isTypeSupported.bind(ManagedMediaSource);
171
+
172
+ window.ManagedMediaSource.isTypeSupported = (mimeType) => {
173
+ const actualContainer = shaka.util.MimeUtils.getContainerType(mimeType);
174
+ return actualContainer != container && isTypeSupportedManaged(mimeType);
175
+ };
176
+ }
167
177
  }
168
178
 
169
179
  /**
@@ -175,12 +185,25 @@ shaka.polyfill.MediaSource = class {
175
185
  * @private
176
186
  */
177
187
  static rejectCodec_(codec) {
178
- const isTypeSupported = MediaSource.isTypeSupported;
188
+ const isTypeSupported =
189
+ // eslint-disable-next-line no-restricted-syntax
190
+ MediaSource.isTypeSupported.bind(MediaSource);
179
191
 
180
192
  MediaSource.isTypeSupported = (mimeType) => {
181
193
  const actualCodec = shaka.util.MimeUtils.getCodecBase(mimeType);
182
194
  return actualCodec != codec && isTypeSupported(mimeType);
183
195
  };
196
+
197
+ if (window.ManagedMediaSource) {
198
+ const isTypeSupportedManaged =
199
+ // eslint-disable-next-line no-restricted-syntax
200
+ ManagedMediaSource.isTypeSupported.bind(ManagedMediaSource);
201
+
202
+ window.ManagedMediaSource.isTypeSupported = (mimeType) => {
203
+ const actualCodec = shaka.util.MimeUtils.getCodecBase(mimeType);
204
+ return actualCodec != codec && isTypeSupportedManaged(mimeType);
205
+ };
206
+ }
184
207
  }
185
208
 
186
209
  /**
@@ -84,51 +84,16 @@ shaka.text.VttTextParser = class {
84
84
  // HLS content, which should use X-TIMESTAMP-MAP and periodStart instead.
85
85
  let offset = time.vttOffset;
86
86
 
87
- // Only use 'X-TIMESTAMP-MAP' in sequence mode.
88
- // Note that an offset based on the first video
89
- // timestamp has already been extracted, and appears in periodStart.
90
- if (blocks[0].includes('X-TIMESTAMP-MAP') && this.sequenceMode_) {
91
- // https://bit.ly/2K92l7y
92
- // The 'X-TIMESTAMP-MAP' header is used in HLS to align text with
93
- // the rest of the media.
94
- // The header format is 'X-TIMESTAMP-MAP=MPEGTS:n,LOCAL:m'
95
- // (the attributes can go in any order)
96
- // where n is MPEG-2 time and m is cue time it maps to.
97
- // For example 'X-TIMESTAMP-MAP=LOCAL:00:00:00.000,MPEGTS:900000'
98
- // means an offset of 10 seconds
99
- // 900000/MPEG_TIMESCALE - cue time.
100
- const cueTimeMatch =
101
- blocks[0].match(/LOCAL:((?:(\d{1,}):)?(\d{2}):(\d{2})\.(\d{3}))/m);
102
-
103
- const mpegTimeMatch = blocks[0].match(/MPEGTS:(\d+)/m);
104
- if (cueTimeMatch && mpegTimeMatch) {
105
- const parser = new shaka.util.TextParser(cueTimeMatch[1]);
106
- const cueTime = shaka.text.VttTextParser.parseTime_(parser);
107
- if (cueTime == null) {
108
- throw new shaka.util.Error(
109
- shaka.util.Error.Severity.CRITICAL,
110
- shaka.util.Error.Category.TEXT,
111
- shaka.util.Error.Code.INVALID_TEXT_HEADER);
112
- }
113
-
114
- let mpegTime = Number(mpegTimeMatch[1]);
115
- const mpegTimescale = shaka.text.VttTextParser.MPEG_TIMESCALE_;
116
-
117
- const rolloverSeconds =
118
- shaka.text.VttTextParser.TS_ROLLOVER_ / mpegTimescale;
119
- let segmentStart = time.segmentStart - time.periodStart;
120
- while (segmentStart >= rolloverSeconds) {
121
- segmentStart -= rolloverSeconds;
122
- mpegTime += shaka.text.VttTextParser.TS_ROLLOVER_;
123
- }
124
-
125
- offset = time.periodStart + mpegTime / mpegTimescale - cueTime;
126
- }
127
- } else if (blocks[0].includes('X-TIMESTAMP-MAP') &&
87
+ // Only use 'X-TIMESTAMP-MAP' with HLS. This overrides offset above.
88
+ if (blocks[0].includes('X-TIMESTAMP-MAP') &&
128
89
  this.manifestType_ == shaka.media.ManifestParser.HLS) {
129
- // In we use HLS segments mode, with the presence of this tag we need
130
- // calculate the offset from the segment startTime.
131
- offset = time.segmentStart;
90
+ if (this.sequenceMode_) {
91
+ // Compute a different, rollover-based offset for sequence mode.
92
+ offset = this.computeHlsSequenceModeOffset_(blocks[0], time);
93
+ } else {
94
+ // Calculate the offset from the segment startTime.
95
+ offset = time.segmentStart;
96
+ }
132
97
  }
133
98
 
134
99
  // Parse VTT regions.
@@ -159,6 +124,48 @@ shaka.text.VttTextParser = class {
159
124
  return ret;
160
125
  }
161
126
 
127
+ /**
128
+ * @param {string} headerBlock Contains X-TIMESTAMP-MAP.
129
+ * @param {shaka.extern.TextParser.TimeContext} time
130
+ * @return {number}
131
+ * @private
132
+ */
133
+ computeHlsSequenceModeOffset_(headerBlock, time) {
134
+ // https://bit.ly/2K92l7y
135
+ // The 'X-TIMESTAMP-MAP' header is used in HLS to align text with
136
+ // the rest of the media.
137
+ // The header format is 'X-TIMESTAMP-MAP=MPEGTS:n,LOCAL:m'
138
+ // (the attributes can go in any order)
139
+ // where n is MPEG-2 time and m is cue time it maps to.
140
+ // For example 'X-TIMESTAMP-MAP=LOCAL:00:00:00.000,MPEGTS:900000'
141
+ // means an offset of 10 seconds
142
+ // 900000/MPEG_TIMESCALE - cue time.
143
+ const cueTimeMatch = headerBlock.match(
144
+ /LOCAL:((?:(\d{1,}):)?(\d{2}):(\d{2})\.(\d{3}))/m);
145
+ const mpegTimeMatch = headerBlock.match(/MPEGTS:(\d+)/m);
146
+
147
+ if (!cueTimeMatch || !mpegTimeMatch) {
148
+ throw new shaka.util.Error(
149
+ shaka.util.Error.Severity.CRITICAL,
150
+ shaka.util.Error.Category.TEXT,
151
+ shaka.util.Error.Code.INVALID_TEXT_HEADER);
152
+ }
153
+
154
+ const parser = new shaka.util.TextParser(cueTimeMatch[1]);
155
+ const cueTime = shaka.text.VttTextParser.parseTime_(parser);
156
+ if (cueTime == null) {
157
+ throw new shaka.util.Error(
158
+ shaka.util.Error.Severity.CRITICAL,
159
+ shaka.util.Error.Category.TEXT,
160
+ shaka.util.Error.Code.INVALID_TEXT_HEADER);
161
+ }
162
+
163
+ const mpegTime = Number(mpegTimeMatch[1]);
164
+ const mpegTimescale = shaka.text.VttTextParser.MPEG_TIMESCALE_;
165
+
166
+ return time.periodStart + mpegTime / mpegTimescale - cueTime;
167
+ }
168
+
162
169
  /**
163
170
  * Add default color
164
171
  *
@@ -1061,13 +1068,6 @@ shaka.text.VttTextParser = class {
1061
1068
  */
1062
1069
  shaka.text.VttTextParser.MPEG_TIMESCALE_ = 90000;
1063
1070
 
1064
- /**
1065
- * At this value, timestamps roll over in TS content.
1066
- * @const {number}
1067
- * @private
1068
- */
1069
- shaka.text.VttTextParser.TS_ROLLOVER_ = 0x200000000;
1070
-
1071
1071
  /**
1072
1072
  * @const
1073
1073
  * @private {!RegExp}
@@ -144,7 +144,7 @@ shaka.transmuxer.H264 = class {
144
144
  const vSpacingTable = [
145
145
  1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1,
146
146
  ];
147
- if (aspectRatioIdc > 0 && aspectRatioIdc < 16) {
147
+ if (aspectRatioIdc > 0 && aspectRatioIdc <= 16) {
148
148
  hSpacing = hSpacingTable[aspectRatioIdc - 1];
149
149
  vSpacing = vSpacingTable[aspectRatioIdc - 1];
150
150
  } else if (aspectRatioIdc === 255) {
@@ -267,8 +267,8 @@ shaka.transmuxer.H264 = class {
267
267
  naluLength[1] = (size >> 16) & 0xff;
268
268
  naluLength[2] = (size >> 8) & 0xff;
269
269
  naluLength[3] = size & 0xff;
270
- nalusData.push(shaka.util.Uint8ArrayUtils.concat(
271
- naluLength, nalu.fullData));
270
+ nalusData.push(naluLength);
271
+ nalusData.push(nalu.fullData);
272
272
  }
273
273
  }
274
274
  if (!nalusData.length) {
@@ -317,7 +317,7 @@ shaka.transmuxer.H265 = class {
317
317
  const sarHeightTable = [
318
318
  1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1,
319
319
  ];
320
- if (aspectRatioIdc > 0 && aspectRatioIdc < 16) {
320
+ if (aspectRatioIdc > 0 && aspectRatioIdc <= 16) {
321
321
  sarWidth = sarWidthTable[aspectRatioIdc - 1];
322
322
  sarHeight = sarHeightTable[aspectRatioIdc - 1];
323
323
  } else if (aspectRatioIdc === 255) {
@@ -652,8 +652,8 @@ shaka.transmuxer.H265 = class {
652
652
  naluLength[1] = (size >> 16) & 0xff;
653
653
  naluLength[2] = (size >> 8) & 0xff;
654
654
  naluLength[3] = size & 0xff;
655
- nalusData.push(shaka.util.Uint8ArrayUtils.concat(
656
- naluLength, nalu.fullData));
655
+ nalusData.push(naluLength);
656
+ nalusData.push(nalu.fullData);
657
657
  }
658
658
  }
659
659
  if (!nalusData.length) {
@@ -7,6 +7,7 @@
7
7
  goog.provide('shaka.transmuxer.TsTransmuxer');
8
8
 
9
9
  goog.require('shaka.media.Capabilities');
10
+ goog.require('shaka.transmuxer.AacTransmuxer');
10
11
  goog.require('shaka.transmuxer.Ac3');
11
12
  goog.require('shaka.transmuxer.ADTS');
12
13
  goog.require('shaka.transmuxer.Ec3');
@@ -16,6 +17,7 @@ goog.require('shaka.transmuxer.MpegAudio');
16
17
  goog.require('shaka.transmuxer.TransmuxerEngine');
17
18
  goog.require('shaka.util.BufferUtils');
18
19
  goog.require('shaka.util.Error');
20
+ goog.require('shaka.util.Id3Utils');
19
21
  goog.require('shaka.util.ManifestParserUtils');
20
22
  goog.require('shaka.util.MimeUtils');
21
23
  goog.require('shaka.util.Mp4Generator');
@@ -45,6 +47,9 @@ shaka.transmuxer.TsTransmuxer = class {
45
47
 
46
48
  /** @private {?shaka.util.TsParser} */
47
49
  this.tsParser_ = null;
50
+
51
+ /** @private {?shaka.transmuxer.AacTransmuxer} */
52
+ this.aacTransmuxer_ = null;
48
53
  }
49
54
 
50
55
 
@@ -54,6 +59,10 @@ shaka.transmuxer.TsTransmuxer = class {
54
59
  */
55
60
  destroy() {
56
61
  this.initSegments.clear();
62
+
63
+ if (this.aacTransmuxer_) {
64
+ this.aacTransmuxer_.destroy();
65
+ }
57
66
  }
58
67
 
59
68
 
@@ -187,14 +196,44 @@ shaka.transmuxer.TsTransmuxer = class {
187
196
  */
188
197
  transmux(data, stream, reference, duration, contentType) {
189
198
  const ContentType = shaka.util.ManifestParserUtils.ContentType;
199
+ const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
200
+
201
+ const uint8ArrayData = shaka.util.BufferUtils.toUint8(data);
202
+
203
+ if (contentType == ContentType.AUDIO &&
204
+ !shaka.util.TsParser.probe(uint8ArrayData)) {
205
+ const id3Data = shaka.util.Id3Utils.getID3Data(uint8ArrayData);
206
+ let offset = id3Data.length;
207
+ for (; offset < uint8ArrayData.length; offset++) {
208
+ if (shaka.transmuxer.MpegAudio.probe(uint8ArrayData, offset)) {
209
+ return Promise.reject(new shaka.util.Error(
210
+ shaka.util.Error.Severity.CRITICAL,
211
+ shaka.util.Error.Category.MEDIA,
212
+ shaka.util.Error.Code.TRANSMUXING_FAILED));
213
+ }
214
+ }
215
+ offset = id3Data.length;
216
+ for (; offset < uint8ArrayData.length; offset++) {
217
+ if (shaka.transmuxer.ADTS.probe(uint8ArrayData, offset)) {
218
+ if (!this.aacTransmuxer_) {
219
+ this.aacTransmuxer_ =
220
+ new shaka.transmuxer.AacTransmuxer('audio/aac');
221
+ }
222
+ return this.aacTransmuxer_
223
+ .transmux(data, stream, reference, duration, contentType);
224
+ }
225
+ }
226
+ return Promise.reject(new shaka.util.Error(
227
+ shaka.util.Error.Severity.CRITICAL,
228
+ shaka.util.Error.Category.MEDIA,
229
+ shaka.util.Error.Code.TRANSMUXING_FAILED));
230
+ }
231
+
190
232
  if (!this.tsParser_) {
191
233
  this.tsParser_ = new shaka.util.TsParser();
192
234
  } else {
193
235
  this.tsParser_.clearData();
194
236
  }
195
- const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
196
-
197
- const uint8ArrayData = shaka.util.BufferUtils.toUint8(data);
198
237
 
199
238
  const tsParser = this.tsParser_.parse(uint8ArrayData);
200
239
  const streamInfos = [];
@@ -203,6 +203,7 @@ shaka.util.ManifestParserUtils.AUDIO_CODEC_REGEXPS_ = [
203
203
  /^[ae]c-3$/,
204
204
  /^ac-4$/,
205
205
  /^dts[cex]$/, // DTS Digital Surround (dtsc), DTS Express (dtse), DTS:X (dtsx)
206
+ /^iamf/,
206
207
  ];
207
208
 
208
209
 
@@ -271,6 +271,63 @@ shaka.util.Mp4Parser = class {
271
271
  }
272
272
 
273
273
 
274
+ /**
275
+ * A callback that tells the Mp4 parser to treat the body of a box as a audio
276
+ * sample entry. A audio sample entry has some fixed-sized fields
277
+ * describing the audio codec parameters, followed by an arbitrary number of
278
+ * appended children. Each child is a box.
279
+ *
280
+ * @param {!shaka.extern.ParsedBox} box
281
+ * @export
282
+ */
283
+ static audioSampleEntry(box) {
284
+ // The "reader" starts at the payload, so we need to add the header to the
285
+ // start position. The header size varies.
286
+ const headerSize = shaka.util.Mp4Parser.headerSize(box);
287
+
288
+ // 6 bytes reserved
289
+ // 2 bytes data reference index
290
+ box.reader.skip(8);
291
+ // 2 bytes version
292
+ const version = box.reader.readUint16();
293
+ // 2 bytes revision (0, could be ignored)
294
+ // 4 bytes reserved
295
+ box.reader.skip(6);
296
+
297
+ if (version == 2) {
298
+ // 16 bytes hard-coded values with no comments
299
+ // 8 bytes sample rate
300
+ // 4 bytes channel count
301
+ // 4 bytes hard-coded values with no comments
302
+ // 4 bytes bits per sample
303
+ // 4 bytes lpcm flags
304
+ // 4 bytes sample size
305
+ // 4 bytes samples per packet
306
+ box.reader.skip(48);
307
+ } else {
308
+ // 2 bytes channel count
309
+ // 2 bytes bits per sample
310
+ // 2 bytes compression ID
311
+ // 2 bytes packet size
312
+ // 2 bytes sample rate
313
+ // 2 byte reserved
314
+ box.reader.skip(12);
315
+ }
316
+
317
+ if (version == 1) {
318
+ // 4 bytes samples per packet
319
+ // 4 bytes bytes per packet
320
+ // 4 bytes bytes per frame
321
+ // 4 bytes bytes per sample
322
+ box.reader.skip(16);
323
+ }
324
+
325
+ while (box.reader.hasMoreData() && !box.parser.done_) {
326
+ box.parser.parseNext(box.start + headerSize, box.reader, box.partialOkay);
327
+ }
328
+ }
329
+
330
+
274
331
  /**
275
332
  * Create a callback that tells the Mp4 parser to treat the body of a box as a
276
333
  * binary blob and to parse the body's contents using the provided callback.
@@ -535,6 +535,10 @@ shaka.util.Platform = class {
535
535
  Platform.isWebOS4() || Platform.isWebOS5()) {
536
536
  return false;
537
537
  }
538
+ // See: https://chromium-review.googlesource.com/c/chromium/src/+/4577759
539
+ if (Platform.isWindows() && Platform.isEdge()) {
540
+ return false;
541
+ }
538
542
  return true;
539
543
  }
540
544
 
@@ -10,6 +10,7 @@ goog.require('shaka.Deprecate');
10
10
  goog.require('shaka.log');
11
11
  goog.require('shaka.util.ExpGolomb');
12
12
  goog.require('shaka.util.Id3Utils');
13
+ goog.require('shaka.util.StringUtils');
13
14
  goog.require('shaka.util.Uint8ArrayUtils');
14
15
 
15
16
 
@@ -55,6 +56,12 @@ shaka.util.TsParser = class {
55
56
 
56
57
  /** @private {!Array.<!Array.<Uint8Array>>} */
57
58
  this.id3Data_ = [];
59
+
60
+ /** @private {?number} */
61
+ this.referencePts_ = null;
62
+
63
+ /** @private {?number} */
64
+ this.referenceDts_ = null;
58
65
  }
59
66
 
60
67
  /**
@@ -227,6 +234,7 @@ shaka.util.TsParser = class {
227
234
  * @private
228
235
  */
229
236
  parsePMT_(data, offset) {
237
+ const StringUtils = shaka.util.StringUtils;
230
238
  const result = {
231
239
  audio: -1,
232
240
  video: -1,
@@ -249,26 +257,52 @@ shaka.util.TsParser = class {
249
257
  case 0x06:
250
258
  // stream_type 6 can mean a lot of different things in case of DVB.
251
259
  // We need to look at the descriptors. Right now, we're only
252
- // interested in AC-3 and EC-3 audio, so we do the descriptor parsing
253
- // only when we don't have an audio PID yet.
254
- if (result.audio == -1 && esInfoLength > 0) {
260
+ // interested in a few audio and video formats,.
261
+ if (esInfoLength > 0) {
255
262
  let parsePos = offset + 5;
256
263
  let remaining = esInfoLength;
264
+ // Descriptor info: https://www.streamguru.de/mpeg-analyzer/supported-descriptor-list/
257
265
  while (remaining > 2) {
258
266
  const descriptorId = data[parsePos];
267
+ const descriptorLen = data[parsePos + 1] + 2;
259
268
  switch (descriptorId) {
269
+ // Registration descriptor
270
+ case 0x05: {
271
+ const registrationData =
272
+ data.subarray(parsePos + 2, parsePos + descriptorLen);
273
+ const registration =
274
+ StringUtils.fromCharCode(registrationData);
275
+ if (result.audio == -1 && registration === 'Opus') {
276
+ result.audio = pid;
277
+ result.audioCodec = 'opus';
278
+ } else if (result.video == -1 && registration === 'AV01') {
279
+ result.video = pid;
280
+ result.videoCodec = 'av1';
281
+ }
282
+ break;
283
+ }
260
284
  // DVB Descriptor for AC-3
261
285
  case 0x6a:
262
- result.audio = pid;
263
- result.audioCodec = 'ac3';
286
+ if (result.audio == -1) {
287
+ result.audio = pid;
288
+ result.audioCodec = 'ac3';
289
+ }
264
290
  break;
265
291
  // DVB Descriptor for EC-3
266
292
  case 0x7a:
267
- result.audio = pid;
268
- result.audioCodec = 'ec3';
293
+ if (result.audio == -1) {
294
+ result.audio = pid;
295
+ result.audioCodec = 'ec3';
296
+ }
297
+ break;
298
+ // DVB Descriptor for AAC
299
+ case 0x7c:
300
+ if (result.audio == -1) {
301
+ result.audio = pid;
302
+ result.audioCodec = 'aac';
303
+ }
269
304
  break;
270
305
  }
271
- const descriptorLen = data[parsePos + 1] + 2;
272
306
  parsePos += descriptorLen;
273
307
  remaining -= descriptorLen;
274
308
  }
@@ -284,6 +318,13 @@ shaka.util.TsParser = class {
284
318
  result.audioCodec = 'aac';
285
319
  }
286
320
  break;
321
+ // LOAS/LATM AAC
322
+ case 0x11:
323
+ if (result.audio == -1) {
324
+ result.audio = pid;
325
+ result.audioCodec = 'aac-loas';
326
+ }
327
+ break;
287
328
  // Packetized metadata (ID3)
288
329
  case 0x15:
289
330
  if (result.id3 == -1) {
@@ -390,22 +431,36 @@ shaka.util.TsParser = class {
390
431
  // the PTS and DTS are not written out directly. For information
391
432
  // on how they are encoded, see
392
433
  // http://dvd.sourceforge.net/dvdinfo/pes-hdr.html
393
- pes.pts =
434
+ const pts =
394
435
  (data[9] & 0x0e) * 536870912 + // 1 << 29
395
436
  (data[10] & 0xff) * 4194304 + // 1 << 22
396
437
  (data[11] & 0xfe) * 16384 + // 1 << 14
397
438
  (data[12] & 0xff) * 128 + // 1 << 7
398
439
  (data[13] & 0xfe) / 2;
399
440
 
441
+ if (this.referencePts_ == null) {
442
+ this.referencePts_ = pts;
443
+ }
444
+
445
+ pes.pts = this.handleRollover_(pts, this.referencePts_);
446
+ this.referencePts_ = pes.pts;
447
+
400
448
  pes.dts = pes.pts;
401
449
  if (ptsDtsFlags & 0x40) {
402
- pes.dts =
450
+ const dts =
403
451
  (data[14] & 0x0e) * 536870912 + // 1 << 29
404
452
  (data[15] & 0xff) * 4194304 + // 1 << 22
405
453
  (data[16] & 0xfe) * 16384 + // 1 << 14
406
454
  (data[17] & 0xff) * 128 + // 1 << 7
407
455
  (data[18] & 0xfe) / 2;
456
+
457
+ if (this.referenceDts_ == null) {
458
+ this.referenceDts_ = dts;
459
+ }
460
+
461
+ pes.dts = this.handleRollover_(dts, this.referenceDts_);
408
462
  }
463
+ this.referenceDts_ = pes.dts;
409
464
  }
410
465
 
411
466
  const pesHdrLen = data[8];
@@ -1069,6 +1124,40 @@ shaka.util.TsParser = class {
1069
1124
  return ('0' + x.toString(16).toUpperCase()).slice(-2);
1070
1125
  }
1071
1126
 
1127
+ /**
1128
+ * @param {number} value
1129
+ * @param {number} reference
1130
+ * @return {number}
1131
+ * @private
1132
+ */
1133
+ handleRollover_(value, reference) {
1134
+ const MAX_TS = 8589934592;
1135
+ const RO_THRESH = 4294967296;
1136
+
1137
+ let direction = 1;
1138
+
1139
+ if (value > reference) {
1140
+ // If the current timestamp value is greater than our reference timestamp
1141
+ // and we detect a timestamp rollover, this means the roll over is
1142
+ // happening in the opposite direction.
1143
+ // Example scenario: Enter a long stream/video just after a rollover
1144
+ // occurred. The reference point will be set to a small number, e.g. 1.
1145
+ // The user then seeks backwards over the rollover point. In loading this
1146
+ // segment, the timestamp values will be very large, e.g. 2^33 - 1. Since
1147
+ // this comes before the data we loaded previously, we want to adjust the
1148
+ // time stamp to be `value - 2^33`.
1149
+ direction = -1;
1150
+ }
1151
+
1152
+ // Note: A seek forwards or back that is greater than the RO_THRESH
1153
+ // (2^32, ~13 hours) will cause an incorrect adjustment.
1154
+ while (Math.abs(reference - value) > RO_THRESH) {
1155
+ value += (direction * MAX_TS);
1156
+ }
1157
+
1158
+ return value;
1159
+ }
1160
+
1072
1161
  /**
1073
1162
  * Check if the passed data corresponds to an MPEG2-TS
1074
1163
  *
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.7.11",
4
+ "version": "4.7.12",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -1,57 +0,0 @@
1
- /*! @license
2
- * Shaka Player
3
- * Copyright 2016 Google LLC
4
- * SPDX-License-Identifier: Apache-2.0
5
- */
6
-
7
- goog.provide('shaka.polyfill.StorageEstimate');
8
-
9
- goog.require('shaka.polyfill');
10
-
11
-
12
- /**
13
- * @summary A polyfill to provide navigator.storage.estimate in old
14
- * webkit browsers.
15
- * See: https://developers.google.com/web/updates/2017/08/estimating-available-storage-space#the-present
16
- * @export
17
- */
18
- shaka.polyfill.StorageEstimate = class {
19
- /**
20
- * Install the polyfill if needed.
21
- * @export
22
- */
23
- static install() {
24
- if (navigator.storage && navigator.storage.estimate) {
25
- // No need.
26
- return;
27
- }
28
-
29
- if (navigator.webkitTemporaryStorage &&
30
- navigator.webkitTemporaryStorage.queryUsageAndQuota) {
31
- if (!('storage' in navigator)) {
32
- navigator.storage = /** @type {!StorageManager} */ ({});
33
- }
34
- navigator.storage.estimate =
35
- shaka.polyfill.StorageEstimate.storageEstimate_;
36
- }
37
- }
38
-
39
- /**
40
- * @this {StorageManager}
41
- * @return {!Promise}
42
- * @private
43
- */
44
- static storageEstimate_() {
45
- return new Promise((resolve, reject) => {
46
- navigator.webkitTemporaryStorage.queryUsageAndQuota(
47
- (usage, quota) => {
48
- resolve({usage: usage, quota: quota});
49
- },
50
- reject,
51
- );
52
- });
53
- }
54
- };
55
-
56
-
57
- shaka.polyfill.register(shaka.polyfill.StorageEstimate.install);