shaka-player 4.9.32 → 4.9.34
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 +6 -5
- package/dist/locales.js +36 -36
- 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 +453 -440
- package/dist/shaka-player.compiled.debug.map +4 -4
- package/dist/shaka-player.compiled.externs.js +8 -5
- package/dist/shaka-player.compiled.js +1281 -1271
- 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 +483 -470
- package/dist/shaka-player.ui.debug.map +4 -4
- package/dist/shaka-player.ui.externs.js +8 -5
- package/dist/shaka-player.ui.js +1478 -1468
- package/dist/shaka-player.ui.map +4 -4
- package/externs/managedmediasource.js +6 -0
- package/externs/shaka/codecs.js +21 -0
- package/externs/shaka/offline.js +4 -0
- package/lib/cast/cast_utils.js +1 -0
- package/lib/hls/hls_parser.js +26 -5
- package/lib/media/drm_engine.js +78 -18
- package/lib/media/media_source_capabilities.js +3 -2
- package/lib/media/media_source_engine.js +117 -36
- package/lib/media/segment_utils.js +57 -47
- 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 +11 -2
- package/lib/player.js +89 -53
- 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/text/vtt_text_parser.js +9 -4
- 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/abortable_operation.js +3 -0
- package/lib/util/drm_utils.js +20 -0
- package/lib/util/functional.js +27 -0
- package/lib/util/mime_utils.js +2 -0
- package/lib/util/number_utils.js +37 -0
- package/lib/util/object_utils.js +83 -0
- package/lib/util/platform.js +48 -40
- package/lib/util/stream_utils.js +27 -106
- package/lib/util/ts_parser.js +111 -85
- package/package.json +2 -2
- package/ui/controls.js +4 -0
- package/ui/fast_forward_button.js +7 -0
- package/ui/play_button.js +1 -1
- package/ui/rewind_button.js +7 -0
package/lib/util/platform.js
CHANGED
|
@@ -765,46 +765,6 @@ shaka.util.Platform = class {
|
|
|
765
765
|
shaka.log.alwaysWarn('Tizen: Error detecting screen size, default ' +
|
|
766
766
|
'screen size 1920x1080.');
|
|
767
767
|
}
|
|
768
|
-
} else if (Platform.isXboxOne()) {
|
|
769
|
-
maxResolution.width = 1920;
|
|
770
|
-
maxResolution.height = 1080;
|
|
771
|
-
try {
|
|
772
|
-
let winRT = undefined;
|
|
773
|
-
|
|
774
|
-
// Try to access to WinRT for WebView, if it's not defined,
|
|
775
|
-
// try to access to WinRT for WebView2, if it's not defined either,
|
|
776
|
-
// let it throw.
|
|
777
|
-
if (typeof Windows !== 'undefined') {
|
|
778
|
-
winRT = Windows;
|
|
779
|
-
} else {
|
|
780
|
-
winRT = chrome.webview.hostObjects.sync.Windows;
|
|
781
|
-
}
|
|
782
|
-
|
|
783
|
-
const protectionCapabilities =
|
|
784
|
-
new winRT.Media.Protection.ProtectionCapabilities();
|
|
785
|
-
const protectionResult =
|
|
786
|
-
winRT.Media.Protection.ProtectionCapabilityResult;
|
|
787
|
-
// isTypeSupported may return "maybe", which means the operation is not
|
|
788
|
-
// completed. This means we need to retry
|
|
789
|
-
// https://learn.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilityresult?view=winrt-22621
|
|
790
|
-
let result = null;
|
|
791
|
-
const type =
|
|
792
|
-
'video/mp4;codecs="hvc1,mp4a";features="decode-res-x=3840,' +
|
|
793
|
-
'decode-res-y=2160,decode-bitrate=20000,decode-fps=30,' +
|
|
794
|
-
'decode-bpc=10,display-res-x=3840,display-res-y=2160,' +
|
|
795
|
-
'display-bpc=8"';
|
|
796
|
-
const keySystem = 'com.microsoft.playready.recommendation';
|
|
797
|
-
do {
|
|
798
|
-
result = protectionCapabilities.isTypeSupported(type, keySystem);
|
|
799
|
-
} while (result === protectionResult.maybe);
|
|
800
|
-
if (result === protectionResult.probably) {
|
|
801
|
-
maxResolution.width = 3840;
|
|
802
|
-
maxResolution.height = 2160;
|
|
803
|
-
}
|
|
804
|
-
} catch (e) {
|
|
805
|
-
shaka.log.alwaysWarn('Xbox: Error detecting screen size, default ' +
|
|
806
|
-
'screen size 1920x1080.');
|
|
807
|
-
}
|
|
808
768
|
} else if (Platform.isWebOS()) {
|
|
809
769
|
try {
|
|
810
770
|
const deviceInfo =
|
|
@@ -851,6 +811,54 @@ shaka.util.Platform = class {
|
|
|
851
811
|
maxResolution.width = 1920;
|
|
852
812
|
maxResolution.height = 1080;
|
|
853
813
|
}
|
|
814
|
+
} else {
|
|
815
|
+
// For Xbox and UWP apps.
|
|
816
|
+
let winRT = undefined;
|
|
817
|
+
try {
|
|
818
|
+
// Try to access to WinRT for WebView, if it's not defined,
|
|
819
|
+
// try to access to WinRT for WebView2, if it's not defined either,
|
|
820
|
+
// let it throw.
|
|
821
|
+
if (typeof Windows !== 'undefined') {
|
|
822
|
+
winRT = Windows;
|
|
823
|
+
} else {
|
|
824
|
+
winRT = chrome.webview.hostObjects.sync.Windows;
|
|
825
|
+
}
|
|
826
|
+
} catch (e) {}
|
|
827
|
+
if (winRT) {
|
|
828
|
+
maxResolution.width = 1920;
|
|
829
|
+
maxResolution.height = 1080;
|
|
830
|
+
try {
|
|
831
|
+
const protectionCapabilities =
|
|
832
|
+
new winRT.Media.Protection.ProtectionCapabilities();
|
|
833
|
+
const protectionResult =
|
|
834
|
+
winRT.Media.Protection.ProtectionCapabilityResult;
|
|
835
|
+
// isTypeSupported may return "maybe", which means the operation
|
|
836
|
+
// is not completed. This means we need to retry
|
|
837
|
+
// https://learn.microsoft.com/en-us/uwp/api/windows.media.protection.protectioncapabilityresult?view=winrt-22621
|
|
838
|
+
let result = null;
|
|
839
|
+
const type =
|
|
840
|
+
'video/mp4;codecs="hvc1,mp4a";features="decode-res-x=3840,' +
|
|
841
|
+
'decode-res-y=2160,decode-bitrate=20000,decode-fps=30,' +
|
|
842
|
+
'decode-bpc=10,display-res-x=3840,display-res-y=2160,' +
|
|
843
|
+
'display-bpc=8"';
|
|
844
|
+
const keySystem = 'com.microsoft.playready.recommendation';
|
|
845
|
+
do {
|
|
846
|
+
result = protectionCapabilities.isTypeSupported(type, keySystem);
|
|
847
|
+
} while (result === protectionResult.maybe);
|
|
848
|
+
if (result === protectionResult.probably) {
|
|
849
|
+
maxResolution.width = 3840;
|
|
850
|
+
maxResolution.height = 2160;
|
|
851
|
+
}
|
|
852
|
+
} catch (e) {
|
|
853
|
+
shaka.log.alwaysWarn('Xbox: Error detecting screen size, default ' +
|
|
854
|
+
'screen size 1920x1080.');
|
|
855
|
+
}
|
|
856
|
+
} else if (Platform.isXboxOne()) {
|
|
857
|
+
maxResolution.width = 1920;
|
|
858
|
+
maxResolution.height = 1080;
|
|
859
|
+
shaka.log.alwaysWarn('Xbox: Error detecting screen size, default ' +
|
|
860
|
+
'screen size 1920x1080.');
|
|
861
|
+
}
|
|
854
862
|
}
|
|
855
863
|
return maxResolution;
|
|
856
864
|
}
|
package/lib/util/stream_utils.js
CHANGED
|
@@ -444,11 +444,23 @@ shaka.util.StreamUtils = class {
|
|
|
444
444
|
* @private
|
|
445
445
|
*/
|
|
446
446
|
static checkVariantSupported_(variant, keySystem) {
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
447
|
+
const variantSupported = variant.decodingInfos.some((decodingInfo) => {
|
|
448
|
+
if (!decodingInfo.supported) {
|
|
449
|
+
return false;
|
|
450
|
+
}
|
|
451
|
+
if (keySystem) {
|
|
452
|
+
const keySystemAccess = decodingInfo.keySystemAccess;
|
|
453
|
+
if (keySystemAccess) {
|
|
454
|
+
if (keySystemAccess.keySystem != keySystem) {
|
|
455
|
+
return false;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return true;
|
|
460
|
+
});
|
|
461
|
+
if (!variantSupported) {
|
|
462
|
+
return false;
|
|
463
|
+
}
|
|
452
464
|
|
|
453
465
|
const isXboxOne = shaka.util.Platform.isXboxOne();
|
|
454
466
|
const isFirefoxAndroid = shaka.util.Platform.isFirefox() &&
|
|
@@ -466,43 +478,6 @@ shaka.util.StreamUtils = class {
|
|
|
466
478
|
return false;
|
|
467
479
|
}
|
|
468
480
|
|
|
469
|
-
if (video) {
|
|
470
|
-
let videoCodecs = StreamUtils.getCorrectVideoCodecs(video.codecs);
|
|
471
|
-
// For multiplexed streams. Here we must check the audio of the
|
|
472
|
-
// stream to see if it is compatible.
|
|
473
|
-
if (video.codecs.includes(',')) {
|
|
474
|
-
const allCodecs = video.codecs.split(',');
|
|
475
|
-
|
|
476
|
-
videoCodecs = ManifestParserUtils.guessCodecs(
|
|
477
|
-
ContentType.VIDEO, allCodecs);
|
|
478
|
-
videoCodecs = StreamUtils.getCorrectVideoCodecs(videoCodecs);
|
|
479
|
-
let audioCodecs = ManifestParserUtils.guessCodecs(
|
|
480
|
-
ContentType.AUDIO, allCodecs);
|
|
481
|
-
audioCodecs = StreamUtils.getCorrectAudioCodecs(
|
|
482
|
-
audioCodecs, video.mimeType);
|
|
483
|
-
|
|
484
|
-
const audioFullType = MimeUtils.getFullOrConvertedType(
|
|
485
|
-
video.mimeType, audioCodecs, ContentType.AUDIO);
|
|
486
|
-
|
|
487
|
-
if (!Capabilities.isTypeSupported(audioFullType)) {
|
|
488
|
-
return false;
|
|
489
|
-
}
|
|
490
|
-
|
|
491
|
-
// Update the codec string with the (possibly) converted codecs.
|
|
492
|
-
videoCodecs = [videoCodecs, audioCodecs].join(',');
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
const fullType = MimeUtils.getFullOrConvertedType(
|
|
496
|
-
video.mimeType, videoCodecs, ContentType.VIDEO);
|
|
497
|
-
|
|
498
|
-
if (!Capabilities.isTypeSupported(fullType)) {
|
|
499
|
-
return false;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
// Update the codec string with the (possibly) converted codecs.
|
|
503
|
-
video.codecs = videoCodecs;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
481
|
const audio = variant.audio;
|
|
507
482
|
|
|
508
483
|
// See: https://github.com/shaka-project/shaka-player/issues/6111
|
|
@@ -514,68 +489,7 @@ shaka.util.StreamUtils = class {
|
|
|
514
489
|
return false;
|
|
515
490
|
}
|
|
516
491
|
|
|
517
|
-
|
|
518
|
-
const codecs = StreamUtils.getCorrectAudioCodecs(
|
|
519
|
-
audio.codecs, audio.mimeType);
|
|
520
|
-
const fullType = MimeUtils.getFullOrConvertedType(
|
|
521
|
-
audio.mimeType, codecs, ContentType.AUDIO);
|
|
522
|
-
|
|
523
|
-
if (!Capabilities.isTypeSupported(fullType)) {
|
|
524
|
-
return false;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
// Update the codec string with the (possibly) converted codecs.
|
|
528
|
-
audio.codecs = codecs;
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
return variant.decodingInfos.some((decodingInfo) => {
|
|
532
|
-
if (!decodingInfo.supported) {
|
|
533
|
-
return false;
|
|
534
|
-
}
|
|
535
|
-
if (keySystem) {
|
|
536
|
-
const keySystemAccess = decodingInfo.keySystemAccess;
|
|
537
|
-
if (keySystemAccess) {
|
|
538
|
-
if (keySystemAccess.keySystem != keySystem) {
|
|
539
|
-
return false;
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
return true;
|
|
544
|
-
});
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
/**
|
|
549
|
-
* Constructs a string out of an object, similar to the JSON.stringify method.
|
|
550
|
-
* Unlike that method, this guarantees that the order of the keys is
|
|
551
|
-
* alphabetical, so it can be used as a way to reliably compare two objects.
|
|
552
|
-
*
|
|
553
|
-
* @param {!Object} obj
|
|
554
|
-
* @return {string}
|
|
555
|
-
* @private
|
|
556
|
-
*/
|
|
557
|
-
static alphabeticalKeyOrderStringify_(obj) {
|
|
558
|
-
const keys = [];
|
|
559
|
-
for (const key in obj) {
|
|
560
|
-
keys.push(key);
|
|
561
|
-
}
|
|
562
|
-
// Alphabetically sort the keys, so they will be in a reliable order.
|
|
563
|
-
keys.sort();
|
|
564
|
-
|
|
565
|
-
const terms = [];
|
|
566
|
-
for (const key of keys) {
|
|
567
|
-
const escapedKey = JSON.stringify(key);
|
|
568
|
-
const value = obj[key];
|
|
569
|
-
if (value instanceof Object) {
|
|
570
|
-
const stringifiedValue =
|
|
571
|
-
shaka.util.StreamUtils.alphabeticalKeyOrderStringify_(value);
|
|
572
|
-
terms.push(escapedKey + ':' + stringifiedValue);
|
|
573
|
-
} else {
|
|
574
|
-
const escapedValue = JSON.stringify(value);
|
|
575
|
-
terms.push(escapedKey + ':' + escapedValue);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
return '{' + terms.join(',') + '}';
|
|
492
|
+
return true;
|
|
579
493
|
}
|
|
580
494
|
|
|
581
495
|
|
|
@@ -614,7 +528,7 @@ shaka.util.StreamUtils = class {
|
|
|
614
528
|
const promises = [];
|
|
615
529
|
for (const decodingConfig of decodingConfigs) {
|
|
616
530
|
const cacheKey =
|
|
617
|
-
|
|
531
|
+
shaka.util.ObjectUtils.alphabeticalKeyOrderStringify(decodingConfig);
|
|
618
532
|
|
|
619
533
|
const cache = StreamUtils.decodingConfigCache_;
|
|
620
534
|
if (cache[cacheKey]) {
|
|
@@ -682,7 +596,14 @@ shaka.util.StreamUtils = class {
|
|
|
682
596
|
mimeType, audioCodec);
|
|
683
597
|
}
|
|
684
598
|
promises.push(new Promise((resolve, reject) => {
|
|
685
|
-
|
|
599
|
+
// On some (Android) WebView environments, decodingInfo will
|
|
600
|
+
// not resolve or reject, at least if RESOURCE_PROTECTED_MEDIA_ID
|
|
601
|
+
// is not set. This is a workaround for that issue.
|
|
602
|
+
const TIMEOUT_FOR_DECODING_INFO_IN_SECONDS = 1;
|
|
603
|
+
shaka.util.Functional.promiseWithTimeout(
|
|
604
|
+
TIMEOUT_FOR_DECODING_INFO_IN_SECONDS,
|
|
605
|
+
navigator.mediaCapabilities.decodingInfo(copy),
|
|
606
|
+
).then((res) => {
|
|
686
607
|
resolve(res);
|
|
687
608
|
}).catch(reject);
|
|
688
609
|
}));
|
package/lib/util/ts_parser.js
CHANGED
|
@@ -516,7 +516,11 @@ shaka.util.TsParser = class {
|
|
|
516
516
|
shaka.Deprecate.deprecateFeature(5,
|
|
517
517
|
'TsParser.parseAvcNalus',
|
|
518
518
|
'Please use parseNalus function instead.');
|
|
519
|
-
|
|
519
|
+
const lastInfo = {
|
|
520
|
+
nalu: null,
|
|
521
|
+
state: null,
|
|
522
|
+
};
|
|
523
|
+
return this.parseNalus(pes, lastInfo);
|
|
520
524
|
}
|
|
521
525
|
|
|
522
526
|
/**
|
|
@@ -526,12 +530,11 @@ shaka.util.TsParser = class {
|
|
|
526
530
|
* Credit to https://github.com/video-dev/hls.js/blob/master/src/demux/tsdemuxer.ts
|
|
527
531
|
*
|
|
528
532
|
* @param {shaka.extern.MPEG_PES} pes
|
|
529
|
-
* @param {?shaka.extern.VideoNalu
|
|
530
|
-
* @param {?number=} lastState
|
|
533
|
+
* @param {{nalu: ?shaka.extern.VideoNalu, state: ?number}} lastInfo
|
|
531
534
|
* @return {!Array.<shaka.extern.VideoNalu>}
|
|
532
535
|
* @export
|
|
533
536
|
*/
|
|
534
|
-
parseNalus(pes,
|
|
537
|
+
parseNalus(pes, lastInfo) {
|
|
535
538
|
const timescale = shaka.util.TsParser.Timescale;
|
|
536
539
|
const time = pes.pts ? pes.pts / timescale : null;
|
|
537
540
|
const data = pes.data;
|
|
@@ -547,7 +550,12 @@ shaka.util.TsParser = class {
|
|
|
547
550
|
// precede each NALU. A start code is 2 or 3 0x00 bytes followed with a
|
|
548
551
|
// 0x01 byte. e.g. 0x000001 or 0x00000001.
|
|
549
552
|
// More info in: https://stackoverflow.com/questions/24884827/possible-locations-for-sequence-picture-parameter-sets-for-h-264-stream/24890903#24890903
|
|
550
|
-
let numZeros =
|
|
553
|
+
let numZeros = lastInfo.state || 0;
|
|
554
|
+
|
|
555
|
+
const initialNumZeros = numZeros;
|
|
556
|
+
|
|
557
|
+
/** @type {number} */
|
|
558
|
+
let i = 0;
|
|
551
559
|
|
|
552
560
|
/** @type {!Array.<shaka.extern.VideoNalu>} */
|
|
553
561
|
const nalus = [];
|
|
@@ -558,103 +566,119 @@ shaka.util.TsParser = class {
|
|
|
558
566
|
// Extracted from the first byte.
|
|
559
567
|
let lastNaluType = 0;
|
|
560
568
|
|
|
561
|
-
|
|
562
|
-
|
|
569
|
+
const getNaluType = (offset) => {
|
|
570
|
+
if (this.videoCodec_ == 'hvc') {
|
|
571
|
+
return (data[offset] >> 1) & 0x3f;
|
|
572
|
+
} else {
|
|
573
|
+
return data[offset] & 0x1f;
|
|
574
|
+
}
|
|
575
|
+
};
|
|
563
576
|
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
// If we are scanning the next PES, we need append the data to the
|
|
571
|
-
// previous Nalu and don't scan for more nalus.
|
|
572
|
-
const startCodeSize = numZeros > 3 ? 3 : numZeros;
|
|
573
|
-
const lastByteToKeep = i - startCodeSize;
|
|
574
|
-
// Optimization
|
|
575
|
-
if (lastState && lastByteToKeep != 0) {
|
|
576
|
-
const prevData = data.subarray(0, lastByteToKeep);
|
|
577
|
-
lastNalu.data = shaka.util.Uint8ArrayUtils.concat(
|
|
578
|
-
lastNalu.data, prevData);
|
|
579
|
-
lastNalu.fullData = shaka.util.Uint8ArrayUtils.concat(
|
|
580
|
-
lastNalu.fullData, prevData);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
// We just read a start code. Consume the NALU we passed, if any.
|
|
584
|
-
if (lastNaluStart >= 0) {
|
|
585
|
-
// Because the start position includes the header size.
|
|
586
|
-
const firstByteToKeep = lastNaluStart + naluHeaderSize;
|
|
577
|
+
const getLastNalu = () => {
|
|
578
|
+
if (nalus.length) {
|
|
579
|
+
return nalus[nalus.length - 1];
|
|
580
|
+
}
|
|
581
|
+
return lastInfo.nalu;
|
|
582
|
+
};
|
|
587
583
|
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
584
|
+
if (numZeros == -1) {
|
|
585
|
+
// special use case where we found 3 or 4-byte start codes exactly at the
|
|
586
|
+
// end of previous PES packet
|
|
587
|
+
lastNaluStart = 0;
|
|
588
|
+
// NALu type is value read from offset 0
|
|
589
|
+
lastNaluType = getNaluType(0);
|
|
590
|
+
numZeros = 0;
|
|
591
|
+
i = 1;
|
|
592
|
+
}
|
|
592
593
|
|
|
594
|
+
while (i < len) {
|
|
595
|
+
const value = data[i++];
|
|
596
|
+
// Optimization. numZeros 0 and 1 are the predominant case.
|
|
597
|
+
if (!numZeros) {
|
|
598
|
+
numZeros = value ? 0 : 1;
|
|
599
|
+
continue;
|
|
600
|
+
}
|
|
601
|
+
if (numZeros === 1) {
|
|
602
|
+
numZeros = value ? 0 : 2;
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
if (!value) {
|
|
606
|
+
numZeros = 3;
|
|
607
|
+
} else if (value == 1) {
|
|
608
|
+
const overflow = i - numZeros - 1;
|
|
609
|
+
if (lastNaluStart >= 0) {
|
|
593
610
|
/** @type {shaka.extern.VideoNalu} */
|
|
594
611
|
const nalu = {
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
fullData: data.subarray(lastNaluStart, lastByteToKeep + 1),
|
|
612
|
+
data: data.subarray(lastNaluStart + naluHeaderSize, overflow),
|
|
613
|
+
fullData: data.subarray(lastNaluStart, overflow),
|
|
598
614
|
type: lastNaluType,
|
|
599
615
|
time: time,
|
|
616
|
+
state: null,
|
|
600
617
|
};
|
|
601
618
|
nalus.push(nalu);
|
|
602
|
-
} else
|
|
603
|
-
const
|
|
604
|
-
if (
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
619
|
+
} else {
|
|
620
|
+
const lastNalu = getLastNalu();
|
|
621
|
+
if (lastNalu) {
|
|
622
|
+
if (initialNumZeros && i <= 4 - initialNumZeros) {
|
|
623
|
+
// Start delimiter overlapping between PES packets
|
|
624
|
+
// strip start delimiter bytes from the end of last NAL unit
|
|
625
|
+
// check if lastNalu had a state different from zero
|
|
626
|
+
if (lastNalu.state) {
|
|
627
|
+
// strip last bytes
|
|
628
|
+
lastNalu.data = lastNalu.data.subarray(
|
|
629
|
+
0, lastNalu.data.byteLength - initialNumZeros);
|
|
630
|
+
lastNalu.fullData = lastNalu.fullData.subarray(
|
|
631
|
+
0, lastNalu.fullData.byteLength - initialNumZeros);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
// If NAL units are not starting right at the beginning of the PES
|
|
635
|
+
// packet, push preceding data into previous NAL unit.
|
|
636
|
+
if (overflow > 0) {
|
|
637
|
+
const prevData = data.subarray(0, overflow);
|
|
638
|
+
lastNalu.data = shaka.util.Uint8ArrayUtils.concat(
|
|
639
|
+
lastNalu.data, prevData);
|
|
640
|
+
lastNalu.fullData = shaka.util.Uint8ArrayUtils.concat(
|
|
641
|
+
lastNalu.fullData, prevData);
|
|
642
|
+
lastNalu.state = 0;
|
|
643
|
+
}
|
|
610
644
|
}
|
|
611
645
|
}
|
|
612
646
|
|
|
613
|
-
//
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
// Advance and read the type of the next NALU.
|
|
621
|
-
i++;
|
|
622
|
-
lastNaluStart = i;
|
|
623
|
-
if (this.videoCodec_ == 'hvc') {
|
|
624
|
-
lastNaluType = (data[i] >> 1) & 0x3f;
|
|
647
|
+
// Check if we can read unit type
|
|
648
|
+
if (i < len) {
|
|
649
|
+
lastNaluType = getNaluType(i);
|
|
650
|
+
lastNaluStart = i;
|
|
651
|
+
numZeros = 0;
|
|
625
652
|
} else {
|
|
626
|
-
|
|
653
|
+
// Not enough byte to read unit type.
|
|
654
|
+
// Let's read it on next PES parsing.
|
|
655
|
+
numZeros = -1;
|
|
627
656
|
}
|
|
628
|
-
numZeros = 0;
|
|
629
657
|
} else {
|
|
630
658
|
numZeros = 0;
|
|
631
659
|
}
|
|
632
|
-
// If we have gone through all the data from the PES and we have an
|
|
633
|
-
// unfinished Nalu, we will try to use the next PES to complete the
|
|
634
|
-
// unfinished Nalu.
|
|
635
|
-
if (i >= (len - 1) && lastNaluStart >= 0 && numZeros >= 0) {
|
|
636
|
-
// The rest of the buffer was a NALU.
|
|
637
|
-
// Because the start position includes the header size.
|
|
638
|
-
const firstByteToKeep = lastNaluStart + naluHeaderSize;
|
|
639
|
-
infoOfLastNalu = {
|
|
640
|
-
data: data.subarray(firstByteToKeep, len),
|
|
641
|
-
fullData: data.subarray(lastNaluStart, len),
|
|
642
|
-
type: lastNaluType,
|
|
643
|
-
time: time,
|
|
644
|
-
};
|
|
645
|
-
}
|
|
646
660
|
}
|
|
647
661
|
|
|
648
|
-
if (
|
|
649
|
-
|
|
662
|
+
if (lastNaluStart >= 0 && numZeros >= 0) {
|
|
663
|
+
const nalu = {
|
|
664
|
+
data: data.subarray(lastNaluStart + naluHeaderSize, len),
|
|
665
|
+
fullData: data.subarray(lastNaluStart, len),
|
|
666
|
+
type: lastNaluType,
|
|
667
|
+
time: time,
|
|
668
|
+
state: numZeros,
|
|
669
|
+
};
|
|
670
|
+
nalus.push(nalu);
|
|
650
671
|
}
|
|
651
|
-
if (!nalus.length &&
|
|
652
|
-
lastNalu
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
672
|
+
if (!nalus.length && lastInfo.nalu) {
|
|
673
|
+
const lastNalu = getLastNalu();
|
|
674
|
+
if (lastNalu) {
|
|
675
|
+
lastNalu.data = shaka.util.Uint8ArrayUtils.concat(
|
|
676
|
+
lastNalu.data, data);
|
|
677
|
+
lastNalu.fullData = shaka.util.Uint8ArrayUtils.concat(
|
|
678
|
+
lastNalu.fullData, data);
|
|
679
|
+
}
|
|
656
680
|
}
|
|
657
|
-
|
|
681
|
+
lastInfo.state = numZeros;
|
|
658
682
|
return nalus;
|
|
659
683
|
}
|
|
660
684
|
|
|
@@ -758,14 +782,16 @@ shaka.util.TsParser = class {
|
|
|
758
782
|
}
|
|
759
783
|
}
|
|
760
784
|
if (naluProcessing) {
|
|
761
|
-
|
|
762
|
-
|
|
785
|
+
const lastInfo = {
|
|
786
|
+
nalu: null,
|
|
787
|
+
state: null,
|
|
788
|
+
};
|
|
763
789
|
const pesWithLength = [];
|
|
764
790
|
for (const pes of this.videoPes_) {
|
|
765
|
-
pes.nalus = this.parseNalus(pes,
|
|
791
|
+
pes.nalus = this.parseNalus(pes, lastInfo);
|
|
766
792
|
if (pes.nalus.length) {
|
|
767
793
|
pesWithLength.push(pes);
|
|
768
|
-
|
|
794
|
+
lastInfo.nalu = pes.nalus[pes.nalus.length - 1];
|
|
769
795
|
}
|
|
770
796
|
}
|
|
771
797
|
this.videoPes_ = pesWithLength;
|
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.
|
|
4
|
+
"version": "4.9.34",
|
|
5
5
|
"homepage": "https://github.com/shaka-project/shaka-player",
|
|
6
6
|
"author": "Google",
|
|
7
7
|
"maintainers": [
|
|
@@ -105,7 +105,7 @@
|
|
|
105
105
|
"prepublishOnly": "python build/checkversion.py && python build/all.py --force"
|
|
106
106
|
},
|
|
107
107
|
"dependencies": {
|
|
108
|
-
"eme-encryption-scheme-polyfill": "^2.1.
|
|
108
|
+
"eme-encryption-scheme-polyfill": "^2.1.6"
|
|
109
109
|
},
|
|
110
110
|
"clean-package": {
|
|
111
111
|
"remove": [
|
package/ui/controls.js
CHANGED
|
@@ -841,6 +841,10 @@ shaka.ui.Controls = class extends shaka.util.FakeEventTarget {
|
|
|
841
841
|
this.player_.cancelTrickPlay();
|
|
842
842
|
|
|
843
843
|
if (this.presentationIsPaused()) {
|
|
844
|
+
// If we are at the end, go back to the beginning.
|
|
845
|
+
if (this.player_.isEnded()) {
|
|
846
|
+
this.video_.currentTime = this.player_.seekRange().start;
|
|
847
|
+
}
|
|
844
848
|
this.video_.play();
|
|
845
849
|
} else {
|
|
846
850
|
this.video_.pause();
|
|
@@ -81,6 +81,13 @@ shaka.ui.FastForwardButton = class extends shaka.ui.Element {
|
|
|
81
81
|
// selected. If no more rates are available, the first one is set.
|
|
82
82
|
const newRate = (newRateIndex != this.fastForwardRates_.length) ?
|
|
83
83
|
this.fastForwardRates_[newRateIndex] : this.fastForwardRates_[0];
|
|
84
|
+
|
|
85
|
+
if (this.video.paused) {
|
|
86
|
+
// Our fast forward is implemented with playbackRate and needs the video
|
|
87
|
+
// to be playing (to not be paused) to take immediate effect.
|
|
88
|
+
// If the video is paused, "unpause" it.
|
|
89
|
+
this.video.play();
|
|
90
|
+
}
|
|
84
91
|
this.player.trickPlay(newRate);
|
|
85
92
|
|
|
86
93
|
this.button_.setAttribute('shaka-status', newRate + 'x');
|
package/ui/play_button.js
CHANGED
package/ui/rewind_button.js
CHANGED
|
@@ -82,6 +82,13 @@ shaka.ui.RewindButton = class extends shaka.ui.Element {
|
|
|
82
82
|
// selected. If no more rates are available, the first one is set.
|
|
83
83
|
const newRate = (newRateIndex != this.rewindRates_.length) ?
|
|
84
84
|
this.rewindRates_[newRateIndex] : this.rewindRates_[0];
|
|
85
|
+
|
|
86
|
+
if (this.video.paused) {
|
|
87
|
+
// Our fast forward is implemented with playbackRate and needs the video
|
|
88
|
+
// to be playing (to not be paused) to take immediate effect.
|
|
89
|
+
// If the video is paused, "unpause" it.
|
|
90
|
+
this.video.play();
|
|
91
|
+
}
|
|
85
92
|
this.player.trickPlay(newRate);
|
|
86
93
|
|
|
87
94
|
this.button_.setAttribute('shaka-status', newRate + 'x');
|