shaka-player 4.12.4 → 4.12.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.
- package/dist/deps.js +2 -2
- package/dist/locales.js +35 -35
- package/dist/shaka-player.compiled.d.ts +11 -3
- package/dist/shaka-player.compiled.debug.d.ts +11 -3
- package/dist/shaka-player.compiled.debug.externs.js +8 -5
- package/dist/shaka-player.compiled.debug.js +340 -331
- package/dist/shaka-player.compiled.debug.map +3 -3
- package/dist/shaka-player.compiled.externs.js +8 -5
- package/dist/shaka-player.compiled.js +1383 -1378
- package/dist/shaka-player.compiled.map +4 -4
- package/dist/shaka-player.ui.d.ts +11 -3
- package/dist/shaka-player.ui.debug.d.ts +11 -3
- package/dist/shaka-player.ui.debug.externs.js +8 -5
- package/dist/shaka-player.ui.debug.js +357 -348
- package/dist/shaka-player.ui.debug.map +3 -3
- package/dist/shaka-player.ui.externs.js +8 -5
- package/dist/shaka-player.ui.js +1605 -1599
- package/dist/shaka-player.ui.map +3 -3
- package/externs/shaka/codecs.js +21 -0
- package/externs/shaka/offline.js +4 -0
- package/lib/cast/cast_utils.js +1 -0
- package/lib/dash/dash_parser.js +3 -0
- package/lib/hls/hls_parser.js +26 -5
- package/lib/media/drm_engine.js +70 -29
- package/lib/media/manifest_filterer.js +2 -1
- package/lib/media/media_source_engine.js +138 -129
- package/lib/media/segment_utils.js +50 -44
- package/lib/media/streaming_engine.js +9 -1
- package/lib/offline/download_info.js +5 -0
- package/lib/offline/download_manager.js +54 -0
- package/lib/offline/manifest_converter.js +1 -0
- package/lib/offline/storage.js +12 -3
- package/lib/player.js +52 -29
- package/lib/polyfill/media_capabilities.js +1 -1
- package/lib/text/simple_text_displayer.js +9 -1
- package/lib/text/ui_text_displayer.js +2 -14
- package/lib/transmuxer/aac_transmuxer.js +11 -3
- package/lib/transmuxer/ac3_transmuxer.js +11 -3
- package/lib/transmuxer/ec3_transmuxer.js +11 -3
- package/lib/transmuxer/h264.js +143 -70
- package/lib/transmuxer/mp3_transmuxer.js +11 -3
- package/lib/transmuxer/ts_transmuxer.js +68 -28
- package/lib/util/drm_utils.js +20 -0
- package/lib/util/mime_utils.js +3 -0
- package/lib/util/object_utils.js +83 -0
- package/lib/util/platform.js +48 -40
- package/lib/util/stream_utils.js +37 -113
- package/lib/util/ts_parser.js +111 -85
- package/package.json +1 -1
- package/ui/ad_counter.js +5 -0
- package/ui/controls.js +4 -2
- package/ui/fast_forward_button.js +7 -0
- package/ui/play_button.js +1 -1
- package/ui/playback_rate_selection.js +1 -1
- package/ui/rewind_button.js +7 -0
package/lib/transmuxer/h264.js
CHANGED
|
@@ -196,71 +196,28 @@ shaka.transmuxer.H264 = class {
|
|
|
196
196
|
}
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
|
-
* @param {!Array.<shaka.extern.
|
|
200
|
-
* @return {
|
|
199
|
+
* @param {!Array.<shaka.extern.MPEG_PES>} videoData
|
|
200
|
+
* @return {!Array.<shaka.extern.VideoSample>}
|
|
201
201
|
*/
|
|
202
|
-
static
|
|
202
|
+
static getVideoSamples(videoData) {
|
|
203
203
|
const H264 = shaka.transmuxer.H264;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
push = true;
|
|
216
|
-
const data = nalu.data;
|
|
217
|
-
// Only check slice type to detect KF in case SPS found in same packet
|
|
218
|
-
// (any keyframe is preceded by SPS ...)
|
|
219
|
-
if (spsNalu && data.length > 4) {
|
|
220
|
-
// retrieve slice type by parsing beginning of NAL unit (follow
|
|
221
|
-
// H264 spec,slice_header definition) to detect keyframe embedded
|
|
222
|
-
// in NDR
|
|
223
|
-
const sliceType = new shaka.util.ExpGolomb(data).readSliceType();
|
|
224
|
-
// 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
|
|
225
|
-
// SI slice : A slice that is coded using intra prediction only and
|
|
226
|
-
// using quantisation of the prediction samples.
|
|
227
|
-
// An SI slice can be coded such that its decoded samples can be
|
|
228
|
-
// constructed identically to an SP slice.
|
|
229
|
-
// I slice: A slice that is not an SI slice that is decoded using
|
|
230
|
-
// intra prediction only.
|
|
231
|
-
if (sliceType === 2 || sliceType === 4 ||
|
|
232
|
-
sliceType === 7 || sliceType === 9) {
|
|
233
|
-
isKeyframe = true;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
break;
|
|
237
|
-
}
|
|
238
|
-
case H264.NALU_TYPE_IDR_:
|
|
239
|
-
avcSample = true;
|
|
240
|
-
push = true;
|
|
241
|
-
isKeyframe = true;
|
|
242
|
-
break;
|
|
243
|
-
case H264.NALU_TYPE_SEI_:
|
|
244
|
-
push = true;
|
|
245
|
-
break;
|
|
246
|
-
case H264.NALU_TYPE_SPS_:
|
|
247
|
-
push = true;
|
|
248
|
-
break;
|
|
249
|
-
case H264.NALU_TYPE_PPS_:
|
|
250
|
-
push = true;
|
|
251
|
-
break;
|
|
252
|
-
case H264.NALU_TYPE_AUD_:
|
|
253
|
-
push = true;
|
|
254
|
-
avcSample = true;
|
|
255
|
-
break;
|
|
256
|
-
case H264.NALU_TYPE_FILLER_DATA_:
|
|
257
|
-
push = true;
|
|
258
|
-
break;
|
|
259
|
-
default:
|
|
260
|
-
push = false;
|
|
261
|
-
break;
|
|
204
|
+
|
|
205
|
+
/** @type {!Array.<shaka.extern.VideoSample>} */
|
|
206
|
+
const videoSamples = [];
|
|
207
|
+
/** @type {?shaka.extern.VideoSample} */
|
|
208
|
+
let lastVideoSample = null;
|
|
209
|
+
/** @type {boolean} */
|
|
210
|
+
let audFound = false;
|
|
211
|
+
|
|
212
|
+
const addLastVideoSample = () => {
|
|
213
|
+
if (!lastVideoSample) {
|
|
214
|
+
return;
|
|
262
215
|
}
|
|
263
|
-
if (
|
|
216
|
+
if (!lastVideoSample.nalus.length || !lastVideoSample.frame) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const nalusData = [];
|
|
220
|
+
for (const nalu of lastVideoSample.nalus) {
|
|
264
221
|
const size = nalu.fullData.byteLength;
|
|
265
222
|
const naluLength = new Uint8Array(4);
|
|
266
223
|
naluLength[0] = (size >> 24) & 0xff;
|
|
@@ -270,15 +227,131 @@ shaka.transmuxer.H264 = class {
|
|
|
270
227
|
nalusData.push(naluLength);
|
|
271
228
|
nalusData.push(nalu.fullData);
|
|
272
229
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
230
|
+
lastVideoSample.data = shaka.util.Uint8ArrayUtils.concat(...nalusData);
|
|
231
|
+
videoSamples.push(lastVideoSample);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const createLastVideoSample = (pes) => {
|
|
235
|
+
lastVideoSample = {
|
|
236
|
+
data: new Uint8Array([]),
|
|
237
|
+
frame: false,
|
|
238
|
+
isKeyframe: false,
|
|
239
|
+
pts: pes.pts,
|
|
240
|
+
dts: pes.dts,
|
|
241
|
+
nalus: [],
|
|
242
|
+
};
|
|
281
243
|
};
|
|
244
|
+
|
|
245
|
+
for (let i = 0; i < videoData.length; i++) {
|
|
246
|
+
const pes = videoData[i];
|
|
247
|
+
const nalus = pes.nalus;
|
|
248
|
+
let spsFound = false;
|
|
249
|
+
|
|
250
|
+
// If new NAL units found and last sample still there, let's push ...
|
|
251
|
+
// This helps parsing streams with missing AUD
|
|
252
|
+
// (only do this if AUD never found)
|
|
253
|
+
if (lastVideoSample && nalus.length && !audFound) {
|
|
254
|
+
addLastVideoSample();
|
|
255
|
+
createLastVideoSample(pes);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
for (const nalu of pes.nalus) {
|
|
259
|
+
let push = false;
|
|
260
|
+
switch (nalu.type) {
|
|
261
|
+
case H264.NALU_TYPE_NDR_: {
|
|
262
|
+
let isKeyframe = false;
|
|
263
|
+
push = true;
|
|
264
|
+
const data = nalu.data;
|
|
265
|
+
// Only check slice type to detect KF in case SPS found in same
|
|
266
|
+
// packet (any keyframe is preceded by SPS ...)
|
|
267
|
+
if (spsFound && data.length > 4) {
|
|
268
|
+
// retrieve slice type by parsing beginning of NAL unit (follow
|
|
269
|
+
// H264 spec,slice_header definition) to detect keyframe embedded
|
|
270
|
+
// in NDR
|
|
271
|
+
const sliceType = new shaka.util.ExpGolomb(data).readSliceType();
|
|
272
|
+
// 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice
|
|
273
|
+
// SI slice : A slice that is coded using intra prediction only
|
|
274
|
+
// and using quantisation of the prediction samples.
|
|
275
|
+
// An SI slice can be coded such that its decoded samples can be
|
|
276
|
+
// constructed identically to an SP slice.
|
|
277
|
+
// I slice: A slice that is not an SI slice that is decoded using
|
|
278
|
+
// intra prediction only.
|
|
279
|
+
if (sliceType === 2 || sliceType === 4 ||
|
|
280
|
+
sliceType === 7 || sliceType === 9) {
|
|
281
|
+
isKeyframe = true;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (isKeyframe) {
|
|
285
|
+
// If we have non-keyframe data already, that cannot belong to
|
|
286
|
+
// the same frame as a keyframe, so force a push
|
|
287
|
+
if (lastVideoSample &&
|
|
288
|
+
lastVideoSample.frame && !lastVideoSample.isKeyframe) {
|
|
289
|
+
addLastVideoSample();
|
|
290
|
+
lastVideoSample = null;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (!lastVideoSample) {
|
|
294
|
+
createLastVideoSample(pes);
|
|
295
|
+
}
|
|
296
|
+
lastVideoSample.frame = true;
|
|
297
|
+
lastVideoSample.isKeyframe = isKeyframe;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
case H264.NALU_TYPE_IDR_: {
|
|
301
|
+
push = true;
|
|
302
|
+
// Handle PES not starting with AUD
|
|
303
|
+
// If we have frame data already, that cannot belong to the same
|
|
304
|
+
// frame, so force a push
|
|
305
|
+
if (lastVideoSample &&
|
|
306
|
+
lastVideoSample.frame && !lastVideoSample.isKeyframe) {
|
|
307
|
+
addLastVideoSample();
|
|
308
|
+
lastVideoSample = null;
|
|
309
|
+
}
|
|
310
|
+
if (!lastVideoSample) {
|
|
311
|
+
createLastVideoSample(pes);
|
|
312
|
+
}
|
|
313
|
+
lastVideoSample.frame = true;
|
|
314
|
+
lastVideoSample.isKeyframe = true;
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
case H264.NALU_TYPE_SEI_:
|
|
318
|
+
push = true;
|
|
319
|
+
break;
|
|
320
|
+
case H264.NALU_TYPE_SPS_:
|
|
321
|
+
push = true;
|
|
322
|
+
spsFound = true;
|
|
323
|
+
break;
|
|
324
|
+
case H264.NALU_TYPE_PPS_:
|
|
325
|
+
push = true;
|
|
326
|
+
break;
|
|
327
|
+
case H264.NALU_TYPE_AUD_:
|
|
328
|
+
push = true;
|
|
329
|
+
audFound = true;
|
|
330
|
+
if (lastVideoSample && lastVideoSample.frame) {
|
|
331
|
+
addLastVideoSample();
|
|
332
|
+
lastVideoSample = null;
|
|
333
|
+
}
|
|
334
|
+
if (!lastVideoSample) {
|
|
335
|
+
createLastVideoSample(pes);
|
|
336
|
+
}
|
|
337
|
+
break;
|
|
338
|
+
case H264.NALU_TYPE_FILLER_DATA_:
|
|
339
|
+
push = true;
|
|
340
|
+
break;
|
|
341
|
+
default:
|
|
342
|
+
push = false;
|
|
343
|
+
break;
|
|
344
|
+
}
|
|
345
|
+
if (lastVideoSample && push) {
|
|
346
|
+
lastVideoSample.nalus.push(nalu);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// If last PES packet, push samples
|
|
352
|
+
addLastVideoSample();
|
|
353
|
+
|
|
354
|
+
return videoSamples;
|
|
282
355
|
}
|
|
283
356
|
};
|
|
284
357
|
|
|
@@ -34,6 +34,9 @@ shaka.transmuxer.Mp3Transmuxer = class {
|
|
|
34
34
|
|
|
35
35
|
/** @private {!Map.<string, !Uint8Array>} */
|
|
36
36
|
this.initSegments = new Map();
|
|
37
|
+
|
|
38
|
+
/** @private {?Uint8Array} */
|
|
39
|
+
this.lastInitSegment_ = null;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
|
|
@@ -196,11 +199,16 @@ shaka.transmuxer.Mp3Transmuxer = class {
|
|
|
196
199
|
} else {
|
|
197
200
|
initSegment = this.initSegments.get(initSegmentKey);
|
|
198
201
|
}
|
|
202
|
+
const appendInitSegment = this.lastInitSegment_ !== initSegment;
|
|
199
203
|
const segmentData = mp4Generator.segmentData();
|
|
200
|
-
|
|
204
|
+
this.lastInitSegment_ = initSegment;
|
|
201
205
|
this.frameIndex_++;
|
|
202
|
-
|
|
203
|
-
|
|
206
|
+
if (appendInitSegment) {
|
|
207
|
+
const transmuxData = Uint8ArrayUtils.concat(initSegment, segmentData);
|
|
208
|
+
return Promise.resolve(transmuxData);
|
|
209
|
+
} else {
|
|
210
|
+
return Promise.resolve(segmentData);
|
|
211
|
+
}
|
|
204
212
|
}
|
|
205
213
|
};
|
|
206
214
|
|
|
@@ -52,6 +52,9 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
52
52
|
|
|
53
53
|
/** @private {?shaka.transmuxer.AacTransmuxer} */
|
|
54
54
|
this.aacTransmuxer_ = null;
|
|
55
|
+
|
|
56
|
+
/** @private {?Uint8Array} */
|
|
57
|
+
this.lastInitSegment_ = null;
|
|
55
58
|
}
|
|
56
59
|
|
|
57
60
|
|
|
@@ -293,11 +296,16 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
293
296
|
} else {
|
|
294
297
|
initSegment = this.initSegments.get(initSegmentKey);
|
|
295
298
|
}
|
|
299
|
+
const appendInitSegment = this.lastInitSegment_ !== initSegment;
|
|
296
300
|
const segmentData = mp4Generator.segmentData();
|
|
297
|
-
|
|
301
|
+
this.lastInitSegment_ = initSegment;
|
|
298
302
|
this.frameIndex_++;
|
|
299
|
-
|
|
300
|
-
|
|
303
|
+
if (appendInitSegment) {
|
|
304
|
+
const transmuxData = Uint8ArrayUtils.concat(initSegment, segmentData);
|
|
305
|
+
return Promise.resolve(transmuxData);
|
|
306
|
+
} else {
|
|
307
|
+
return Promise.resolve(segmentData);
|
|
308
|
+
}
|
|
301
309
|
}
|
|
302
310
|
|
|
303
311
|
|
|
@@ -312,6 +320,7 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
312
320
|
getAacStreamInfo_(tsParser, stream, duration, reference) {
|
|
313
321
|
const ADTS = shaka.transmuxer.ADTS;
|
|
314
322
|
const timescale = shaka.util.TsParser.Timescale;
|
|
323
|
+
const Uint8ArrayUtils = shaka.util.Uint8ArrayUtils;
|
|
315
324
|
|
|
316
325
|
/** @type {!Array.<shaka.util.Mp4Generator.Mp4Sample>} */
|
|
317
326
|
const samples = [];
|
|
@@ -320,12 +329,41 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
320
329
|
|
|
321
330
|
let firstPts = null;
|
|
322
331
|
|
|
332
|
+
/** @type {?number} */
|
|
333
|
+
let nextStartOffset = null;
|
|
334
|
+
/** @type {?Uint8Array} */
|
|
335
|
+
let overflowBytes = null;
|
|
336
|
+
|
|
323
337
|
for (const audioData of tsParser.getAudioData()) {
|
|
324
|
-
|
|
338
|
+
let data = audioData.data;
|
|
325
339
|
if (!data) {
|
|
326
340
|
continue;
|
|
327
341
|
}
|
|
328
342
|
let offset = 0;
|
|
343
|
+
if (nextStartOffset == -1 && overflowBytes) {
|
|
344
|
+
data = Uint8ArrayUtils.concat(overflowBytes, audioData.data);
|
|
345
|
+
nextStartOffset = null;
|
|
346
|
+
} else if (nextStartOffset != null && overflowBytes) {
|
|
347
|
+
offset = Math.max(0, nextStartOffset);
|
|
348
|
+
const missingFrameData =
|
|
349
|
+
Uint8ArrayUtils.concat(overflowBytes, data.subarray(0, offset));
|
|
350
|
+
samples.push({
|
|
351
|
+
data: missingFrameData,
|
|
352
|
+
size: missingFrameData.byteLength,
|
|
353
|
+
duration: ADTS.AAC_SAMPLES_PER_FRAME,
|
|
354
|
+
cts: 0,
|
|
355
|
+
flags: {
|
|
356
|
+
isLeading: 0,
|
|
357
|
+
isDependedOn: 0,
|
|
358
|
+
hasRedundancy: 0,
|
|
359
|
+
degradPrio: 0,
|
|
360
|
+
dependsOn: 2,
|
|
361
|
+
isNonSync: 0,
|
|
362
|
+
},
|
|
363
|
+
});
|
|
364
|
+
overflowBytes = null;
|
|
365
|
+
nextStartOffset = null;
|
|
366
|
+
}
|
|
329
367
|
info = ADTS.parseInfo(data, offset);
|
|
330
368
|
if (!info) {
|
|
331
369
|
throw new shaka.util.Error(
|
|
@@ -342,12 +380,16 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
342
380
|
while (offset < data.length) {
|
|
343
381
|
const header = ADTS.parseHeader(data, offset);
|
|
344
382
|
if (!header) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
383
|
+
overflowBytes = data.subarray(offset, data.length);
|
|
384
|
+
nextStartOffset = -1;
|
|
385
|
+
break;
|
|
348
386
|
}
|
|
349
387
|
const length = header.headerLength + header.frameLength;
|
|
350
|
-
|
|
388
|
+
nextStartOffset = Math.max(0, offset + length - data.length);
|
|
389
|
+
if (nextStartOffset != 0) {
|
|
390
|
+
overflowBytes = data.subarray(
|
|
391
|
+
offset + header.headerLength, offset + length);
|
|
392
|
+
} else if (offset + length <= data.length) {
|
|
351
393
|
const frameData = data.subarray(
|
|
352
394
|
offset + header.headerLength, offset + length);
|
|
353
395
|
|
|
@@ -865,50 +907,48 @@ shaka.transmuxer.TsTransmuxer = class {
|
|
|
865
907
|
/** @type {?number} */
|
|
866
908
|
let baseMediaDecodeTime = null;
|
|
867
909
|
|
|
868
|
-
const nalus = [];
|
|
869
910
|
const videoData = tsParser.getVideoData();
|
|
870
|
-
|
|
911
|
+
const videoSamples = H264.getVideoSamples(videoData);
|
|
912
|
+
if (!videoSamples.length) {
|
|
871
913
|
throw new shaka.util.Error(
|
|
872
914
|
shaka.util.Error.Severity.CRITICAL,
|
|
873
915
|
shaka.util.Error.Category.MEDIA,
|
|
874
916
|
shaka.util.Error.Code.TRANSMUXING_NO_VIDEO_DATA,
|
|
875
917
|
reference ? reference.getUris()[0] : null);
|
|
876
918
|
}
|
|
877
|
-
for (let i = 0; i <
|
|
878
|
-
const
|
|
879
|
-
const dataNalus = pes.nalus;
|
|
880
|
-
nalus.push(...dataNalus);
|
|
881
|
-
const frame = H264.parseFrame(dataNalus);
|
|
882
|
-
if (!frame) {
|
|
883
|
-
continue;
|
|
884
|
-
}
|
|
919
|
+
for (let i = 0; i < videoSamples.length; i++) {
|
|
920
|
+
const videoSample = videoSamples[i];
|
|
885
921
|
if (baseMediaDecodeTime == null) {
|
|
886
|
-
baseMediaDecodeTime =
|
|
922
|
+
baseMediaDecodeTime = videoSample.dts;
|
|
887
923
|
}
|
|
888
924
|
let duration;
|
|
889
|
-
if (i + 1 <
|
|
890
|
-
duration = (
|
|
891
|
-
} else if (
|
|
892
|
-
duration = (
|
|
925
|
+
if (i + 1 < videoSamples.length) {
|
|
926
|
+
duration = (videoSamples[i + 1].dts || 0) - (videoSample.dts || 0);
|
|
927
|
+
} else if (videoSamples.length > 1) {
|
|
928
|
+
duration = (videoSample.dts || 0) - (videoSamples[i - 1].dts || 0);
|
|
893
929
|
} else {
|
|
894
930
|
duration = (reference.endTime - reference.startTime) * timescale;
|
|
895
931
|
}
|
|
896
932
|
samples.push({
|
|
897
|
-
data:
|
|
898
|
-
size:
|
|
933
|
+
data: videoSample.data,
|
|
934
|
+
size: videoSample.data.byteLength,
|
|
899
935
|
duration: duration,
|
|
900
|
-
cts: Math.round((
|
|
936
|
+
cts: Math.round((videoSample.pts || 0) - (videoSample.dts || 0)),
|
|
901
937
|
flags: {
|
|
902
938
|
isLeading: 0,
|
|
903
939
|
isDependedOn: 0,
|
|
904
940
|
hasRedundancy: 0,
|
|
905
941
|
degradPrio: 0,
|
|
906
|
-
dependsOn:
|
|
907
|
-
isNonSync:
|
|
942
|
+
dependsOn: videoSample.isKeyframe ? 2 : 1,
|
|
943
|
+
isNonSync: videoSample.isKeyframe ? 0 : 1,
|
|
908
944
|
},
|
|
909
945
|
});
|
|
910
946
|
}
|
|
911
947
|
|
|
948
|
+
const nalus = [];
|
|
949
|
+
for (const pes of videoData) {
|
|
950
|
+
nalus.push(...pes.nalus);
|
|
951
|
+
}
|
|
912
952
|
const info = H264.parseInfo(nalus);
|
|
913
953
|
|
|
914
954
|
if (!info || baseMediaDecodeTime == null) {
|
package/lib/util/drm_utils.js
CHANGED
|
@@ -113,6 +113,26 @@ shaka.util.DrmUtils = class {
|
|
|
113
113
|
return drmInfo ? drmInfo.keySystem : '';
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
/**
|
|
117
|
+
* @param {?string} keySystem
|
|
118
|
+
* @return {boolean}
|
|
119
|
+
*/
|
|
120
|
+
static isClearKeySystem(keySystem) {
|
|
121
|
+
return keySystem === 'org.w3.clearkey';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* @param {?string} keySystem
|
|
126
|
+
* @return {boolean}
|
|
127
|
+
*/
|
|
128
|
+
static isWidevineKeySystem(keySystem) {
|
|
129
|
+
if (keySystem) {
|
|
130
|
+
return !!keySystem.match(/^com\.widevine\.alpha/);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
|
|
116
136
|
/**
|
|
117
137
|
* @param {?string} keySystem
|
|
118
138
|
* @return {boolean}
|
package/lib/util/mime_utils.js
CHANGED
|
@@ -179,6 +179,9 @@ shaka.util.MimeUtils = class {
|
|
|
179
179
|
return 'vvc'; // H266
|
|
180
180
|
case base === 'dvh1':
|
|
181
181
|
case base === 'dvhe':
|
|
182
|
+
if (profile && profile.startsWith('05')) {
|
|
183
|
+
return 'dovi-p5'; // Dolby Vision profile 5
|
|
184
|
+
}
|
|
182
185
|
return 'dovi-hevc'; // Dolby Vision based in HEVC
|
|
183
186
|
case base === 'dvav':
|
|
184
187
|
case base === 'dva1':
|
package/lib/util/object_utils.js
CHANGED
|
@@ -89,4 +89,87 @@ shaka.util.ObjectUtils = class {
|
|
|
89
89
|
}
|
|
90
90
|
return clone;
|
|
91
91
|
}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Constructs a string out of a value, similar to the JSON.stringify method.
|
|
96
|
+
* Unlike that method, this guarantees that the order of the keys in an
|
|
97
|
+
* object is alphabetical, so it can be used as a way to reliably compare two
|
|
98
|
+
* objects.
|
|
99
|
+
*
|
|
100
|
+
* @param {?} value
|
|
101
|
+
* @return {string}
|
|
102
|
+
*/
|
|
103
|
+
static alphabeticalKeyOrderStringify(value) {
|
|
104
|
+
if (Array.isArray(value)) {
|
|
105
|
+
return shaka.util.ObjectUtils.arrayStringify_(value);
|
|
106
|
+
} else if (typeof value == 'function') {
|
|
107
|
+
// For safety, skip functions. For function x,
|
|
108
|
+
// x.prototype.constructor.prototype === x.prototype, so all functions
|
|
109
|
+
// contain circular references if treated like Objects.
|
|
110
|
+
return '';
|
|
111
|
+
} else if (value instanceof Object) {
|
|
112
|
+
return shaka.util.ObjectUtils.objectStringify_(value);
|
|
113
|
+
} else {
|
|
114
|
+
return JSON.stringify(value);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Helper for alphabeticalKeyOrderStringify for objects.
|
|
121
|
+
*
|
|
122
|
+
* @param {!Object} obj
|
|
123
|
+
* @return {string}
|
|
124
|
+
* @private
|
|
125
|
+
*/
|
|
126
|
+
static objectStringify_(obj) {
|
|
127
|
+
// NOTE: This excludes prototype chain keys. For now, this is intended for
|
|
128
|
+
// anonymous objects only, so we don't care. If that changes, go back to a
|
|
129
|
+
// for-in loop.
|
|
130
|
+
const keys = Object.keys(obj);
|
|
131
|
+
// Alphabetically sort the keys, so they will be in a reliable order.
|
|
132
|
+
keys.sort();
|
|
133
|
+
|
|
134
|
+
const terms = [];
|
|
135
|
+
for (const key of keys) {
|
|
136
|
+
const escapedKey = JSON.stringify(key);
|
|
137
|
+
const value = obj[key];
|
|
138
|
+
if (value !== undefined) {
|
|
139
|
+
const escapedValue =
|
|
140
|
+
shaka.util.ObjectUtils.alphabeticalKeyOrderStringify(value);
|
|
141
|
+
if (escapedValue) {
|
|
142
|
+
terms.push(escapedKey + ':' + escapedValue);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return '{' + terms.join(',') + '}';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Helper for alphabeticalKeyOrderStringify for arrays.
|
|
152
|
+
*
|
|
153
|
+
* This could itself be JSON.stringify, except we want objects within the
|
|
154
|
+
* array to go through our own stringifiers.
|
|
155
|
+
*
|
|
156
|
+
* @param {!Array} arr
|
|
157
|
+
* @return {string}
|
|
158
|
+
* @private
|
|
159
|
+
*/
|
|
160
|
+
static arrayStringify_(arr) {
|
|
161
|
+
const terms = [];
|
|
162
|
+
for (let index = 0; index < arr.length; index++) {
|
|
163
|
+
const escapedKey = index.toString();
|
|
164
|
+
const value = arr[index];
|
|
165
|
+
if (value !== undefined) {
|
|
166
|
+
const escapedValue =
|
|
167
|
+
shaka.util.ObjectUtils.alphabeticalKeyOrderStringify(value);
|
|
168
|
+
if (escapedValue) {
|
|
169
|
+
terms.push(escapedKey + ':' + escapedValue);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return '[' + terms.join(',') + ']';
|
|
174
|
+
}
|
|
92
175
|
};
|
package/lib/util/platform.js
CHANGED
|
@@ -780,46 +780,6 @@ shaka.util.Platform = class {
|
|
|
780
780
|
shaka.log.alwaysWarn('Tizen: Error detecting screen size, default ' +
|
|
781
781
|
'screen size 1920x1080.');
|
|
782
782
|
}
|
|
783
|
-
} else if (Platform.isXboxOne()) {
|
|
784
|
-
maxResolution.width = 1920;
|
|
785
|
-
maxResolution.height = 1080;
|
|
786
|
-
try {
|
|
787
|
-
let winRT = undefined;
|
|
788
|
-
|
|
789
|
-
// Try to access to WinRT for WebView, if it's not defined,
|
|
790
|
-
// try to access to WinRT for WebView2, if it's not defined either,
|
|
791
|
-
// let it throw.
|
|
792
|
-
if (typeof Windows !== 'undefined') {
|
|
793
|
-
winRT = Windows;
|
|
794
|
-
} else {
|
|
795
|
-
winRT = chrome.webview.hostObjects.sync.Windows;
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
const protectionCapabilities =
|
|
799
|
-
new winRT.Media.Protection.ProtectionCapabilities();
|
|
800
|
-
const protectionResult =
|
|
801
|
-
winRT.Media.Protection.ProtectionCapabilityResult;
|
|
802
|
-
// isTypeSupported may return "maybe", which means the operation is not
|
|
803
|
-
// completed. This means we need to retry
|
|
804
|
-
// https://learn.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilityresult?view=winrt-22621
|
|
805
|
-
let result = null;
|
|
806
|
-
const type =
|
|
807
|
-
'video/mp4;codecs="hvc1,mp4a";features="decode-res-x=3840,' +
|
|
808
|
-
'decode-res-y=2160,decode-bitrate=20000,decode-fps=30,' +
|
|
809
|
-
'decode-bpc=10,display-res-x=3840,display-res-y=2160,' +
|
|
810
|
-
'display-bpc=8"';
|
|
811
|
-
const keySystem = 'com.microsoft.playready.recommendation';
|
|
812
|
-
do {
|
|
813
|
-
result = protectionCapabilities.isTypeSupported(type, keySystem);
|
|
814
|
-
} while (result === protectionResult.maybe);
|
|
815
|
-
if (result === protectionResult.probably) {
|
|
816
|
-
maxResolution.width = 3840;
|
|
817
|
-
maxResolution.height = 2160;
|
|
818
|
-
}
|
|
819
|
-
} catch (e) {
|
|
820
|
-
shaka.log.alwaysWarn('Xbox: Error detecting screen size, default ' +
|
|
821
|
-
'screen size 1920x1080.');
|
|
822
|
-
}
|
|
823
783
|
} else if (Platform.isWebOS()) {
|
|
824
784
|
try {
|
|
825
785
|
const deviceInfo =
|
|
@@ -866,6 +826,54 @@ shaka.util.Platform = class {
|
|
|
866
826
|
maxResolution.width = 1920;
|
|
867
827
|
maxResolution.height = 1080;
|
|
868
828
|
}
|
|
829
|
+
} else {
|
|
830
|
+
// For Xbox and UWP apps.
|
|
831
|
+
let winRT = undefined;
|
|
832
|
+
try {
|
|
833
|
+
// Try to access to WinRT for WebView, if it's not defined,
|
|
834
|
+
// try to access to WinRT for WebView2, if it's not defined either,
|
|
835
|
+
// let it throw.
|
|
836
|
+
if (typeof Windows !== 'undefined') {
|
|
837
|
+
winRT = Windows;
|
|
838
|
+
} else {
|
|
839
|
+
winRT = chrome.webview.hostObjects.sync.Windows;
|
|
840
|
+
}
|
|
841
|
+
} catch (e) {}
|
|
842
|
+
if (winRT) {
|
|
843
|
+
maxResolution.width = 1920;
|
|
844
|
+
maxResolution.height = 1080;
|
|
845
|
+
try {
|
|
846
|
+
const protectionCapabilities =
|
|
847
|
+
new winRT.Media.Protection.ProtectionCapabilities();
|
|
848
|
+
const protectionResult =
|
|
849
|
+
winRT.Media.Protection.ProtectionCapabilityResult;
|
|
850
|
+
// isTypeSupported may return "maybe", which means the operation
|
|
851
|
+
// is not completed. This means we need to retry
|
|
852
|
+
// https://learn.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilityresult?view=winrt-22621
|
|
853
|
+
let result = null;
|
|
854
|
+
const type =
|
|
855
|
+
'video/mp4;codecs="hvc1,mp4a";features="decode-res-x=3840,' +
|
|
856
|
+
'decode-res-y=2160,decode-bitrate=20000,decode-fps=30,' +
|
|
857
|
+
'decode-bpc=10,display-res-x=3840,display-res-y=2160,' +
|
|
858
|
+
'display-bpc=8"';
|
|
859
|
+
const keySystem = 'com.microsoft.playready.recommendation';
|
|
860
|
+
do {
|
|
861
|
+
result = protectionCapabilities.isTypeSupported(type, keySystem);
|
|
862
|
+
} while (result === protectionResult.maybe);
|
|
863
|
+
if (result === protectionResult.probably) {
|
|
864
|
+
maxResolution.width = 3840;
|
|
865
|
+
maxResolution.height = 2160;
|
|
866
|
+
}
|
|
867
|
+
} catch (e) {
|
|
868
|
+
shaka.log.alwaysWarn('Xbox: Error detecting screen size, default ' +
|
|
869
|
+
'screen size 1920x1080.');
|
|
870
|
+
}
|
|
871
|
+
} else if (Platform.isXboxOne()) {
|
|
872
|
+
maxResolution.width = 1920;
|
|
873
|
+
maxResolution.height = 1080;
|
|
874
|
+
shaka.log.alwaysWarn('Xbox: Error detecting screen size, default ' +
|
|
875
|
+
'screen size 1920x1080.');
|
|
876
|
+
}
|
|
869
877
|
}
|
|
870
878
|
return maxResolution;
|
|
871
879
|
}
|