shaka-player 4.6.5 → 4.6.7
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.
- package/AUTHORS +1 -0
- package/CHANGELOG.md +32 -0
- package/CONTRIBUTORS +1 -0
- package/demo/main.js +11 -1
- package/dist/demo.compiled.debug.js +30 -30
- package/dist/demo.compiled.debug.map +2 -2
- package/dist/demo.compiled.js +30 -30
- package/dist/demo.compiled.map +2 -2
- package/dist/locales.js +31 -31
- package/dist/shaka-player.compiled.debug.js +112 -112
- package/dist/shaka-player.compiled.debug.map +2 -2
- package/dist/shaka-player.compiled.js +708 -706
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.debug.js +69 -70
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.js +834 -834
- package/dist/shaka-player.ui.map +2 -2
- package/docs/tutorials/fairplay.md +2 -2
- package/externs/isoboxer.js +20 -12
- package/externs/shaka/codecs.js +4 -0
- package/lib/ads/client_side_ad.js +9 -0
- package/lib/ads/client_side_ad_manager.js +3 -2
- package/lib/ads/media_tailor_ad_manager.js +3 -2
- package/lib/hls/hls_parser.js +12 -1
- package/lib/media/segment_prefetch.js +2 -2
- package/lib/net/networking_utils.js +3 -1
- package/lib/player.js +1 -1
- package/lib/polyfill/patchedmediakeys_apple.js +6 -0
- package/lib/text/ui_text_displayer.js +7 -0
- package/lib/transmuxer/mss_transmuxer.js +3 -2
- package/lib/transmuxer/ts_transmuxer.js +4 -4
- package/lib/util/mp4_generator.js +14 -10
- package/lib/util/periods.js +5 -0
- package/lib/util/platform.js +20 -1
- package/lib/util/ts_parser.js +15 -16
- package/lib/util/uint8array_utils.js +12 -7
- package/package.json +1 -1
- package/test/text/ui_text_displayer_unit.js +9 -0
- package/test/util/periods_unit.js +49 -0
- package/ui/controls.js +2 -1
|
@@ -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(
|
|
103
|
+
request.body = shaka.util.StringUtils.toUTF8(params);
|
|
104
104
|
});
|
|
105
105
|
|
|
106
106
|
player.getNetworkingEngine().registerResponseFilter((type, response, context) => {
|
package/externs/isoboxer.js
CHANGED
|
@@ -13,27 +13,35 @@
|
|
|
13
13
|
/**
|
|
14
14
|
* @typedef {{
|
|
15
15
|
* Utils: !ISOBoxerUtils,
|
|
16
|
-
* parseBuffer: function(!(ArrayBuffer|ArrayBufferView)):!
|
|
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():!
|
|
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)):!
|
|
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():!
|
|
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.<!
|
|
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:
|
|
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.<!
|
|
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 {
|
|
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
|
package/externs/shaka/codecs.js
CHANGED
|
@@ -207,6 +207,15 @@ shaka.ads.ClientSideAd = class {
|
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
/**
|
|
211
|
+
* It's required for a muted ad to start when autoplaying.
|
|
212
|
+
*
|
|
213
|
+
* @param {number} videoVolume
|
|
214
|
+
*/
|
|
215
|
+
setInitialMuted(videoVolume) {
|
|
216
|
+
this.volume_ = videoVolume;
|
|
217
|
+
this.manager_.setVolume(0);
|
|
218
|
+
}
|
|
210
219
|
|
|
211
220
|
/**
|
|
212
221
|
* @override
|
|
@@ -532,9 +532,10 @@ shaka.ads.ClientSideAdManager = class {
|
|
|
532
532
|
if (this.ad_.isLinear()) {
|
|
533
533
|
this.adContainer_.setAttribute('ad-active', 'true');
|
|
534
534
|
this.video_.pause();
|
|
535
|
-
this.ad_.setVolume(this.video_.volume);
|
|
536
535
|
if (this.video_.muted) {
|
|
537
|
-
this.ad_.
|
|
536
|
+
this.ad_.setInitialMuted(this.video_.volume);
|
|
537
|
+
} else {
|
|
538
|
+
this.ad_.setVolume(this.video_.volume);
|
|
538
539
|
}
|
|
539
540
|
}
|
|
540
541
|
}
|
|
@@ -576,8 +576,9 @@ shaka.ads.MediaTailorAdManager = class {
|
|
|
576
576
|
if (!app.data.source.length) {
|
|
577
577
|
continue;
|
|
578
578
|
}
|
|
579
|
-
const imageElement =
|
|
580
|
-
|
|
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) + '%';
|
package/lib/hls/hls_parser.js
CHANGED
|
@@ -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);
|
|
@@ -3450,6 +3452,13 @@ shaka.hls.HlsParser = class {
|
|
|
3450
3452
|
if (this.isLive_() && mediaSequenceToStartTime.has(position)) {
|
|
3451
3453
|
firstStartTime = mediaSequenceToStartTime.get(position);
|
|
3452
3454
|
}
|
|
3455
|
+
// This is for recovering from disconnects.
|
|
3456
|
+
if (firstStartTime === 0 &&
|
|
3457
|
+
this.presentationType_ == shaka.hls.HlsParser.PresentationType_.LIVE &&
|
|
3458
|
+
mediaSequenceToStartTime.size > 0 &&
|
|
3459
|
+
!mediaSequenceToStartTime.has(position)) {
|
|
3460
|
+
firstStartTime = this.presentationTimeline_.getSegmentAvailabilityStart();
|
|
3461
|
+
}
|
|
3453
3462
|
|
|
3454
3463
|
/** @type {!Array.<!shaka.media.SegmentReference>} */
|
|
3455
3464
|
const references = [];
|
|
@@ -3764,7 +3773,9 @@ shaka.hls.HlsParser = class {
|
|
|
3764
3773
|
|
|
3765
3774
|
contentMimeType = response.headers['content-type'];
|
|
3766
3775
|
} catch (error) {
|
|
3767
|
-
if (error &&
|
|
3776
|
+
if (error &&
|
|
3777
|
+
(error.code == shaka.util.Error.Code.HTTP_ERROR ||
|
|
3778
|
+
error.code == shaka.util.Error.Code.BAD_HTTP_STATUS)) {
|
|
3768
3779
|
headRequest.method = 'GET';
|
|
3769
3780
|
const response = await this.makeNetworkRequest_(
|
|
3770
3781
|
headRequest, requestType, {type});
|
|
@@ -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
|
-
'
|
|
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
|
-
'
|
|
173
|
+
'missed init segment at time, mapSize',
|
|
174
174
|
this.segmentPrefetchMap_.size);
|
|
175
175
|
}
|
|
176
176
|
return null;
|
|
@@ -37,7 +37,9 @@ shaka.net.NetworkingUtils = class {
|
|
|
37
37
|
const response = await netEngine.request(type, request).promise;
|
|
38
38
|
mimeType = response.headers['content-type'];
|
|
39
39
|
} catch (error) {
|
|
40
|
-
if (error &&
|
|
40
|
+
if (error &&
|
|
41
|
+
(error.code == shaka.util.Error.Code.HTTP_ERROR ||
|
|
42
|
+
error.code == shaka.util.Error.Code.BAD_HTTP_STATUS)) {
|
|
41
43
|
request.method = 'GET';
|
|
42
44
|
const response = await netEngine.request(type, request).promise;
|
|
43
45
|
mimeType = response.headers['content-type'];
|
package/lib/player.js
CHANGED
|
@@ -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.
|
|
6782
|
+
shaka.Player.version = 'v4.6.7' + '-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.
|
|
@@ -791,3 +791,9 @@ shaka.polyfill.PatchedMediaKeysApple.MediaKeyStatusMap = class {
|
|
|
791
791
|
goog.asserts.assert(false, 'Not used! Provided only for the compiler.');
|
|
792
792
|
}
|
|
793
793
|
};
|
|
794
|
+
|
|
795
|
+
if (!navigator.requestMediaKeySystemAccess ||
|
|
796
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
797
|
+
!MediaKeySystemAccess.prototype.getConfiguration) {
|
|
798
|
+
shaka.polyfill.register(shaka.polyfill.PatchedMediaKeysApple.install);
|
|
799
|
+
}
|
|
@@ -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
|
-
|
|
317
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
50
|
+
const trakArrays = [];
|
|
51
51
|
for (const streamInfo of this.streamInfos_) {
|
|
52
|
-
|
|
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
|
-
|
|
697
|
+
const trexArrays = [];
|
|
697
698
|
for (const streamInfo of this.streamInfos_) {
|
|
698
|
-
|
|
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
|
-
|
|
735
|
+
const initDatas = [];
|
|
734
736
|
if (!streamInfo.encrypted) {
|
|
735
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
851
|
+
const segmentDataArray = [];
|
|
849
852
|
for (const streamInfo of this.streamInfos_) {
|
|
850
|
-
|
|
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
|
|
package/lib/util/periods.js
CHANGED
|
@@ -1097,6 +1097,11 @@ shaka.util.PeriodCombiner = class {
|
|
|
1097
1097
|
return true;
|
|
1098
1098
|
}
|
|
1099
1099
|
|
|
1100
|
+
// Forced subtitles should be treated as unique streams
|
|
1101
|
+
if (outputStream.forced !== candidate.forced) {
|
|
1102
|
+
return false;
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1100
1105
|
const languageRelatedness = LanguageUtils.relatedness(
|
|
1101
1106
|
outputStream.language, candidate.language);
|
|
1102
1107
|
|
package/lib/util/platform.js
CHANGED
|
@@ -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
|
*
|
package/lib/util/ts_parser.js
CHANGED
|
@@ -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
|
|
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,7 +609,8 @@ shaka.util.TsParser = class {
|
|
|
612
609
|
getAudioData() {
|
|
613
610
|
if (this.audioData_.length && !this.audioPes_.length) {
|
|
614
611
|
let sort = false;
|
|
615
|
-
for (const
|
|
612
|
+
for (const audioDataArray of this.audioData_) {
|
|
613
|
+
const audioData = shaka.util.Uint8ArrayUtils.concat(...audioDataArray);
|
|
616
614
|
const pes = this.parsePES_(audioData);
|
|
617
615
|
let previousPes = this.audioPes_.length ?
|
|
618
616
|
this.audioPes_[this.audioPes_.length - 1] : null;
|
|
@@ -654,7 +652,8 @@ shaka.util.TsParser = class {
|
|
|
654
652
|
getVideoData(naluProcessing = true) {
|
|
655
653
|
if (this.videoData_.length && !this.videoPes_.length) {
|
|
656
654
|
let sort = false;
|
|
657
|
-
for (const
|
|
655
|
+
for (const videoDataArray of this.videoData_) {
|
|
656
|
+
const videoData = shaka.util.Uint8ArrayUtils.concat(...videoDataArray);
|
|
658
657
|
const pes = this.parsePES_(videoData);
|
|
659
658
|
let previousPes = this.videoPes_.length ?
|
|
660
659
|
this.videoPes_[this.videoPes_.length - 1] : null;
|
|
@@ -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 (
|
|
112
|
-
|
|
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
|
-
|
|
118
|
-
|
|
119
|
-
|
|
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(
|
|
125
|
+
result.set(BufferUtils.toUint8(value), offset);
|
|
122
126
|
}
|
|
123
|
-
offset +=
|
|
127
|
+
offset += value.byteLength;
|
|
124
128
|
}
|
|
129
|
+
|
|
125
130
|
return result;
|
|
126
131
|
}
|
|
127
132
|
};
|
package/package.json
CHANGED
|
@@ -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
|
});
|
|
@@ -603,6 +603,55 @@ describe('PeriodCombiner', () => {
|
|
|
603
603
|
}
|
|
604
604
|
});
|
|
605
605
|
|
|
606
|
+
// Regression test for #6054, where we failed on multi-period content with
|
|
607
|
+
// different numbers of forced-subtitle streams per period.
|
|
608
|
+
it('Does not combine subtitle and forced-subtitle tracks', async () => {
|
|
609
|
+
const videoStreams = [makeVideoStream(1280)];
|
|
610
|
+
const audioStreams = [makeAudioStream('en', /* channels= */ 2)];
|
|
611
|
+
const imageStreams = [];
|
|
612
|
+
|
|
613
|
+
const forcedSubtitle = makeTextStream('de');
|
|
614
|
+
forcedSubtitle.roles = ['forced-subtitle'];
|
|
615
|
+
forcedSubtitle.forced = true;
|
|
616
|
+
|
|
617
|
+
const subtitle = makeTextStream('de');
|
|
618
|
+
subtitle.roles = ['subtitle'];
|
|
619
|
+
|
|
620
|
+
/** @type {!Array.<shaka.extern.Period>} */
|
|
621
|
+
const periods = [
|
|
622
|
+
{
|
|
623
|
+
id: '1',
|
|
624
|
+
videoStreams,
|
|
625
|
+
audioStreams,
|
|
626
|
+
textStreams: [
|
|
627
|
+
forcedSubtitle,
|
|
628
|
+
subtitle,
|
|
629
|
+
],
|
|
630
|
+
imageStreams,
|
|
631
|
+
},
|
|
632
|
+
{
|
|
633
|
+
id: '2',
|
|
634
|
+
videoStreams,
|
|
635
|
+
audioStreams,
|
|
636
|
+
textStreams: [
|
|
637
|
+
subtitle,
|
|
638
|
+
],
|
|
639
|
+
imageStreams,
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
id: '3',
|
|
643
|
+
videoStreams,
|
|
644
|
+
audioStreams,
|
|
645
|
+
textStreams: [],
|
|
646
|
+
imageStreams,
|
|
647
|
+
},
|
|
648
|
+
];
|
|
649
|
+
|
|
650
|
+
await combiner.combinePeriods(periods, /* isDynamic= */ true);
|
|
651
|
+
const textStreams = combiner.getTextStreams();
|
|
652
|
+
expect(textStreams.every((s) => s.roles.length === 1)).toBe(true);
|
|
653
|
+
});
|
|
654
|
+
|
|
606
655
|
// Regression test for #3383, where we failed on multi-period content with
|
|
607
656
|
// multiple image streams per period.
|
|
608
657
|
it('Can handle multiple image streams', async () => {
|