shaka-player 4.6.4 → 4.6.6

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.
@@ -98,9 +98,9 @@ player.getNetworkingEngine().registerRequestFilter((type, request, context) => {
98
98
  const originalPayload = new Uint8Array(request.body);
99
99
  const base64Payload =
100
100
  shaka.util.Uint8ArrayUtils.toStandardBase64(originalPayload);
101
- const params = 'spc=' + base64Payload;
101
+ const params = 'spc=' + encodeURIComponent(base64Payload);
102
102
  request.headers['Content-Type'] = 'application/x-www-form-urlencoded';
103
- request.body = shaka.util.StringUtils.toUTF8(encodeURIComponent(params));
103
+ request.body = shaka.util.StringUtils.toUTF8(params);
104
104
  });
105
105
 
106
106
  player.getNetworkingEngine().registerResponseFilter((type, response, context) => {
@@ -13,27 +13,35 @@
13
13
  /**
14
14
  * @typedef {{
15
15
  * Utils: !ISOBoxerUtils,
16
- * parseBuffer: function(!(ArrayBuffer|ArrayBufferView)):!ISOBoxer,
16
+ * parseBuffer: function(!(ArrayBuffer|ArrayBufferView)):!ISOFile,
17
17
  * createBox: function(string, !ISOBoxer, boolean=):!ISOBoxer,
18
18
  * createFullBox: function(string, !ISOBoxer, ?ISOBoxer=):!ISOBoxer,
19
19
  * addBoxProcessor: function(string, function()):!ISOBoxer,
20
- * createFile: function():!ISOBoxer,
21
- * write: function():!ArrayBuffer,
22
- * fetch: function(!ArrayBuffer):!ISOBoxer
20
+ * createFile: function():!ISOFile,
23
21
  * }}
24
22
  * @property {!ISOBoxerUtils} Utils
25
- * @property {function(!(ArrayBuffer|ArrayBufferView)):!ISOBoxer} parseBuffer
23
+ * @property {function(!(ArrayBuffer|ArrayBufferView)):!ISOFile} parseBuffer
26
24
  * @property {function(string, !ISOBoxer, boolean=):!ISOBoxer} createBox
27
25
  * @property {function(string, !ISOBoxer, ?ISOBoxer=):!ISOBoxer} createFullBox
28
26
  * @property {function(string, function()):!ISOBoxer} addBoxProcessor
29
- * @property {function():!ISOBoxer} createFile
30
- * @property {function():!ArrayBuffer} write
31
- * @property {function(!ArrayBuffer):!ISOBoxer} fetch
27
+ * @property {function():!ISOFile} createFile
32
28
  * @const
33
29
  */
34
30
  var ISOBoxer;
35
31
 
36
32
 
33
+ /**
34
+ * @typedef {{
35
+ * write: function():!ArrayBuffer,
36
+ * fetch: function(string):!ISOBox
37
+ * }}
38
+ * @property {function():!ArrayBuffer} write
39
+ * @property {function(string):!ISOBox} fetch
40
+ * @const
41
+ */
42
+ var ISOFile;
43
+
44
+
37
45
  /**
38
46
  * @typedef {{
39
47
  * appendBox: function(!ISOBoxer, !ISOBoxer):!ISOBox
@@ -91,7 +99,7 @@ var ISOBoxerUtils;
91
99
  * graphicsmode: number,
92
100
  * opcolor: Array.<number>,
93
101
  * balance: number,
94
- * entries: Array.<!ISOBox>,
102
+ * entries: Array.<!ISOEntry>,
95
103
  * location: string,
96
104
  * data_reference_index: number,
97
105
  * pre_defined1: number,
@@ -119,7 +127,7 @@ var ISOBoxerUtils;
119
127
  * default_sample_size: number,
120
128
  * default_sample_flags: number,
121
129
  * baseMediaDecodeTime: number,
122
- * usertype: ?string,
130
+ * usertype: (string|null|undefined),
123
131
  * offset: Array.<number>,
124
132
  * sample_info_size: Array.<number>,
125
133
  * data_offset: number
@@ -170,7 +178,7 @@ var ISOBoxerUtils;
170
178
  * @property {number} graphicsmode
171
179
  * @property {Array.<number>} opcolor
172
180
  * @property {number} balance
173
- * @property {Array.<!ISOBox} entries
181
+ * @property {Array.<!ISOEntry>} entries
174
182
  * @property {string} location
175
183
  * @property {number} data_reference_index
176
184
  * @property {number} pre_defined1
@@ -198,7 +206,7 @@ var ISOBoxerUtils;
198
206
  * @property {number} default_sample_size
199
207
  * @property {number} default_sample_flags
200
208
  * @property {number} baseMediaDecodeTime
201
- * @property {?string} usertype
209
+ * @property {string|null|undefined} usertype
202
210
  * @property {Array.<number} offset
203
211
  * @property {Array.<number} sample_info_size
204
212
  * @property {number} data_offset
@@ -4,6 +4,10 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ /**
8
+ * @externs
9
+ */
10
+
7
11
  /**
8
12
  * @typedef {{
9
13
  * data: !Uint8Array,
@@ -576,8 +576,9 @@ shaka.ads.MediaTailorAdManager = class {
576
576
  if (!app.data.source.length) {
577
577
  continue;
578
578
  }
579
- const imageElement = document.createElement('img');
580
- imageElement.setAttribute('src', app.data.source[0].url);
579
+ const imageElement = /** @type {!HTMLImageElement} */ (
580
+ document.createElement('img'));
581
+ imageElement.src = app.data.source[0].url;
581
582
  imageElement.style.top = (app.placeholder.top || 0) + '%';
582
583
  imageElement.style.height = (100 - (app.placeholder.top || 0)) + '%';
583
584
  imageElement.style.left = (app.placeholder.left || 0) + '%';
@@ -295,6 +295,7 @@ shaka.hls.HlsParser = class {
295
295
  this.groupIdToStreamInfosMap_.clear();
296
296
  this.groupIdToCodecsMap_.clear();
297
297
  this.globalVariables_.clear();
298
+ this.mapTagToInitSegmentRefMap_.clear();
298
299
 
299
300
  if (this.contentSteeringManager_) {
300
301
  this.contentSteeringManager_.destroy();
@@ -905,6 +906,7 @@ shaka.hls.HlsParser = class {
905
906
  let initData = null;
906
907
  const initSegmentRef = this.getInitSegmentReference_(
907
908
  playlist, middleSegment.tags, getUris);
909
+ this.mapTagToInitSegmentRefMap_.clear();
908
910
  if (initSegmentRef) {
909
911
  const initSegmentRequest = shaka.net.NetworkingEngine.makeRequest(
910
912
  initSegmentRef.getUris(), this.config_.retryParameters);
@@ -864,7 +864,7 @@ shaka.media.MediaSourceEngine = class {
864
864
  const calculatedTimestampOffset = reference.startTime - timestamp;
865
865
  const timestampOffsetDifference =
866
866
  Math.abs(timestampOffset - calculatedTimestampOffset);
867
- if ((timestampOffsetDifference >= 0.1 || seeked || adaptation) &&
867
+ if ((timestampOffsetDifference >= 0.001 || seeked || adaptation) &&
868
868
  (!isChunkedData || calculatedTimestampOffset > 0 ||
869
869
  !timestampOffset)) {
870
870
  timestampOffset = calculatedTimestampOffset;
@@ -165,12 +165,12 @@ shaka.media.SegmentPrefetch = class {
165
165
  if (reference instanceof shaka.media.SegmentReference) {
166
166
  shaka.log.debug(
167
167
  logPrefix,
168
- 'reused prefetched segment at time:', reference.startTime,
168
+ 'missed segment at time:', reference.startTime,
169
169
  'mapSize', this.segmentPrefetchMap_.size);
170
170
  } else {
171
171
  shaka.log.debug(
172
172
  logPrefix,
173
- 'reused prefetched init segment at time, mapSize',
173
+ 'missed init segment at time, mapSize',
174
174
  this.segmentPrefetchMap_.size);
175
175
  }
176
176
  return null;
@@ -8,6 +8,7 @@ goog.provide('shaka.net.NetworkingUtils');
8
8
 
9
9
  goog.require('goog.Uri');
10
10
  goog.require('shaka.net.NetworkingEngine');
11
+ goog.require('shaka.util.Error');
11
12
 
12
13
 
13
14
  /**
@@ -30,13 +31,21 @@ shaka.net.NetworkingUtils = class {
30
31
  const type = shaka.net.NetworkingEngine.RequestType.MANIFEST;
31
32
 
32
33
  const request = shaka.net.NetworkingEngine.makeRequest([uri], retryParams);
33
- request.method = 'HEAD';
34
34
 
35
- const response = await netEngine.request(type, request).promise;
35
+ try {
36
+ request.method = 'HEAD';
37
+ const response = await netEngine.request(type, request).promise;
38
+ mimeType = response.headers['content-type'];
39
+ } catch (error) {
40
+ if (error && error.code == shaka.util.Error.Code.HTTP_ERROR) {
41
+ request.method = 'GET';
42
+ const response = await netEngine.request(type, request).promise;
43
+ mimeType = response.headers['content-type'];
44
+ }
45
+ }
36
46
 
37
47
  // https://bit.ly/2K9s9kf says this header should always be available,
38
48
  // but just to be safe:
39
- mimeType = response.headers['content-type'];
40
49
  return mimeType ? mimeType.toLowerCase().split(';').shift() : '';
41
50
  }
42
51
 
package/lib/player.js CHANGED
@@ -4525,7 +4525,7 @@ shaka.Player = class extends shaka.util.FakeEventTarget {
4525
4525
 
4526
4526
  if (this.isLive()) {
4527
4527
  const now = this.getPresentationStartTimeAsDate().valueOf() +
4528
- this.seekRange().end * 1000;
4528
+ element.currentTime * 1000;
4529
4529
  const latency = (Date.now() - now) / 1000;
4530
4530
  this.stats_.setLiveLatency(latency);
4531
4531
  }
@@ -6779,7 +6779,7 @@ shaka.Player.TYPICAL_BUFFERING_THRESHOLD_ = 0.5;
6779
6779
  * @export
6780
6780
  */
6781
6781
  // eslint-disable-next-line no-useless-concat
6782
- shaka.Player.version = 'v4.6.4' + '-uncompiled'; // x-release-please-version
6782
+ shaka.Player.version = 'v4.6.6' + '-uncompiled'; // x-release-please-version
6783
6783
 
6784
6784
  // Initialize the deprecation system using the version string we just set
6785
6785
  // on the player.
@@ -154,6 +154,11 @@ shaka.text.UITextDisplayer = class {
154
154
  * @export
155
155
  */
156
156
  destroy() {
157
+ // Return resolved promise if destroy() has been called.
158
+ if (!this.textContainer_) {
159
+ return Promise.resolve();
160
+ }
161
+
157
162
  // Remove the text container element from the UI.
158
163
  this.videoContainer_.removeChild(this.textContainer_);
159
164
  this.textContainer_ = null;
@@ -176,6 +181,8 @@ shaka.text.UITextDisplayer = class {
176
181
  this.resizeObserver_.disconnect();
177
182
  this.resizeObserver_ = null;
178
183
  }
184
+
185
+ return Promise.resolve();
179
186
  }
180
187
 
181
188
 
@@ -253,6 +253,7 @@ shaka.transmuxer.MssTransmuxer = class {
253
253
  */
254
254
  processMediaSegment_(data, stream, reference) {
255
255
  let i;
256
+ /** @type {!ISOFile} */
256
257
  const isoFile = this.isoBoxer_.parseBuffer(data);
257
258
  // Update track_Id in tfhd box
258
259
  const tfhd = isoFile.fetch('tfhd');
@@ -313,8 +314,8 @@ shaka.transmuxer.MssTransmuxer = class {
313
314
  // (subsample_count field size)
314
315
  // 6 = 2 (BytesOfClearData field size) + 4
315
316
  // (BytesOfEncryptedData field size)
316
- saiz.sample_info_size[i] =
317
- 10 + (6 * sepiff.entry[i].NumberOfEntries);
317
+ const entry = /** @type {!ISOEntry} */ (sepiff.entry[i]);
318
+ saiz.sample_info_size[i] = 10 + (6 * entry.NumberOfEntries);
318
319
  }
319
320
  } else {
320
321
  // No sub-sample encryption => set default
@@ -705,7 +705,7 @@ shaka.transmuxer.TsTransmuxer = class {
705
705
  /** @type {?number} */
706
706
  let baseMediaDecodeTime = null;
707
707
 
708
- let nalus = [];
708
+ const nalus = [];
709
709
  const videoData = tsParser.getVideoData();
710
710
  if (!videoData.length) {
711
711
  throw new shaka.util.Error(
@@ -716,7 +716,7 @@ shaka.transmuxer.TsTransmuxer = class {
716
716
  for (let i = 0; i < videoData.length; i++) {
717
717
  const pes = videoData[i];
718
718
  const dataNalus = pes.nalus;
719
- nalus = nalus.concat(dataNalus);
719
+ nalus.push(...dataNalus);
720
720
  const frame = H264.parseFrame(dataNalus);
721
721
  if (!frame) {
722
722
  continue;
@@ -797,7 +797,7 @@ shaka.transmuxer.TsTransmuxer = class {
797
797
  /** @type {?number} */
798
798
  let baseMediaDecodeTime = null;
799
799
 
800
- let nalus = [];
800
+ const nalus = [];
801
801
  const videoData = tsParser.getVideoData();
802
802
  if (!videoData.length) {
803
803
  throw new shaka.util.Error(
@@ -808,7 +808,7 @@ shaka.transmuxer.TsTransmuxer = class {
808
808
  for (let i = 0; i < videoData.length; i++) {
809
809
  const pes = videoData[i];
810
810
  const dataNalus = pes.nalus;
811
- nalus = nalus.concat(dataNalus);
811
+ nalus.push(...dataNalus);
812
812
  const frame = H265.parseFrame(dataNalus);
813
813
  if (!frame) {
814
814
  continue;
@@ -47,10 +47,11 @@ shaka.util.Mp4Generator = class {
47
47
  goog.asserts.assert(this.streamInfos_.length > 0,
48
48
  'StreamInfos must have elements');
49
49
  const Mp4Generator = shaka.util.Mp4Generator;
50
- let traks = new Uint8Array([]);
50
+ const trakArrays = [];
51
51
  for (const streamInfo of this.streamInfos_) {
52
- traks = shaka.util.Uint8ArrayUtils.concat(traks, this.trak_(streamInfo));
52
+ trakArrays.push(this.trak_(streamInfo));
53
53
  }
54
+ const traks = shaka.util.Uint8ArrayUtils.concat(...trakArrays);
54
55
  const firstStreamInfo = this.streamInfos_[0];
55
56
  return Mp4Generator.box('moov',
56
57
  this.mvhd_(firstStreamInfo),
@@ -693,10 +694,11 @@ shaka.util.Mp4Generator = class {
693
694
  */
694
695
  mvex_() {
695
696
  const Mp4Generator = shaka.util.Mp4Generator;
696
- let trexs = new Uint8Array([]);
697
+ const trexArrays = [];
697
698
  for (const streamInfo of this.streamInfos_) {
698
- trexs = shaka.util.Uint8ArrayUtils.concat(trexs, this.trex_(streamInfo));
699
+ trexArrays.push(this.trex_(streamInfo));
699
700
  }
701
+ const trexs = shaka.util.Uint8ArrayUtils.concat(...trexArrays);
700
702
  return Mp4Generator.box('mvex', trexs);
701
703
  }
702
704
 
@@ -730,9 +732,9 @@ shaka.util.Mp4Generator = class {
730
732
  * @private
731
733
  */
732
734
  pssh_(streamInfo) {
733
- let boxes = new Uint8Array([]);
735
+ const initDatas = [];
734
736
  if (!streamInfo.encrypted) {
735
- return boxes;
737
+ return new Uint8Array([]);
736
738
  }
737
739
 
738
740
  for (const drmInfo of streamInfo.stream.drmInfos) {
@@ -740,9 +742,10 @@ shaka.util.Mp4Generator = class {
740
742
  continue;
741
743
  }
742
744
  for (const initData of drmInfo.initData) {
743
- boxes = shaka.util.Uint8ArrayUtils.concat(boxes, initData.initData);
745
+ initDatas.push(initData.initData);
744
746
  }
745
747
  }
748
+ const boxes = shaka.util.Uint8ArrayUtils.concat(...initDatas);
746
749
  return boxes;
747
750
  }
748
751
 
@@ -845,11 +848,12 @@ shaka.util.Mp4Generator = class {
845
848
  * @return {!Uint8Array}
846
849
  */
847
850
  segmentData() {
848
- let result = new Uint8Array([]);
851
+ const segmentDataArray = [];
849
852
  for (const streamInfo of this.streamInfos_) {
850
- result = shaka.util.Uint8ArrayUtils.concat(result,
851
- this.moof_(streamInfo), this.mdat_(streamInfo));
853
+ segmentDataArray.push(
854
+ ...[this.moof_(streamInfo), this.mdat_(streamInfo)]);
852
855
  }
856
+ const result = shaka.util.Uint8ArrayUtils.concat(...segmentDataArray);
853
857
  return result;
854
858
  }
855
859
 
@@ -234,10 +234,12 @@ shaka.util.Platform = class {
234
234
  return !!navigator.vendor && navigator.vendor.includes('Apple') &&
235
235
  !shaka.util.Platform.isTizen() &&
236
236
  !shaka.util.Platform.isEOS() &&
237
+ !shaka.util.Platform.isAPL() &&
237
238
  !shaka.util.Platform.isVirginMedia() &&
238
239
  !shaka.util.Platform.isOrange() &&
239
240
  !shaka.util.Platform.isPS4() &&
240
- !shaka.util.Platform.isAmazonFireTV();
241
+ !shaka.util.Platform.isAmazonFireTV() &&
242
+ !shaka.util.Platform.isWPE();
241
243
  }
242
244
 
243
245
  /**
@@ -292,6 +294,14 @@ shaka.util.Platform = class {
292
294
  return shaka.util.Platform.userAgentContains_('AFT');
293
295
  }
294
296
 
297
+ /**
298
+ * Check if the current platform is Comcast X1.
299
+ * @return {boolean}
300
+ */
301
+ static isWPE() {
302
+ return shaka.util.Platform.userAgentContains_('WPE');
303
+ }
304
+
295
305
  /**
296
306
  * Returns a major version number for Safari, or Safari-based iOS browsers.
297
307
  *
@@ -348,6 +358,15 @@ shaka.util.Platform = class {
348
358
  return shaka.util.Platform.userAgentContains_('PC=EOS');
349
359
  }
350
360
 
361
+ /**
362
+ * Check if the current platform is an APL set-top box.
363
+ *
364
+ * @return {boolean}
365
+ */
366
+ static isAPL() {
367
+ return shaka.util.Platform.userAgentContains_('PC=APL');
368
+ }
369
+
351
370
  /**
352
371
  * Guesses if the platform is a mobile one (iOS or Android).
353
372
  *
@@ -32,7 +32,7 @@ shaka.util.TsParser = class {
32
32
  /** @private {?string} */
33
33
  this.videoCodec_ = null;
34
34
 
35
- /** @private {!Array.<Uint8Array>} */
35
+ /** @private {!Array.<!Array.<Uint8Array>>} */
36
36
  this.videoData_ = [];
37
37
 
38
38
  /** @private {!Array.<shaka.extern.MPEG_PES>} */
@@ -44,7 +44,7 @@ shaka.util.TsParser = class {
44
44
  /** @private {?string} */
45
45
  this.audioCodec_ = null;
46
46
 
47
- /** @private {!Array.<Uint8Array>} */
47
+ /** @private {!Array.<!Array.<Uint8Array>>} */
48
48
  this.audioData_ = [];
49
49
 
50
50
  /** @private {!Array.<shaka.extern.MPEG_PES>} */
@@ -53,7 +53,7 @@ shaka.util.TsParser = class {
53
53
  /** @private {?number} */
54
54
  this.id3Pid_ = null;
55
55
 
56
- /** @private {!Array.<Uint8Array>} */
56
+ /** @private {!Array.<!Array.<Uint8Array>>} */
57
57
  this.id3Data_ = [];
58
58
  }
59
59
 
@@ -79,7 +79,6 @@ shaka.util.TsParser = class {
79
79
  */
80
80
  parse(data) {
81
81
  const packetLength = shaka.util.TsParser.PacketLength_;
82
- const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
83
82
 
84
83
  // A TS fragment should contain at least 3 TS packets, a PAT, a PMT, and
85
84
  // one PID.
@@ -162,12 +161,11 @@ shaka.util.TsParser = class {
162
161
  case this.videoPid_: {
163
162
  const videoData = data.subarray(offset, start + packetLength);
164
163
  if (payloadUnitStartIndicator) {
165
- this.videoData_.push(videoData);
164
+ this.videoData_.push([videoData]);
166
165
  } else if (this.videoData_.length) {
167
166
  const prevVideoData = this.videoData_[this.videoData_.length - 1];
168
167
  if (prevVideoData) {
169
- this.videoData_[this.videoData_.length - 1] =
170
- Uint8ArrayUtils.concat(prevVideoData, videoData);
168
+ this.videoData_[this.videoData_.length - 1].push(videoData);
171
169
  }
172
170
  }
173
171
  break;
@@ -175,12 +173,11 @@ shaka.util.TsParser = class {
175
173
  case this.audioPid_: {
176
174
  const audioData = data.subarray(offset, start + packetLength);
177
175
  if (payloadUnitStartIndicator) {
178
- this.audioData_.push(audioData);
176
+ this.audioData_.push([audioData]);
179
177
  } else if (this.audioData_.length) {
180
178
  const prevAudioData = this.audioData_[this.audioData_.length - 1];
181
179
  if (prevAudioData) {
182
- this.audioData_[this.audioData_.length - 1] =
183
- Uint8ArrayUtils.concat(prevAudioData, audioData);
180
+ this.audioData_[this.audioData_.length - 1].push(audioData);
184
181
  }
185
182
  }
186
183
  break;
@@ -188,12 +185,11 @@ shaka.util.TsParser = class {
188
185
  case this.id3Pid_: {
189
186
  const id3Data = data.subarray(offset, start + packetLength);
190
187
  if (payloadUnitStartIndicator) {
191
- this.id3Data_.push(id3Data);
188
+ this.id3Data_.push([id3Data]);
192
189
  } else if (this.id3Data_.length) {
193
190
  const prevId3Data = this.id3Data_[this.id3Data_.length - 1];
194
191
  if (prevId3Data) {
195
- this.id3Data_[this.id3Data_.length - 1] =
196
- Uint8ArrayUtils.concat(prevId3Data, id3Data);
192
+ this.id3Data_[this.id3Data_.length - 1].push(id3Data);
197
193
  }
198
194
  }
199
195
  break;
@@ -588,7 +584,8 @@ shaka.util.TsParser = class {
588
584
  getMetadata() {
589
585
  const timescale = shaka.util.TsParser.Timescale;
590
586
  const metadata = [];
591
- for (const id3Data of this.id3Data_) {
587
+ for (const id3DataArray of this.id3Data_) {
588
+ const id3Data = shaka.util.Uint8ArrayUtils.concat(...id3DataArray);
592
589
  const pes = this.parsePES_(id3Data);
593
590
  if (pes) {
594
591
  metadata.push({
@@ -612,11 +609,14 @@ shaka.util.TsParser = class {
612
609
  getAudioData() {
613
610
  if (this.audioData_.length && !this.audioPes_.length) {
614
611
  let sort = false;
615
- for (const audioData of this.audioData_) {
612
+ for (const audioDataArray of this.audioData_) {
613
+ const audioData = shaka.util.Uint8ArrayUtils.concat(...audioDataArray);
616
614
  const pes = this.parsePES_(audioData);
617
- if (pes && pes.pts != null && pes.dts != null) {
618
- if (this.audioPes_.length &&
619
- pes.dts < (this.audioPes_[this.audioPes_.length - 1].dts || 0)) {
615
+ let previousPes = this.audioPes_.length ?
616
+ this.audioPes_[this.audioPes_.length - 1] : null;
617
+ if (pes && pes.pts != null && pes.dts != null && (!previousPes ||
618
+ (previousPes.pts != pes.pts && previousPes.dts != pes.dts))) {
619
+ if (this.audioPes_.length && pes.dts < (previousPes.dts || 0)) {
620
620
  sort = true;
621
621
  }
622
622
  this.audioPes_.push(pes);
@@ -625,7 +625,7 @@ shaka.util.TsParser = class {
625
625
  if (!data) {
626
626
  continue;
627
627
  }
628
- const previousPes = this.audioPes_.pop();
628
+ previousPes = this.audioPes_.pop();
629
629
  previousPes.data =
630
630
  shaka.util.Uint8ArrayUtils.concat(previousPes.data, data);
631
631
  this.audioPes_.push(previousPes);
@@ -652,11 +652,14 @@ shaka.util.TsParser = class {
652
652
  getVideoData(naluProcessing = true) {
653
653
  if (this.videoData_.length && !this.videoPes_.length) {
654
654
  let sort = false;
655
- for (const videoData of this.videoData_) {
655
+ for (const videoDataArray of this.videoData_) {
656
+ const videoData = shaka.util.Uint8ArrayUtils.concat(...videoDataArray);
656
657
  const pes = this.parsePES_(videoData);
657
- if (pes && pes.pts != null && pes.dts != null) {
658
- if (this.videoPes_.length &&
659
- pes.dts < (this.videoPes_[this.videoPes_.length - 1].dts || 0)) {
658
+ let previousPes = this.videoPes_.length ?
659
+ this.videoPes_[this.videoPes_.length - 1] : null;
660
+ if (pes && pes.pts != null && pes.dts != null && (!previousPes ||
661
+ (previousPes.pts != pes.pts && previousPes.dts != pes.dts))) {
662
+ if (this.videoPes_.length && pes.dts < (previousPes.dts || 0)) {
660
663
  sort = true;
661
664
  }
662
665
  this.videoPes_.push(pes);
@@ -665,7 +668,7 @@ shaka.util.TsParser = class {
665
668
  if (!data) {
666
669
  continue;
667
670
  }
668
- const previousPes = this.videoPes_.pop();
671
+ previousPes = this.videoPes_.pop();
669
672
  previousPes.data =
670
673
  shaka.util.Uint8ArrayUtils.concat(previousPes.data, data);
671
674
  this.videoPes_.push(previousPes);
@@ -107,21 +107,26 @@ shaka.util.Uint8ArrayUtils = class {
107
107
  * @export
108
108
  */
109
109
  static concat(...varArgs) {
110
+ const BufferUtils = shaka.util.BufferUtils;
110
111
  let totalLength = 0;
111
- for (const arr of varArgs) {
112
- totalLength += arr.byteLength;
112
+ for (let i = 0; i < varArgs.length; ++i) {
113
+ const value = varArgs[i];
114
+ totalLength += value.byteLength;
113
115
  }
114
116
 
115
117
  const result = new Uint8Array(totalLength);
116
118
  let offset = 0;
117
- for (const arr of varArgs) {
118
- if (arr instanceof Uint8Array) {
119
- result.set(arr, offset);
119
+
120
+ for (let i = 0; i < varArgs.length; ++i) {
121
+ const value = varArgs[i];
122
+ if (value instanceof Uint8Array) {
123
+ result.set(value, offset);
120
124
  } else {
121
- result.set(shaka.util.BufferUtils.toUint8(arr), offset);
125
+ result.set(BufferUtils.toUint8(value), offset);
122
126
  }
123
- offset += arr.byteLength;
127
+ offset += value.byteLength;
124
128
  }
129
+
125
130
  return result;
126
131
  }
127
132
  };
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.6.4",
4
+ "version": "4.6.6",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -3484,6 +3484,37 @@ describe('Player', () => {
3484
3484
  expect(tracks[0].id).toBe(2);
3485
3485
  });
3486
3486
 
3487
+ it('removes based on frameRate', async () => {
3488
+ manifest = shaka.test.ManifestGenerator.generate((manifest) => {
3489
+ manifest.addVariant(0, (variant) => {
3490
+ variant.addVideo(1, (stream) => {
3491
+ stream.frameRate = 15;
3492
+ });
3493
+ });
3494
+
3495
+ manifest.addVariant(1, (variant) => {
3496
+ variant.addVideo(2, (stream) => {
3497
+ stream.frameRate = 30;
3498
+ });
3499
+ });
3500
+
3501
+ manifest.addVariant(2, (variant) => {
3502
+ variant.addVideo(3, (stream) => {
3503
+ stream.frameRate = 60;
3504
+ });
3505
+ });
3506
+ });
3507
+
3508
+ await player.load(fakeManifestUri, 0, fakeMimeType);
3509
+ expect(player.getVariantTracks().length).toBe(3);
3510
+
3511
+ player.configure({restrictions: {minFrameRate: 20, maxFrameRate: 40}});
3512
+
3513
+ const tracks = player.getVariantTracks();
3514
+ expect(tracks.length).toBe(1);
3515
+ expect(tracks[0].id).toBe(1);
3516
+ });
3517
+
3487
3518
  it('removes the whole variant if one stream is restricted', async () => {
3488
3519
  manifest = shaka.test.ManifestGenerator.generate((manifest) => {
3489
3520
  manifest.addVariant(0, (variant) => {
@@ -605,4 +605,13 @@ describe('UITextDisplayer', () => {
605
605
  (e) => e.nodeType == Node.ELEMENT_NODE);
606
606
  expect(childrenOfTwo.length).toBe(3);
607
607
  });
608
+
609
+ it('textDisplayer does not crash if destroy is called more than once', () => {
610
+ expect(videoContainer.childNodes.length).toBe(1);
611
+
612
+ textDisplayer.destroy();
613
+ textDisplayer.destroy();
614
+
615
+ expect(videoContainer.childNodes.length).toBe(0);
616
+ });
608
617
  });
package/ui/controls.js CHANGED
@@ -650,7 +650,8 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
650
650
  isPiPAllowed() {
651
651
  if ('documentPictureInPicture' in window &&
652
652
  this.config_.preferDocumentPictureInPicture) {
653
- return true;
653
+ const video = /** @type {HTMLVideoElement} */(this.localVideo_);
654
+ return !video.disablePictureInPicture;
654
655
  }
655
656
  if (document.pictureInPictureEnabled) {
656
657
  const video = /** @type {HTMLVideoElement} */(this.localVideo_);