shaka-player 4.2.11 → 4.2.12

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.
Files changed (49) hide show
  1. package/.github/workflows/appspot.yaml +39 -0
  2. package/CHANGELOG.md +25 -0
  3. package/CONTRIBUTORS +1 -0
  4. package/demo/custom.js +1 -1
  5. package/dist/demo.compiled.debug.js +2 -2
  6. package/dist/demo.compiled.debug.map +2 -2
  7. package/dist/demo.compiled.js +2 -2
  8. package/dist/demo.compiled.map +2 -2
  9. package/dist/deps.js +2 -2
  10. package/dist/locales.js +30 -30
  11. package/dist/shaka-player.compiled.d.ts +22 -0
  12. package/dist/shaka-player.compiled.debug.d.ts +22 -0
  13. package/dist/shaka-player.compiled.debug.js +216 -214
  14. package/dist/shaka-player.compiled.debug.map +3 -3
  15. package/dist/shaka-player.compiled.js +735 -734
  16. package/dist/shaka-player.compiled.map +4 -4
  17. package/dist/shaka-player.ui.d.ts +22 -0
  18. package/dist/shaka-player.ui.debug.d.ts +22 -0
  19. package/dist/shaka-player.ui.debug.js +241 -238
  20. package/dist/shaka-player.ui.debug.map +3 -3
  21. package/dist/shaka-player.ui.js +916 -915
  22. package/dist/shaka-player.ui.map +4 -4
  23. package/docs/tutorials/drm-config.md +9 -6
  24. package/externs/ima.js +34 -0
  25. package/lib/abr/simple_abr_manager.js +1 -1
  26. package/lib/cea/mp4_cea_parser.js +30 -14
  27. package/lib/dash/dash_parser.js +18 -3
  28. package/lib/hls/hls_parser.js +13 -2
  29. package/lib/media/drm_engine.js +14 -8
  30. package/lib/media/gap_jumping_controller.js +24 -2
  31. package/lib/media/media_source_engine.js +5 -3
  32. package/lib/media/playhead.js +11 -0
  33. package/lib/media/region_observer.js +19 -1
  34. package/lib/media/streaming_engine.js +7 -3
  35. package/lib/media/video_wrapper.js +6 -1
  36. package/lib/player.js +9 -4
  37. package/lib/polyfill/abort_controller.js +7 -0
  38. package/lib/text/vtt_text_parser.js +2 -1
  39. package/lib/util/manifest_parser_utils.js +1 -1
  40. package/lib/util/mp4_box_parsers.js +8 -3
  41. package/lib/util/stream_utils.js +1 -0
  42. package/package.json +2 -3
  43. package/test/cea/mp4_cea_parser_unit.js +26 -3
  44. package/test/dash/dash_parser_manifest_unit.js +120 -0
  45. package/test/hls/hls_parser_unit.js +150 -0
  46. package/test/media/region_observer_unit.js +2 -1
  47. package/test/test/assets/multiple-trun-init.mp4 +0 -0
  48. package/test/test/assets/multiple-trun-segment.mp4 +0 -0
  49. package/ui/resolution_selection.js +7 -1
@@ -1066,6 +1066,7 @@ shaka.util.StreamUtils = class {
1066
1066
  track.frameRate = video.frameRate || null;
1067
1067
  track.pixelAspectRatio = video.pixelAspectRatio || null;
1068
1068
  track.videoBandwidth = video.bandwidth || null;
1069
+ track.hdr = video.hdr || null;
1069
1070
  }
1070
1071
 
1071
1072
  if (audio) {
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.2.11",
4
+ "version": "4.2.12",
5
5
  "homepage": "https://github.com/shaka-project/shaka-player",
6
6
  "author": "Google",
7
7
  "maintainers": [
@@ -42,7 +42,7 @@
42
42
  "karma-coverage": "^2.2.0",
43
43
  "karma-jasmine": "^4.0.1",
44
44
  "karma-jasmine-ajax": "^0.1.13",
45
- "karma-local-wd-launcher": "^1.6.1",
45
+ "karma-local-wd-launcher": "^1.6.2",
46
46
  "karma-opera-launcher": "^1.0.0",
47
47
  "karma-sourcemap-loader": "^0.3.8",
48
48
  "karma-spec-reporter": "^0.0.34",
@@ -67,7 +67,6 @@
67
67
  "demoDeps": [
68
68
  "awesomplete/awesomplete.css",
69
69
  "awesomplete/awesomplete.min.js",
70
- "codem-isoboxer/dist/iso_boxer.min.js",
71
70
  "dialog-polyfill/dialog-polyfill.css",
72
71
  "dialog-polyfill/dist/dialog-polyfill.js",
73
72
  "eme-encryption-scheme-polyfill/index.js",
@@ -7,20 +7,33 @@
7
7
  describe('Mp4CeaParser', () => {
8
8
  const ceaInitSegmentUri = '/base/test/test/assets/cea-init.mp4';
9
9
  const ceaSegmentUri = '/base/test/test/assets/cea-segment.mp4';
10
+ const multipleTrunInitSegmentUri =
11
+ '/base/test/test/assets/multiple-trun-init.mp4';
12
+ const multipleTrunSegmentUri =
13
+ '/base/test/test/assets/multiple-trun-segment.mp4';
10
14
  const Util = shaka.test.Util;
11
15
 
12
16
  /** @type {!ArrayBuffer} */
13
17
  let ceaInitSegment;
14
18
  /** @type {!ArrayBuffer} */
15
19
  let ceaSegment;
20
+ /** @type {!ArrayBuffer} */
21
+ let multipleTrunInitSegment;
22
+ /** @type {!ArrayBuffer} */
23
+ let multipleTrunSegment;
16
24
 
17
25
  beforeAll(async () => {
18
- const responses = await Promise.all([
26
+ [
27
+ ceaInitSegment,
28
+ ceaSegment,
29
+ multipleTrunInitSegment,
30
+ multipleTrunSegment,
31
+ ] = await Promise.all([
19
32
  shaka.test.Util.fetch(ceaInitSegmentUri),
20
33
  shaka.test.Util.fetch(ceaSegmentUri),
34
+ shaka.test.Util.fetch(multipleTrunInitSegmentUri),
35
+ shaka.test.Util.fetch(multipleTrunSegmentUri),
21
36
  ]);
22
- ceaInitSegment = responses[0];
23
- ceaSegment = responses[1];
24
37
  });
25
38
 
26
39
  /**
@@ -70,6 +83,16 @@ describe('Mp4CeaParser', () => {
70
83
  .toEqual(expectedCea708Packet);
71
84
  });
72
85
 
86
+ it('parses cea data from a segment with multiple trun boxes', () => {
87
+ const ceaParser = new shaka.cea.Mp4CeaParser();
88
+
89
+ ceaParser.init(multipleTrunInitSegment);
90
+ const ceaPackets = ceaParser.parse(multipleTrunSegment);
91
+ // The first trun box references samples with 48 CEA packets.
92
+ // The second trun box references samples with 132 more, for a total of 180.
93
+ expect(ceaPackets.length).toBe(180);
94
+ });
95
+
73
96
  it('parses an invalid init segment', () => {
74
97
  const cea708Parser = new shaka.cea.Mp4CeaParser();
75
98
 
@@ -1931,6 +1931,66 @@ describe('DashParser Manifest', () => {
1931
1931
  expect(stream.hdr).toBe('PQ');
1932
1932
  });
1933
1933
 
1934
+ it('supports SupplementalProperty MatrixCoefficients', async () => {
1935
+ // (DASH-IF IOP v4.3 6.2.5.1.)
1936
+ const scheme = cicpScheme('MatrixCoefficients');
1937
+ const manifestText = [
1938
+ '<MPD minBufferTime="PT75S">',
1939
+ ' <Period id="1" duration="PT30S">',
1940
+ ' <AdaptationSet id="2" mimeType="video/mp4">',
1941
+ ` <SupplementalProperty schemeIdUri="${scheme}" value="9" />`,
1942
+ ' <Representation codecs="hvc1.2.4.L153.B0">',
1943
+ ' <BaseURL>v-sd.mp4</BaseURL>',
1944
+ ' <SegmentBase indexRange="100-200" />',
1945
+ ' </Representation>',
1946
+ ' </AdaptationSet>',
1947
+ ' <AdaptationSet id="3" mimeType="audio/mp4">',
1948
+ ' <Representation id="audio-en">',
1949
+ ' <BaseURL>a-en.mp4</BaseURL>',
1950
+ ' <SegmentBase indexRange="100-200" />',
1951
+ ' </Representation>',
1952
+ ' </AdaptationSet>',
1953
+ ' </Period>',
1954
+ '</MPD>',
1955
+ ].join('\n');
1956
+
1957
+ fakeNetEngine.setResponseText('dummy://foo', manifestText);
1958
+
1959
+ /** @type {shaka.extern.Manifest} */
1960
+ const manifest = await parser.start('dummy://foo', playerInterface);
1961
+ expect(manifest.variants.length).toBe(1);
1962
+ });
1963
+
1964
+ it('supports SupplementalProperty ColourPrimaries', async () => {
1965
+ // (DASH-IF IOP v4.3 6.2.5.1.)
1966
+ const scheme = cicpScheme('ColourPrimaries');
1967
+ const manifestText = [
1968
+ '<MPD minBufferTime="PT75S">',
1969
+ ' <Period id="1" duration="PT30S">',
1970
+ ' <AdaptationSet id="2" mimeType="video/mp4">',
1971
+ ` <SupplementalProperty schemeIdUri="${scheme}" value="9" />`,
1972
+ ' <Representation codecs="hvc1.2.4.L153.B0">',
1973
+ ' <BaseURL>v-sd.mp4</BaseURL>',
1974
+ ' <SegmentBase indexRange="100-200" />',
1975
+ ' </Representation>',
1976
+ ' </AdaptationSet>',
1977
+ ' <AdaptationSet id="3" mimeType="audio/mp4">',
1978
+ ' <Representation id="audio-en">',
1979
+ ' <BaseURL>a-en.mp4</BaseURL>',
1980
+ ' <SegmentBase indexRange="100-200" />',
1981
+ ' </Representation>',
1982
+ ' </AdaptationSet>',
1983
+ ' </Period>',
1984
+ '</MPD>',
1985
+ ].join('\n');
1986
+
1987
+ fakeNetEngine.setResponseText('dummy://foo', manifestText);
1988
+
1989
+ /** @type {shaka.extern.Manifest} */
1990
+ const manifest = await parser.start('dummy://foo', playerInterface);
1991
+ expect(manifest.variants.length).toBe(1);
1992
+ });
1993
+
1934
1994
  it('supports HDR signaling via EssentialProperty', async () => {
1935
1995
  // (DASH-IF IOP v4.3 6.2.5.1.)
1936
1996
  const hdrScheme = cicpScheme('TransferCharacteristics');
@@ -1964,6 +2024,66 @@ describe('DashParser Manifest', () => {
1964
2024
  expect(stream.hdr).toBe('HLG');
1965
2025
  });
1966
2026
 
2027
+ it('supports EssentialProperty MatrixCoefficients', async () => {
2028
+ // (DASH-IF IOP v4.3 6.2.5.1.)
2029
+ const hdrScheme = cicpScheme('MatrixCoefficients');
2030
+ const manifestText = [
2031
+ '<MPD minBufferTime="PT75S">',
2032
+ ' <Period id="1" duration="PT30S">',
2033
+ ' <AdaptationSet id="2" mimeType="video/mp4">',
2034
+ ` <EssentialProperty schemeIdUri="${hdrScheme}" value="9" />`,
2035
+ ' <Representation codecs="hvc1.2.4.L153.B0">',
2036
+ ' <BaseURL>v-sd.mp4</BaseURL>',
2037
+ ' <SegmentBase indexRange="100-200" />',
2038
+ ' </Representation>',
2039
+ ' </AdaptationSet>',
2040
+ ' <AdaptationSet id="3" mimeType="audio/mp4">',
2041
+ ' <Representation id="audio-en">',
2042
+ ' <BaseURL>a-en.mp4</BaseURL>',
2043
+ ' <SegmentBase indexRange="100-200" />',
2044
+ ' </Representation>',
2045
+ ' </AdaptationSet>',
2046
+ ' </Period>',
2047
+ '</MPD>',
2048
+ ].join('\n');
2049
+
2050
+ fakeNetEngine.setResponseText('dummy://foo', manifestText);
2051
+
2052
+ /** @type {shaka.extern.Manifest} */
2053
+ const manifest = await parser.start('dummy://foo', playerInterface);
2054
+ expect(manifest.variants.length).toBe(1);
2055
+ });
2056
+
2057
+ it('supports EssentialProperty ColourPrimaries', async () => {
2058
+ // (DASH-IF IOP v4.3 6.2.5.1.)
2059
+ const hdrScheme = cicpScheme('ColourPrimaries');
2060
+ const manifestText = [
2061
+ '<MPD minBufferTime="PT75S">',
2062
+ ' <Period id="1" duration="PT30S">',
2063
+ ' <AdaptationSet id="2" mimeType="video/mp4">',
2064
+ ` <EssentialProperty schemeIdUri="${hdrScheme}" value="9" />`,
2065
+ ' <Representation codecs="hvc1.2.4.L153.B0">',
2066
+ ' <BaseURL>v-sd.mp4</BaseURL>',
2067
+ ' <SegmentBase indexRange="100-200" />',
2068
+ ' </Representation>',
2069
+ ' </AdaptationSet>',
2070
+ ' <AdaptationSet id="3" mimeType="audio/mp4">',
2071
+ ' <Representation id="audio-en">',
2072
+ ' <BaseURL>a-en.mp4</BaseURL>',
2073
+ ' <SegmentBase indexRange="100-200" />',
2074
+ ' </Representation>',
2075
+ ' </AdaptationSet>',
2076
+ ' </Period>',
2077
+ '</MPD>',
2078
+ ].join('\n');
2079
+
2080
+ fakeNetEngine.setResponseText('dummy://foo', manifestText);
2081
+
2082
+ /** @type {shaka.extern.Manifest} */
2083
+ const manifest = await parser.start('dummy://foo', playerInterface);
2084
+ expect(manifest.variants.length).toBe(1);
2085
+ });
2086
+
1967
2087
  it('supports SDR signalling via EssentialProperty', async () => {
1968
2088
  // (DASH-IF IOP v4.3 6.2.5.1.)
1969
2089
  const scheme = cicpScheme('TransferCharacteristics');
@@ -983,6 +983,81 @@ describe('HlsParser', () => {
983
983
  await testHlsParser(master, media, manifest);
984
984
  });
985
985
 
986
+ it('adds subtitle role when characteristics are empty', async () => {
987
+ const master = [
988
+ '#EXTM3U\n',
989
+ '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1",',
990
+ 'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1",SUBTITLES="sub1"\n',
991
+ 'video\n',
992
+
993
+ '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",',
994
+ 'NAME="English",URI="audio"\n',
995
+
996
+ '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="en",',
997
+ 'CHARACTERISTICS="public.accessibility.describes-video,',
998
+ 'public.accessibility.describes-music-and-sound",',
999
+ 'NAME="English (describes-video)",URI="audio2"\n',
1000
+
1001
+ '#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="en",',
1002
+ 'NAME="English (subtitle)",DEFAULT=YES,AUTOSELECT=YES,',
1003
+ 'URI="text"\n',
1004
+
1005
+ '#EXT-X-MEDIA:TYPE=SUBTITLES,GROUP-ID="sub1",LANGUAGE="en",',
1006
+ 'NAME="English (caption)",DEFAULT=YES,AUTOSELECT=YES,',
1007
+ 'CHARACTERISTICS="public.accessibility.describes-music-and-sound",',
1008
+ 'URI="text2"\n',
1009
+ ].join('');
1010
+
1011
+ const media = [
1012
+ '#EXTM3U\n',
1013
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1014
+ '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
1015
+ '#EXTINF:5,\n',
1016
+ '#EXT-X-BYTERANGE:121090@616\n',
1017
+ 'main.mp4',
1018
+ ].join('');
1019
+
1020
+ const manifest = shaka.test.ManifestGenerator.generate((manifest) => {
1021
+ manifest.anyTimeline();
1022
+ manifest.addPartialVariant((variant) => {
1023
+ variant.language = 'en';
1024
+ variant.addPartialStream(ContentType.VIDEO);
1025
+ variant.addPartialStream(ContentType.AUDIO, (stream) => {
1026
+ stream.language = 'en';
1027
+ stream.roles = [];
1028
+ });
1029
+ });
1030
+ manifest.addPartialVariant((variant) => {
1031
+ variant.language = 'en';
1032
+ variant.addPartialStream(ContentType.VIDEO);
1033
+ variant.addPartialStream(ContentType.AUDIO, (stream) => {
1034
+ stream.language = 'en';
1035
+ stream.roles = [
1036
+ 'public.accessibility.describes-video',
1037
+ 'public.accessibility.describes-music-and-sound',
1038
+ ];
1039
+ });
1040
+ });
1041
+ manifest.addPartialTextStream((stream) => {
1042
+ stream.language = 'en';
1043
+ stream.kind = TextStreamKind.SUBTITLE;
1044
+ stream.roles = [
1045
+ 'subtitle',
1046
+ ];
1047
+ });
1048
+ manifest.addPartialTextStream((stream) => {
1049
+ stream.language = 'en';
1050
+ stream.kind = TextStreamKind.SUBTITLE;
1051
+ stream.roles = [
1052
+ 'public.accessibility.describes-music-and-sound',
1053
+ ];
1054
+ });
1055
+ manifest.sequenceMode = true;
1056
+ });
1057
+
1058
+ await testHlsParser(master, media, manifest);
1059
+ });
1060
+
986
1061
  it('parses characteristics from text tags', async () => {
987
1062
  const master = [
988
1063
  '#EXTM3U\n',
@@ -1652,6 +1727,81 @@ describe('HlsParser', () => {
1652
1727
  }
1653
1728
  });
1654
1729
 
1730
+ it('ignore segments with #EXTINF:0', async () => {
1731
+ const master = [
1732
+ '#EXTM3U\n',
1733
+ '#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aud1",LANGUAGE="eng",',
1734
+ 'CHANNELS="2",URI="audio"\n',
1735
+ '#EXT-X-STREAM-INF:BANDWIDTH=200,CODECS="avc1,mp4a",',
1736
+ 'RESOLUTION=960x540,FRAME-RATE=60,AUDIO="aud1"\n',
1737
+ 'video\n',
1738
+ ].join('');
1739
+
1740
+ const video = [
1741
+ '#EXTM3U\n',
1742
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1743
+ '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
1744
+ '#EXTINF:5,\n',
1745
+ 'main.mp4\n',
1746
+ '#EXTINF:0,\n',
1747
+ 'main.mp4\n',
1748
+ '#EXTINF:5,\n',
1749
+ 'main.mp4\n',
1750
+ ].join('');
1751
+
1752
+ const audio = [
1753
+ '#EXTM3U\n',
1754
+ '#EXT-X-PLAYLIST-TYPE:VOD\n',
1755
+ '#EXT-X-MAP:URI="init.mp4",BYTERANGE="616@0"\n',
1756
+ '#EXTINF:5,\n',
1757
+ 'main.mp4\n',
1758
+ '#EXTINF:0,\n',
1759
+ 'main.mp4\n',
1760
+ '#EXTINF:5,\n',
1761
+ 'main.mp4\n',
1762
+ ].join('');
1763
+
1764
+ fakeNetEngine
1765
+ .setResponseText('test:/master', master)
1766
+ .setResponseText('test:/audio', audio)
1767
+ .setResponseText('test:/video', video)
1768
+ .setResponseValue('test:/init.mp4', initSegmentData)
1769
+ .setResponseValue('test:/main.mp4', segmentData);
1770
+
1771
+ const actual = await parser.start('test:/master', playerInterface);
1772
+
1773
+ expect(actual.variants.length).toBe(1);
1774
+
1775
+ const variant = actual.variants[0];
1776
+ expect(variant.video).toBeTruthy();
1777
+ expect(variant.audio).toBeTruthy();
1778
+
1779
+ await variant.video.createSegmentIndex();
1780
+ goog.asserts.assert(variant.video.segmentIndex != null,
1781
+ 'Null segmentIndex!');
1782
+
1783
+ const firstVideoReference = variant.video.segmentIndex.get(0);
1784
+ const secondVideoReference = variant.video.segmentIndex.get(1);
1785
+ const thirdVideoReference = variant.video.segmentIndex.get(2);
1786
+
1787
+ expect(firstVideoReference).not.toBe(null);
1788
+ expect(secondVideoReference).not.toBe(null);
1789
+ expect(thirdVideoReference).toBe(null);
1790
+
1791
+
1792
+ await variant.audio.createSegmentIndex();
1793
+ goog.asserts.assert(variant.audio.segmentIndex != null,
1794
+ 'Null segmentIndex!');
1795
+
1796
+ const firstAudioReference = variant.audio.segmentIndex.get(0);
1797
+ const secondAudioReference = variant.audio.segmentIndex.get(1);
1798
+ const thirdAudioReference = variant.audio.segmentIndex.get(2);
1799
+
1800
+ expect(firstAudioReference).not.toBe(null);
1801
+ expect(secondAudioReference).not.toBe(null);
1802
+ expect(thirdAudioReference).toBe(null);
1803
+ });
1804
+
1655
1805
  it('Disable audio does not create audio streams', async () => {
1656
1806
  const master = [
1657
1807
  '#EXTM3U\n',
@@ -31,7 +31,8 @@ describe('RegionObserver', () => {
31
31
  timeline = new shaka.media.RegionTimeline(
32
32
  shaka.test.Util.spyFunc(onSeekRange));
33
33
 
34
- observer = new shaka.media.RegionObserver(timeline);
34
+ observer = new shaka.media.RegionObserver(
35
+ timeline, /* startsPastZero= */ false);
35
36
  observer.addEventListener('enter', (event) => {
36
37
  shaka.test.Util.spyFunc(onEnterRegion)(event['region'], event['seeking']);
37
38
  });
@@ -8,6 +8,7 @@
8
8
  goog.provide('shaka.ui.ResolutionSelection');
9
9
 
10
10
  goog.require('goog.asserts');
11
+ goog.require('shaka.Player');
11
12
  goog.require('shaka.ui.Controls');
12
13
  goog.require('shaka.ui.Enums');
13
14
  goog.require('shaka.ui.Locales');
@@ -67,7 +68,12 @@ shaka.ui.ResolutionSelection = class extends shaka.ui.SettingsMenu {
67
68
  /** @private */
68
69
  updateResolutionSelection_() {
69
70
  /** @type {!Array.<shaka.extern.Track>} */
70
- let tracks = this.player.getVariantTracks();
71
+ let tracks = [];
72
+ // When played with src=, the variant tracks available from
73
+ // player.getVariantTracks() represent languages, not resolutions.
74
+ if (this.player.getLoadMode() != shaka.Player.LoadMode.SRC_EQUALS) {
75
+ tracks = this.player.getVariantTracks();
76
+ }
71
77
 
72
78
  // If there is a selected variant track, then we filter out any tracks in
73
79
  // a different language. Then we use those remaining tracks to display the