shaka-player 4.9.8 → 4.9.9

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.
@@ -164,53 +164,62 @@ shaka.cea.Mp4CeaParser = class {
164
164
  /** @type {!Array<!shaka.extern.ICeaParser.CaptionPacket>} **/
165
165
  const captionPackets = [];
166
166
 
167
- // Fields that are found in MOOF boxes
168
- let defaultSampleDuration = this.defaultSampleDuration_;
169
- let defaultSampleSize = this.defaultSampleSize_;
170
167
  let moofOffset = 0;
171
- /** @type {!Array<shaka.util.ParsedTRUNBox>} */
172
- let parsedTRUNs = [];
173
- let baseMediaDecodeTime = null;
174
- let timescale = shaka.cea.CeaUtils.DEFAULT_TIMESCALE_VALUE;
168
+
169
+ /** @type {!Array<!shaka.cea.Mp4CeaParser.ParsedTRAF>} */
170
+ let parsedTRAFs = [];
175
171
 
176
172
  new Mp4Parser()
177
173
  .box('moof', (box) => {
178
174
  moofOffset = box.start;
179
- // trun box parsing is reset on each moof.
180
- parsedTRUNs = [];
175
+ // traf box parsing is reset on each moof.
176
+ parsedTRAFs = [];
177
+ Mp4Parser.children(box);
178
+ })
179
+ .box('traf', (box) => {
180
+ parsedTRAFs.push({
181
+ baseMediaDecodeTime: null,
182
+ defaultSampleDuration: this.defaultSampleDuration_,
183
+ defaultSampleSize: this.defaultSampleSize_,
184
+ parsedTRUNs: [],
185
+ timescale: shaka.cea.CeaUtils.DEFAULT_TIMESCALE_VALUE,
186
+ });
181
187
  Mp4Parser.children(box);
182
188
  })
183
- .box('traf', Mp4Parser.children)
184
189
  .fullBox('trun', (box) => {
185
190
  goog.asserts.assert(
186
- box.version != null && box.flags!=null,
191
+ box.version != null && box.flags != null,
187
192
  'TRUN is a full box and should have a valid version & flags.');
188
193
 
194
+ const lastTRAF = parsedTRAFs[parsedTRAFs.length - 1];
195
+
189
196
  const parsedTRUN = shaka.util.Mp4BoxParsers.parseTRUN(
190
197
  box.reader, box.version, box.flags);
191
- parsedTRUNs.push(parsedTRUN);
198
+ lastTRAF.parsedTRUNs.push(parsedTRUN);
192
199
  })
193
200
  .fullBox('tfhd', (box) => {
194
201
  goog.asserts.assert(
195
202
  box.flags != null,
196
203
  'TFHD is a full box and should have valid flags.');
197
204
 
205
+ const lastTRAF = parsedTRAFs[parsedTRAFs.length - 1];
206
+
198
207
  const parsedTFHD = shaka.util.Mp4BoxParsers.parseTFHD(
199
208
  box.reader, box.flags);
200
209
 
201
210
  // If specified, defaultSampleDuration and defaultSampleSize
202
211
  // override the ones specified in the TREX box
203
- defaultSampleDuration = parsedTFHD.defaultSampleDuration ||
212
+ lastTRAF.defaultSampleDuration = parsedTFHD.defaultSampleDuration ||
204
213
  this.defaultSampleDuration_;
205
214
 
206
- defaultSampleSize = parsedTFHD.defaultSampleSize ||
215
+ lastTRAF.defaultSampleSize = parsedTFHD.defaultSampleSize ||
207
216
  this.defaultSampleSize_;
208
217
 
209
218
  const trackId = parsedTFHD.trackId;
210
219
 
211
220
  // Get the timescale from the track Id
212
221
  if (this.trackIdToTimescale_.has(trackId)) {
213
- timescale = this.trackIdToTimescale_.get(trackId);
222
+ lastTRAF.timescale = this.trackIdToTimescale_.get(trackId);
214
223
  }
215
224
  })
216
225
  .fullBox('tfdt', (box) => {
@@ -218,27 +227,37 @@ shaka.cea.Mp4CeaParser = class {
218
227
  box.version != null,
219
228
  'TFDT is a full box and should have a valid version.');
220
229
 
230
+ const lastTRAF = parsedTRAFs[parsedTRAFs.length - 1];
231
+
221
232
  const parsedTFDT = shaka.util.Mp4BoxParsers.parseTFDTInaccurate(
222
233
  box.reader, box.version);
223
234
 
224
- baseMediaDecodeTime = parsedTFDT.baseMediaDecodeTime;
235
+ lastTRAF.baseMediaDecodeTime = parsedTFDT.baseMediaDecodeTime;
225
236
  })
226
237
  .box('mdat', (box) => {
227
- if (baseMediaDecodeTime === null) {
228
- // This field should have been populated by the Base Media Decode
229
- // Time in the tfdt box.
230
- shaka.log.alwaysWarn(
231
- 'Unable to find base media decode time for CEA captions!');
232
- throw new shaka.util.Error(
233
- shaka.util.Error.Severity.CRITICAL,
234
- shaka.util.Error.Category.TEXT,
235
- shaka.util.Error.Code.INVALID_MP4_CEA);
236
- }
237
-
238
238
  const offset = moofOffset - box.start - 8;
239
- this.parseMdat_(box.reader, baseMediaDecodeTime, timescale,
240
- defaultSampleDuration, defaultSampleSize, offset, parsedTRUNs,
241
- captionPackets);
239
+ const initialPosition = box.reader.getPosition();
240
+ for (const parsedTRAF of parsedTRAFs) {
241
+ if (parsedTRAF.baseMediaDecodeTime === null) {
242
+ // This field should have been populated by the Base Media Decode
243
+ // Time in the tfdt box.
244
+ shaka.log.alwaysWarn(
245
+ 'Unable to find base media decode time for CEA captions!');
246
+ throw new shaka.util.Error(
247
+ shaka.util.Error.Severity.CRITICAL,
248
+ shaka.util.Error.Category.TEXT,
249
+ shaka.util.Error.Code.INVALID_MP4_CEA);
250
+ }
251
+ box.reader.seek(initialPosition);
252
+ this.parseMdat_(box.reader,
253
+ parsedTRAF.baseMediaDecodeTime,
254
+ parsedTRAF.timescale,
255
+ parsedTRAF.defaultSampleDuration,
256
+ parsedTRAF.defaultSampleSize,
257
+ offset,
258
+ parsedTRAF.parsedTRUNs,
259
+ captionPackets);
260
+ }
242
261
  })
243
262
  .parse(mediaSegment, /* partialOkay= */ false);
244
263
 
@@ -315,7 +334,7 @@ shaka.cea.Mp4CeaParser = class {
315
334
  timeOffset = sampleData[sampleIndex].sampleCompositionTimeOffset || 0;
316
335
  }
317
336
 
318
- const pts = (time + timeOffset)/timescale;
337
+ const pts = (time + timeOffset) / timescale;
319
338
  for (const packet of this.seiProcessor_
320
339
  .process(reader.readBytes(naluSize - naluHeaderSize))) {
321
340
  captionPackets.push({
@@ -384,5 +403,22 @@ shaka.cea.Mp4CeaParser.CodecBitstreamMap_ = {
384
403
  'dvhe': shaka.cea.Mp4CeaParser.BitstreamFormat.H265,
385
404
  };
386
405
 
406
+ /**
407
+ * @typedef {{
408
+ * baseMediaDecodeTime: ?number,
409
+ * defaultSampleDuration: number,
410
+ * defaultSampleSize: number,
411
+ * parsedTRUNs: !Array<shaka.util.ParsedTRUNBox>,
412
+ * timescale: number
413
+ * }}
414
+ *
415
+ * @property {?number} baseMediaDecodeTime
416
+ * @property {number} defaultSampleDuration
417
+ * @property {number} defaultSampleSize
418
+ * @property {!Array<shaka.util.ParsedTRUNBox>} parsedTRUNs
419
+ * @property {?number} timescale
420
+ */
421
+ shaka.cea.Mp4CeaParser.ParsedTRAF;
422
+
387
423
  shaka.media.ClosedCaptionParser.registerParser('video/mp4',
388
424
  () => new shaka.cea.Mp4CeaParser());
@@ -2319,24 +2319,32 @@ shaka.hls.HlsParser = class {
2319
2319
  this.config_.hls.disableCodecGuessing) ||
2320
2320
  (this.needsClosedCaptionsDetection_ && type == ContentType.VIDEO &&
2321
2321
  !this.config_.hls.disableClosedCaptionsDetection)) {
2322
- this.needsClosedCaptionsDetection_ = false;
2323
-
2324
- const basicInfo = await this.getMediaPlaylistBasicInfo_(
2325
- playlist, getUris, mediaVariables);
2326
-
2327
- goog.asserts.assert(
2328
- type === basicInfo.type, 'Media types should match!');
2329
-
2330
- if (basicInfo.closedCaptions.size && (!closedCaptions ||
2331
- closedCaptions.size != basicInfo.closedCaptions.size)) {
2332
- closedCaptions = basicInfo.closedCaptions;
2333
- closedCaptionsUpdated = true;
2322
+ let canRequestBasicInfo = playlist.segments.length > 0;
2323
+ if (canRequestBasicInfo) {
2324
+ const segment = playlist.segments[0];
2325
+ if (shaka.hls.Utils.getFirstTagWithName(segment.tags, 'EXT-X-GAP')) {
2326
+ canRequestBasicInfo = false;
2327
+ }
2334
2328
  }
2329
+ if (canRequestBasicInfo) {
2330
+ this.needsClosedCaptionsDetection_ = false;
2331
+ const basicInfo = await this.getMediaPlaylistBasicInfo_(
2332
+ playlist, getUris, mediaVariables);
2333
+
2334
+ goog.asserts.assert(
2335
+ type === basicInfo.type, 'Media types should match!');
2336
+
2337
+ if (basicInfo.closedCaptions.size && (!closedCaptions ||
2338
+ closedCaptions.size != basicInfo.closedCaptions.size)) {
2339
+ closedCaptions = basicInfo.closedCaptions;
2340
+ closedCaptionsUpdated = true;
2341
+ }
2335
2342
 
2336
- if (!this.codecInfoInManifest_ &&
2337
- this.config_.hls.disableCodecGuessing) {
2338
- mimeType = basicInfo.mimeType;
2339
- codecs = basicInfo.codecs;
2343
+ if (!this.codecInfoInManifest_ &&
2344
+ this.config_.hls.disableCodecGuessing) {
2345
+ mimeType = basicInfo.mimeType;
2346
+ codecs = basicInfo.codecs;
2347
+ }
2340
2348
  }
2341
2349
  }
2342
2350
 
@@ -3944,9 +3952,14 @@ shaka.hls.HlsParser = class {
3944
3952
  'Playlist should have segments!');
3945
3953
  const middleSegmentIdx = Math.trunc((playlist.segments.length - 1) / 2);
3946
3954
 
3955
+ const middleSegment = playlist.segments[middleSegmentIdx];
3956
+ if (shaka.hls.Utils.getFirstTagWithName(middleSegment.tags, 'EXT-X-GAP')) {
3957
+ return this.guessMimeTypeFallback_(contentType);
3958
+ }
3959
+
3947
3960
  const middleSegmentUris = shaka.hls.Utils.constructSegmentUris(
3948
3961
  getUris(),
3949
- playlist.segments[middleSegmentIdx].verbatimSegmentUri,
3962
+ middleSegment.verbatimSegmentUri,
3950
3963
  variables);
3951
3964
 
3952
3965
  const parsedUri = new goog.Uri(middleSegmentUris[0]);
@@ -312,4 +312,5 @@ shaka.hls.ManifestTextParser.SEGMENT_TAGS = [
312
312
  'EXT-X-KEY',
313
313
  'EXT-X-DATERANGE',
314
314
  'EXT-X-MAP',
315
+ 'EXT-X-GAP',
315
316
  ];
@@ -140,7 +140,7 @@ shaka.media.SegmentUtils = class {
140
140
  const SegmentUtils = shaka.media.SegmentUtils;
141
141
 
142
142
  const audioCodecs = [];
143
- const videoCodecs = [];
143
+ let videoCodecs = [];
144
144
 
145
145
  let hasAudio = false;
146
146
  let hasVideo = false;
@@ -360,6 +360,13 @@ shaka.media.SegmentUtils = class {
360
360
  })
361
361
 
362
362
  .box('colr', (box) => {
363
+ videoCodecs = videoCodecs.map((codec) => {
364
+ if (codec.startsWith('av01.')) {
365
+ return shaka.util.Mp4BoxParsers.updateAV1CodecWithCOLRBox(
366
+ codec, box.reader);
367
+ }
368
+ return codec;
369
+ });
363
370
  const {videoRange, colorGamut} =
364
371
  shaka.util.Mp4BoxParsers.parseCOLR(box.reader);
365
372
  realVideoRange = videoRange;
@@ -2691,7 +2691,10 @@ shaka.media.StreamingEngine = class {
2691
2691
  }
2692
2692
 
2693
2693
  // First fire an error event.
2694
- this.playerInterface_.onError(error);
2694
+ if (!error.handled ||
2695
+ error.code != shaka.util.Error.Code.SEGMENT_MISSING) {
2696
+ this.playerInterface_.onError(error);
2697
+ }
2695
2698
 
2696
2699
  // If the error was not handled by the application, call the failure
2697
2700
  // callback.
package/lib/player.js CHANGED
@@ -7459,7 +7459,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
7459
7459
  * @export
7460
7460
  */
7461
7461
  // eslint-disable-next-line no-useless-concat
7462
- shaka.Player.version = 'v4.9.8' + '-uncompiled'; // x-release-please-version
7462
+ shaka.Player.version = 'v4.9.9' + '-uncompiled'; // x-release-please-version
7463
7463
 
7464
7464
  // Initialize the deprecation system using the version string we just set
7465
7465
  // on the player.
@@ -604,6 +604,42 @@ shaka.util.Mp4BoxParsers = class {
604
604
  };
605
605
  }
606
606
 
607
+ /**
608
+ * Parses AV1 codec string with COLR box information.
609
+ *
610
+ * AV1 codec info: https://aomediacodec.github.io/av1-isobmff/#codecsparam
611
+ *
612
+ * @param {string} codec
613
+ * @param {!shaka.util.DataViewReader} reader
614
+ * @return {string}
615
+ */
616
+ static updateAV1CodecWithCOLRBox(codec, reader) {
617
+ const Mp4BoxParsers = shaka.util.Mp4BoxParsers;
618
+ const initialPosition = reader.getPosition();
619
+ const data = reader.readBytes(4);
620
+ let colorType = '';
621
+ colorType += String.fromCharCode(data[0]);
622
+ colorType += String.fromCharCode(data[1]);
623
+ colorType += String.fromCharCode(data[2]);
624
+ colorType += String.fromCharCode(data[3]);
625
+ if (colorType === 'nclx') {
626
+ const colorPrimaries = reader.readUint16();
627
+ const transferCharacteristics = reader.readUint16();
628
+ const matrixCoefficients = reader.readUint16();
629
+ const videoFullRangeFlag = reader.readUint8() >> 7;
630
+ const codecParts = codec.split('.');
631
+ if (codecParts.length == 10) {
632
+ codecParts[6] = Mp4BoxParsers.addLeadingZero_(colorPrimaries);
633
+ codecParts[7] = Mp4BoxParsers.addLeadingZero_(transferCharacteristics);
634
+ codecParts[8] = Mp4BoxParsers.addLeadingZero_(matrixCoefficients);
635
+ codecParts[9] = String(videoFullRangeFlag);
636
+ codec = codecParts.join('.');
637
+ }
638
+ }
639
+ reader.seek(initialPosition);
640
+ return codec;
641
+ }
642
+
607
643
  /**
608
644
  * Convert a number to hex
609
645
  * @param {number} x
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.9.8",
4
+ "version": "4.9.9",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [