shaka-player 4.5.0 → 4.6.1

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 (214) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +1 -1
  2. package/AUTHORS +2 -0
  3. package/CHANGELOG.md +109 -0
  4. package/CODE_OF_CONDUCT.md +1 -1
  5. package/CONTRIBUTORS +2 -0
  6. package/README.md +38 -32
  7. package/demo/common/asset.js +11 -17
  8. package/demo/common/assets.js +117 -11
  9. package/demo/config.js +28 -1
  10. package/demo/custom.js +52 -38
  11. package/demo/main.js +11 -3
  12. package/demo/search.js +7 -0
  13. package/dist/controls.css +1 -1
  14. package/dist/controls.css.map +1 -1
  15. package/dist/demo.compiled.debug.js +133 -124
  16. package/dist/demo.compiled.debug.map +3 -3
  17. package/dist/demo.compiled.js +133 -124
  18. package/dist/demo.compiled.map +3 -3
  19. package/dist/deps.js +20 -18
  20. package/dist/locales.js +34 -30
  21. package/dist/receiver.compiled.debug.js +58 -50
  22. package/dist/receiver.compiled.debug.map +3 -3
  23. package/dist/receiver.compiled.js +58 -50
  24. package/dist/receiver.compiled.map +3 -3
  25. package/dist/shaka-player.compiled.d.ts +440 -390
  26. package/dist/shaka-player.compiled.debug.d.ts +440 -390
  27. package/dist/shaka-player.compiled.debug.externs.js +162 -77
  28. package/dist/shaka-player.compiled.debug.js +789 -738
  29. package/dist/shaka-player.compiled.debug.map +4 -4
  30. package/dist/shaka-player.compiled.externs.js +162 -77
  31. package/dist/shaka-player.compiled.js +1147 -1113
  32. package/dist/shaka-player.compiled.map +4 -4
  33. package/dist/shaka-player.ui.d.ts +491 -437
  34. package/dist/shaka-player.ui.debug.d.ts +491 -437
  35. package/dist/shaka-player.ui.debug.externs.js +167 -77
  36. package/dist/shaka-player.ui.debug.js +857 -791
  37. package/dist/shaka-player.ui.debug.map +4 -4
  38. package/dist/shaka-player.ui.externs.js +167 -77
  39. package/dist/shaka-player.ui.js +1359 -1314
  40. package/dist/shaka-player.ui.map +4 -4
  41. package/docs/tutorials/ad_monetization.md +54 -0
  42. package/docs/tutorials/basic-usage.md +2 -1
  43. package/docs/tutorials/errors.md +2 -1
  44. package/docs/tutorials/offline.md +10 -8
  45. package/docs/tutorials/plugins.md +5 -0
  46. package/docs/tutorials/ui-customization.md +13 -0
  47. package/docs/tutorials/ui.md +2 -1
  48. package/docs/tutorials/upgrade.md +3 -0
  49. package/externs/managedmediasource.js +24 -0
  50. package/externs/shaka/abr_manager.js +6 -2
  51. package/externs/shaka/cea.js +7 -0
  52. package/externs/shaka/codecs.js +5 -3
  53. package/externs/shaka/manifest.js +21 -5
  54. package/externs/shaka/manifest_parser.js +17 -0
  55. package/externs/shaka/net.js +2 -2
  56. package/externs/shaka/offline.js +9 -2
  57. package/externs/shaka/player.js +53 -7
  58. package/externs/shaka/text.js +3 -1
  59. package/externs/shaka/transmuxer.js +1 -1
  60. package/lib/abr/simple_abr_manager.js +25 -8
  61. package/lib/ads/media_tailor_ad_manager.js +14 -3
  62. package/lib/cast/cast_receiver.js +1 -1
  63. package/lib/cea/cea_decoder.js +28 -0
  64. package/lib/cea/dummy_caption_decoder.js +5 -0
  65. package/lib/dash/dash_parser.js +251 -84
  66. package/lib/dash/mpd_utils.js +52 -31
  67. package/lib/dash/segment_base.js +4 -5
  68. package/lib/dash/segment_list.js +5 -4
  69. package/lib/dash/segment_template.js +92 -29
  70. package/lib/hls/hls_classes.js +28 -43
  71. package/lib/hls/hls_parser.js +770 -652
  72. package/lib/hls/hls_utils.js +64 -5
  73. package/lib/hls/manifest_text_parser.js +7 -11
  74. package/lib/media/adaptation_set.js +20 -13
  75. package/lib/media/adaptation_set_criteria.js +70 -18
  76. package/lib/media/closed_caption_parser.js +13 -0
  77. package/lib/media/content_workarounds.js +6 -0
  78. package/lib/media/drm_engine.js +8 -13
  79. package/lib/media/manifest_parser.js +8 -113
  80. package/lib/media/media_source_capabilities.js +10 -3
  81. package/lib/media/media_source_engine.js +75 -21
  82. package/lib/media/play_rate_controller.js +8 -38
  83. package/lib/media/presentation_timeline.js +7 -1
  84. package/lib/media/quality_observer.js +1 -1
  85. package/lib/media/segment_index.js +5 -5
  86. package/lib/media/segment_prefetch.js +92 -24
  87. package/lib/media/segment_reference.js +67 -1
  88. package/lib/media/segment_utils.js +343 -0
  89. package/lib/media/stall_detector.js +0 -3
  90. package/lib/media/streaming_engine.js +72 -44
  91. package/lib/mss/mss_parser.js +18 -2
  92. package/lib/net/networking_engine.js +66 -12
  93. package/lib/net/networking_utils.js +95 -0
  94. package/lib/offline/indexeddb/v1_storage_cell.js +2 -0
  95. package/lib/offline/indexeddb/v2_storage_cell.js +2 -0
  96. package/lib/offline/manifest_converter.js +2 -0
  97. package/lib/offline/offline_manifest_parser.js +10 -0
  98. package/lib/offline/storage.js +11 -7
  99. package/lib/player.js +998 -1503
  100. package/lib/polyfill/mediasource.js +22 -1
  101. package/lib/text/mp4_ttml_parser.js +2 -2
  102. package/lib/text/srt_text_parser.js +2 -2
  103. package/lib/text/stub_text_displayer.js +51 -0
  104. package/lib/text/text_engine.js +3 -2
  105. package/lib/text/ttml_text_parser.js +28 -5
  106. package/lib/text/ui_text_displayer.js +4 -0
  107. package/lib/text/vtt_text_parser.js +7 -2
  108. package/lib/transmuxer/aac_transmuxer.js +1 -1
  109. package/lib/transmuxer/ac3_transmuxer.js +1 -1
  110. package/lib/transmuxer/ec3_transmuxer.js +1 -1
  111. package/lib/transmuxer/h264.js +5 -4
  112. package/lib/transmuxer/h265.js +5 -4
  113. package/lib/transmuxer/mp3_transmuxer.js +1 -1
  114. package/lib/transmuxer/mpeg_ts_transmuxer.js +1 -1
  115. package/lib/transmuxer/mss_transmuxer.js +1 -1
  116. package/lib/transmuxer/ts_transmuxer.js +33 -13
  117. package/lib/util/buffer_utils.js +26 -4
  118. package/lib/util/content_steering_manager.js +358 -0
  119. package/lib/util/error.js +2 -6
  120. package/lib/util/fairplay_utils.js +1 -1
  121. package/lib/util/fake_event.js +5 -1
  122. package/lib/util/id3_utils.js +50 -1
  123. package/lib/util/manifest_parser_utils.js +12 -8
  124. package/lib/util/mp4_generator.js +2 -4
  125. package/lib/util/mutex.js +59 -0
  126. package/lib/util/object_utils.js +1 -1
  127. package/lib/util/periods.js +154 -179
  128. package/lib/util/platform.js +4 -3
  129. package/lib/util/player_configuration.js +35 -6
  130. package/lib/util/stream_utils.js +105 -98
  131. package/lib/util/ts_parser.js +124 -73
  132. package/lib/util/uint8array_utils.js +5 -1
  133. package/lib/util/xml_utils.js +5 -0
  134. package/maintained-branches.md +2 -2
  135. package/package.json +1 -1
  136. package/roadmap.md +15 -5
  137. package/shaka-player.uncompiled.js +0 -1
  138. package/test/abr/simple_abr_manager_unit.js +33 -33
  139. package/test/ads/ad_manager_unit.js +3 -2
  140. package/test/cast/cast_receiver_integration.js +3 -4
  141. package/test/cast/cast_utils_unit.js +2 -0
  142. package/test/cea/cea_decoder_unit.js +7 -0
  143. package/test/codec_switching/codec_switching_integration.js +2 -1
  144. package/test/dash/dash_parser_integration.js +2 -1
  145. package/test/dash/dash_parser_live_unit.js +0 -19
  146. package/test/dash/dash_parser_manifest_unit.js +494 -2
  147. package/test/dash/mpd_utils_unit.js +61 -30
  148. package/test/hls/hls_live_unit.js +12 -3
  149. package/test/hls/hls_parser_unit.js +417 -27
  150. package/test/hls/manifest_text_parser_unit.js +32 -99
  151. package/test/media/adaptation_set_criteria_unit.js +172 -21
  152. package/test/media/adaptation_set_unit.js +1 -0
  153. package/test/media/content_workarounds_unit.js +1 -1
  154. package/test/media/drm_engine_unit.js +3 -3
  155. package/test/media/media_source_engine_unit.js +1 -1
  156. package/test/media/play_rate_controller_unit.js +0 -48
  157. package/test/media/segment_index_unit.js +2 -1
  158. package/test/mss/mss_player_integration.js +3 -2
  159. package/test/net/networking_engine_unit.js +3 -3
  160. package/test/{media/manifest_parser_unit.js → net/networking_utils_unit.js} +2 -2
  161. package/test/offline/manifest_convert_unit.js +9 -0
  162. package/test/offline/offline_integration.js +2 -1
  163. package/test/offline/storage_integration.js +21 -9
  164. package/test/player_external.js +4 -2
  165. package/test/player_integration.js +15 -4
  166. package/test/player_load_graph_integration.js +47 -151
  167. package/test/player_src_equals_external.js +1 -0
  168. package/test/player_src_equals_integration.js +71 -4
  169. package/test/player_unit.js +80 -27
  170. package/test/test/util/fake_media_source_engine.js +5 -0
  171. package/test/test/util/fake_networking_engine.js +9 -1
  172. package/test/test/util/fake_segment_prefetch.js +3 -0
  173. package/test/test/util/manifest_generator.js +4 -0
  174. package/test/test/util/manifest_parser_util.js +14 -2
  175. package/test/test/util/offline_utils.js +1 -0
  176. package/test/test/util/simple_fakes.js +2 -2
  177. package/test/test/util/streaming_engine_util.js +3 -0
  178. package/test/test/util/test_scheme.js +6 -0
  179. package/test/test/util/ui_utils.js +4 -3
  180. package/test/text/mp4_ttml_parser_unit.js +4 -4
  181. package/test/text/srt_text_parser_unit.js +1 -1
  182. package/test/text/text_displayer_layout_unit.js +1 -1
  183. package/test/text/text_engine_unit.js +6 -0
  184. package/test/text/ttml_text_parser_unit.js +33 -2
  185. package/test/transmuxer/transmuxer_engine_integration.js +9 -0
  186. package/test/transmuxer/transmuxer_integration.js +33 -3
  187. package/test/ui/ad_ui_unit.js +2 -2
  188. package/test/ui/ui_customization_unit.js +17 -16
  189. package/test/ui/ui_integration.js +30 -20
  190. package/test/ui/ui_unit.js +25 -19
  191. package/test/util/buffer_utils_unit.js +1 -1
  192. package/test/util/content_steering_manager_unit.js +427 -0
  193. package/test/util/id3_utils_unit.js +52 -0
  194. package/test/util/stream_utils_unit.js +17 -113
  195. package/test/util/ts_parser_unit.js +3 -3
  196. package/third_party/closure-uri/uri.js +13 -1
  197. package/ui/audio_language_selection.js +9 -2
  198. package/ui/externs/ui.js +17 -2
  199. package/ui/language_utils.js +43 -8
  200. package/ui/less/range_elements.less +33 -0
  201. package/ui/locales/en.json +1 -0
  202. package/ui/locales/es-419.json +1 -0
  203. package/ui/locales/es.json +1 -0
  204. package/ui/locales/source.json +4 -0
  205. package/ui/localization.js +10 -0
  206. package/ui/remote_button.js +3 -1
  207. package/ui/resolution_selection.js +47 -10
  208. package/ui/seek_bar.js +296 -3
  209. package/ui/text_selection.js +2 -1
  210. package/ui/ui.js +6 -2
  211. package/lib/routing/node.js +0 -23
  212. package/lib/routing/payload.js +0 -47
  213. package/lib/routing/walker.js +0 -587
  214. package/test/routing/walker_unit.js +0 -505
@@ -6,6 +6,8 @@
6
6
 
7
7
  goog.provide('shaka.hls.Utils');
8
8
 
9
+ goog.require('shaka.log');
10
+ goog.require('shaka.util.Error');
9
11
  goog.require('shaka.util.ManifestParserUtils');
10
12
  goog.requireType('shaka.hls.Tag');
11
13
 
@@ -68,17 +70,74 @@ shaka.hls.Utils = class {
68
70
  return value;
69
71
  }
70
72
 
73
+ /**
74
+ * @param {!Array.<string>} baseUris
75
+ * @param {?string} relativeUri
76
+ * @param {?Map.<string, string>=} variables
77
+ * @return {!Array.<string>}
78
+ */
79
+ static constructSegmentUris(baseUris, relativeUri, variables) {
80
+ if (!relativeUri) {
81
+ return [];
82
+ }
83
+ return shaka.hls.Utils.constructUris(baseUris, [relativeUri], variables);
84
+ }
85
+
86
+
87
+ /**
88
+ * @param {!Array.<string>} baseUris
89
+ * @param {!Array.<string>} relativeUris
90
+ * @param {?Map.<string, string>=} variables
91
+ * @return {!Array.<string>}
92
+ */
93
+ static constructUris(baseUris, relativeUris, variables) {
94
+ if (!relativeUris.length) {
95
+ return [];
96
+ }
97
+ const resolvedUris = shaka.util.ManifestParserUtils.resolveUris(
98
+ baseUris, relativeUris);
99
+
100
+ const uris = [];
101
+ for (const uri of resolvedUris) {
102
+ uris.push(shaka.hls.Utils.variableSubstitution(uri, variables));
103
+ }
104
+
105
+ return uris;
106
+ }
71
107
 
72
108
  /**
73
- * @param {string} parentAbsoluteUri
109
+ * Replaces the variables of a given URI.
110
+ *
74
111
  * @param {string} uri
112
+ * @param {?Map.<string, string>=} variables
75
113
  * @return {string}
76
114
  */
77
- static constructAbsoluteUri(parentAbsoluteUri, uri) {
78
- const uris = shaka.util.ManifestParserUtils.resolveUris(
79
- [parentAbsoluteUri], [uri]);
115
+ static variableSubstitution(uri, variables) {
116
+ if (!variables || !variables.size) {
117
+ return uri;
118
+ }
119
+ let newUri = String(uri).replace(/%7B/g, '{').replace(/%7D/g, '}');
80
120
 
81
- return uris[0];
121
+ const uriVariables = newUri.match(/{\$\w*}/g);
122
+ if (uriVariables) {
123
+ for (const variable of uriVariables) {
124
+ // Note: All variables have the structure {$...}
125
+ const variableName = variable.slice(2, variable.length - 1);
126
+ const replaceValue = variables.get(variableName);
127
+ if (replaceValue) {
128
+ newUri = newUri.replace(variable, replaceValue);
129
+ } else {
130
+ shaka.log.error('A variable has been found that is not declared',
131
+ variableName);
132
+ throw new shaka.util.Error(
133
+ shaka.util.Error.Severity.CRITICAL,
134
+ shaka.util.Error.Category.MANIFEST,
135
+ shaka.util.Error.Code.HLS_VARIABLE_NOT_FOUND,
136
+ variableName);
137
+ }
138
+ }
139
+ }
140
+ return newUri;
82
141
  }
83
142
 
84
143
 
@@ -29,10 +29,9 @@ shaka.hls.ManifestTextParser = class {
29
29
 
30
30
  /**
31
31
  * @param {BufferSource} data
32
- * @param {string} absolutePlaylistUri An absolute, final URI after redirects.
33
32
  * @return {!shaka.hls.Playlist}
34
33
  */
35
- parsePlaylist(data, absolutePlaylistUri) {
34
+ parsePlaylist(data) {
36
35
  const MEDIA_PLAYLIST_TAGS =
37
36
  shaka.hls.ManifestTextParser.MEDIA_PLAYLIST_TAGS;
38
37
  const SEGMENT_TAGS = shaka.hls.ManifestTextParser.SEGMENT_TAGS;
@@ -101,10 +100,8 @@ shaka.hls.ManifestTextParser = class {
101
100
  }
102
101
 
103
102
  const segmentsData = lines.splice(i, lines.length - i);
104
- const segments = this.parseSegments_(
105
- absolutePlaylistUri, segmentsData, tags);
106
- return new shaka.hls.Playlist(
107
- absolutePlaylistUri, playlistType, tags, segments);
103
+ const segments = this.parseSegments_(segmentsData, tags);
104
+ return new shaka.hls.Playlist(playlistType, tags, segments);
108
105
  }
109
106
 
110
107
  tags.push(tag);
@@ -118,19 +115,18 @@ shaka.hls.ManifestTextParser = class {
118
115
  }
119
116
  }
120
117
 
121
- return new shaka.hls.Playlist(absolutePlaylistUri, playlistType, tags);
118
+ return new shaka.hls.Playlist(playlistType, tags);
122
119
  }
123
120
 
124
121
  /**
125
122
  * Parses an array of strings into an array of HLS Segment objects.
126
123
  *
127
- * @param {string} absoluteMediaPlaylistUri
128
124
  * @param {!Array.<string>} lines
129
125
  * @param {!Array.<!shaka.hls.Tag>} playlistTags
130
126
  * @return {!Array.<shaka.hls.Segment>}
131
127
  * @private
132
128
  */
133
- parseSegments_(absoluteMediaPlaylistUri, lines, playlistTags) {
129
+ parseSegments_(lines, playlistTags) {
134
130
  /** @type {!Array.<shaka.hls.Segment>} */
135
131
  const segments = [];
136
132
  /** @type {!Array.<shaka.hls.Tag>} */
@@ -177,7 +173,7 @@ shaka.hls.ManifestTextParser = class {
177
173
  segmentTags.push(currentMapTag);
178
174
  }
179
175
  // The URI appears after all of the tags describing the segment.
180
- const segment = new shaka.hls.Segment(absoluteMediaPlaylistUri,
176
+ const segment = new shaka.hls.Segment(
181
177
  verbatimSegmentUri, segmentTags, partialSegmentTags);
182
178
  segments.push(segment);
183
179
  segmentTags = [];
@@ -193,7 +189,7 @@ shaka.hls.ManifestTextParser = class {
193
189
  if (currentMapTag) {
194
190
  segmentTags.push(currentMapTag);
195
191
  }
196
- const segment = new shaka.hls.Segment('', '', segmentTags,
192
+ const segment = new shaka.hls.Segment('', segmentTags,
197
193
  partialSegmentTags);
198
194
  segments.push(segment);
199
195
  }
@@ -26,8 +26,11 @@ shaka.media.AdaptationSet = class {
26
26
  * Variants that may be compatible with the root and should be added if
27
27
  * compatible. If a candidate is not compatible, it will not end up in the
28
28
  * adaptation set.
29
+ * @param {boolean=} compareCodecs
30
+ * @param {boolean=} enableAudioGroups
29
31
  */
30
- constructor(root, candidates) {
32
+ constructor(root, candidates, compareCodecs = true,
33
+ enableAudioGroups = false) {
31
34
  /** @private {shaka.extern.Variant} */
32
35
  this.root_ = root;
33
36
  /** @private {!Set.<shaka.extern.Variant>} */
@@ -37,17 +40,18 @@ shaka.media.AdaptationSet = class {
37
40
  // are not compatible with the root, they will be rejected by |add|.
38
41
  candidates = candidates || [];
39
42
  for (const candidate of candidates) {
40
- this.add(candidate);
43
+ this.add(candidate, compareCodecs, enableAudioGroups);
41
44
  }
42
45
  }
43
46
 
44
47
  /**
45
48
  * @param {shaka.extern.Variant} variant
46
- * @param {boolean=} compareCodecs
49
+ * @param {boolean} compareCodecs
50
+ * @param {boolean} enableAudioGroups
47
51
  * @return {boolean}
48
52
  */
49
- add(variant, compareCodecs = true) {
50
- if (this.canInclude(variant, compareCodecs)) {
53
+ add(variant, compareCodecs, enableAudioGroups) {
54
+ if (this.canInclude(variant, compareCodecs, enableAudioGroups)) {
51
55
  this.variants_.add(variant);
52
56
  return true;
53
57
  }
@@ -64,20 +68,22 @@ shaka.media.AdaptationSet = class {
64
68
  *
65
69
  * @param {shaka.extern.Variant} variant
66
70
  * @param {boolean=} compareCodecs
71
+ * @param {boolean=} enableAudioGroups
67
72
  * @return {boolean}
68
73
  */
69
- canInclude(variant, compareCodecs = true) {
74
+ canInclude(variant, compareCodecs = true, enableAudioGroups = false) {
70
75
  return shaka.media.AdaptationSet
71
- .areAdaptable(this.root_, variant, compareCodecs);
76
+ .areAdaptable(this.root_, variant, compareCodecs, enableAudioGroups);
72
77
  }
73
78
 
74
79
  /**
75
80
  * @param {shaka.extern.Variant} a
76
81
  * @param {shaka.extern.Variant} b
77
- * @param {boolean=} compareCodecs
82
+ * @param {boolean} compareCodecs
83
+ * @param {boolean} enableAudioGroups
78
84
  * @return {boolean}
79
85
  */
80
- static areAdaptable(a, b, compareCodecs = true) {
86
+ static areAdaptable(a, b, compareCodecs, enableAudioGroups) {
81
87
  const AdaptationSet = shaka.media.AdaptationSet;
82
88
 
83
89
  // All variants should have audio or should all not have audio.
@@ -98,8 +104,8 @@ shaka.media.AdaptationSet = class {
98
104
  goog.asserts.assert(
99
105
  !!a.audio == !!b.audio,
100
106
  'Both should either have audio or not have audio.');
101
- if (a.audio && b.audio &&
102
- !AdaptationSet.areAudiosCompatible_(a.audio, b.audio, compareCodecs)) {
107
+ if (a.audio && b.audio && !AdaptationSet.areAudiosCompatible_(
108
+ a.audio, b.audio, compareCodecs, enableAudioGroups)) {
103
109
  return false;
104
110
  }
105
111
 
@@ -127,10 +133,11 @@ shaka.media.AdaptationSet = class {
127
133
  * @param {shaka.extern.Stream} a
128
134
  * @param {shaka.extern.Stream} b
129
135
  * @param {boolean} compareCodecs
136
+ * @param {boolean} enableAudioGroups
130
137
  * @return {boolean}
131
138
  * @private
132
139
  */
133
- static areAudiosCompatible_(a, b, compareCodecs) {
140
+ static areAudiosCompatible_(a, b, compareCodecs, enableAudioGroups) {
134
141
  const AdaptationSet = shaka.media.AdaptationSet;
135
142
 
136
143
  // Don't adapt between channel counts, which could annoy the user
@@ -154,7 +161,7 @@ shaka.media.AdaptationSet = class {
154
161
  }
155
162
 
156
163
  // We can only adapt between the same groupId.
157
- if (a.groupId !== b.groupId) {
164
+ if (enableAudioGroups && a.groupId !== b.groupId) {
158
165
  return false;
159
166
  }
160
167
 
@@ -13,7 +13,6 @@ goog.require('shaka.log');
13
13
  goog.require('shaka.media.AdaptationSet');
14
14
  goog.require('shaka.media.Capabilities');
15
15
  goog.require('shaka.util.LanguageUtils');
16
- goog.require('shaka.util.StreamUtils');
17
16
 
18
17
 
19
18
  /**
@@ -43,27 +42,33 @@ shaka.media.ExampleBasedCriteria = class {
43
42
  /**
44
43
  * @param {shaka.extern.Variant} example
45
44
  * @param {shaka.config.CodecSwitchingStrategy=} codecSwitchingStrategy
45
+ * @param {boolean=} enableAudioGroups
46
46
  */
47
47
  constructor(example,
48
- codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD) {
48
+ codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD,
49
+ enableAudioGroups = false) {
49
50
  /** @private {shaka.extern.Variant} */
50
51
  this.example_ = example;
51
52
  /** @private {shaka.config.CodecSwitchingStrategy} */
52
53
  this.codecSwitchingStrategy_ = codecSwitchingStrategy;
54
+ /** @private {boolean} */
55
+ this.enableAudioGroups_ = enableAudioGroups;
56
+
53
57
 
54
58
  // We can't know if role and label are really important, so we don't use
55
59
  // role and label for this.
56
60
  const role = '';
57
61
  const label = '';
58
62
  const hdrLevel = '';
63
+ const videoLayout = '';
59
64
  const channelCount = example.audio && example.audio.channelsCount ?
60
65
  example.audio.channelsCount :
61
66
  0;
62
67
 
63
68
  /** @private {!shaka.media.AdaptationSetCriteria} */
64
69
  this.fallback_ = new shaka.media.PreferenceBasedCriteria(
65
- example.language, role, channelCount, hdrLevel, label,
66
- codecSwitchingStrategy);
70
+ example.language, role, channelCount, hdrLevel, videoLayout, label,
71
+ codecSwitchingStrategy, enableAudioGroups);
67
72
  }
68
73
 
69
74
  /** @override */
@@ -75,14 +80,15 @@ shaka.media.ExampleBasedCriteria = class {
75
80
  // actually be from another period.
76
81
  const shortList = variants.filter((variant) => {
77
82
  return shaka.media.AdaptationSet.areAdaptable(this.example_, variant,
78
- !supportsSmoothCodecTransitions);
83
+ !supportsSmoothCodecTransitions, this.enableAudioGroups_);
79
84
  });
80
85
 
81
86
  if (shortList.length) {
82
87
  // Use the first item in the short list as the root. It should not matter
83
88
  // which element we use as all items in the short list should already be
84
89
  // compatible.
85
- return new shaka.media.AdaptationSet(shortList[0], shortList);
90
+ return new shaka.media.AdaptationSet(shortList[0], shortList,
91
+ !supportsSmoothCodecTransitions, this.enableAudioGroups_);
86
92
  } else {
87
93
  return this.fallback_.create(variants);
88
94
  }
@@ -100,11 +106,14 @@ shaka.media.PreferenceBasedCriteria = class {
100
106
  * @param {string} role
101
107
  * @param {number} channelCount
102
108
  * @param {string} hdrLevel
109
+ * @param {string} videoLayout
103
110
  * @param {string=} label
104
111
  * @param {shaka.config.CodecSwitchingStrategy=} codecSwitchingStrategy
112
+ * @param {boolean=} enableAudioGroups
105
113
  */
106
- constructor(language, role, channelCount, hdrLevel, label = '',
107
- codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD) {
114
+ constructor(language, role, channelCount, hdrLevel, videoLayout, label = '',
115
+ codecSwitchingStrategy = shaka.config.CodecSwitchingStrategy.RELOAD,
116
+ enableAudioGroups = false) {
108
117
  /** @private {string} */
109
118
  this.language_ = language;
110
119
  /** @private {string} */
@@ -114,15 +123,18 @@ shaka.media.PreferenceBasedCriteria = class {
114
123
  /** @private {string} */
115
124
  this.hdrLevel_ = hdrLevel;
116
125
  /** @private {string} */
126
+ this.videoLayout_ = videoLayout;
127
+ /** @private {string} */
117
128
  this.label_ = label;
118
129
  /** @private {shaka.config.CodecSwitchingStrategy} */
119
130
  this.codecSwitchingStrategy_ = codecSwitchingStrategy;
131
+ /** @private {boolean} */
132
+ this.enableAudioGroups_ = enableAudioGroups;
120
133
  }
121
134
 
122
135
  /** @override */
123
136
  create(variants) {
124
137
  const Class = shaka.media.PreferenceBasedCriteria;
125
- const StreamUtils = shaka.util.StreamUtils;
126
138
 
127
139
  let current = [];
128
140
 
@@ -146,6 +158,17 @@ shaka.media.PreferenceBasedCriteria = class {
146
158
  shaka.log.warning('No exact match for variant role could be found.');
147
159
  }
148
160
 
161
+ if (this.videoLayout_) {
162
+ const byVideoLayout = Class.filterVariantsByVideoLayout_(
163
+ current, this.videoLayout_);
164
+ if (byVideoLayout.length) {
165
+ current = byVideoLayout;
166
+ } else {
167
+ shaka.log.warning(
168
+ 'No exact match for the video layout could be found.');
169
+ }
170
+ }
171
+
149
172
  if (this.hdrLevel_) {
150
173
  const byHdrLevel = Class.filterVariantsByHDRLevel_(
151
174
  current, this.hdrLevel_);
@@ -158,7 +181,7 @@ shaka.media.PreferenceBasedCriteria = class {
158
181
  }
159
182
 
160
183
  if (this.channelCount_) {
161
- const byChannel = StreamUtils.filterVariantsByAudioChannelCount(
184
+ const byChannel = Class.filterVariantsByAudioChannelCount_(
162
185
  current, this.channelCount_);
163
186
  if (byChannel.length) {
164
187
  current = byChannel;
@@ -181,14 +204,8 @@ shaka.media.PreferenceBasedCriteria = class {
181
204
  shaka.config.CodecSwitchingStrategy.SMOOTH &&
182
205
  shaka.media.Capabilities.isChangeTypeSupported();
183
206
 
184
- // Make sure we only return a valid adaptation set.
185
- const set = new shaka.media.AdaptationSet(current[0]);
186
- for (const variant of current) {
187
- // `add` checks combatability by calling `canInclude` internally.
188
- set.add(variant, !supportsSmoothCodecTransitions);
189
- }
190
-
191
- return set;
207
+ return new shaka.media.AdaptationSet(current[0], current,
208
+ !supportsSmoothCodecTransitions, this.enableAudioGroups_);
192
209
  }
193
210
 
194
211
  /**
@@ -261,6 +278,23 @@ shaka.media.PreferenceBasedCriteria = class {
261
278
  });
262
279
  }
263
280
 
281
+ /**
282
+ * Filter Variants by channelCount.
283
+ *
284
+ * @param {!Array.<shaka.extern.Variant>} variants
285
+ * @param {number} channelCount
286
+ * @return {!Array.<shaka.extern.Variant>}
287
+ * @private
288
+ */
289
+ static filterVariantsByAudioChannelCount_(variants, channelCount) {
290
+ return variants.filter((variant) => {
291
+ if (variant.audio && variant.audio.channelsCount &&
292
+ variant.audio.channelsCount != channelCount) {
293
+ return false;
294
+ }
295
+ return true;
296
+ });
297
+ }
264
298
 
265
299
  /**
266
300
  * Filters variants according to the given hdr level config.
@@ -285,4 +319,22 @@ shaka.media.PreferenceBasedCriteria = class {
285
319
  return true;
286
320
  });
287
321
  }
322
+
323
+
324
+ /**
325
+ * Filters variants according to the given video layout config.
326
+ *
327
+ * @param {!Array.<shaka.extern.Variant>} variants
328
+ * @param {string} videoLayout
329
+ * @private
330
+ */
331
+ static filterVariantsByVideoLayout_(variants, videoLayout) {
332
+ return variants.filter((variant) => {
333
+ if (variant.video && variant.video.videoLayout &&
334
+ variant.video.videoLayout != videoLayout) {
335
+ return false;
336
+ }
337
+ return true;
338
+ });
339
+ }
288
340
  };
@@ -42,6 +42,12 @@ shaka.media.IClosedCaptionParser = class {
42
42
  * Resets the CaptionStream.
43
43
  */
44
44
  reset() {}
45
+
46
+ /**
47
+ * Returns the streams that the CEA decoder found.
48
+ * @return {!Array.<string>}
49
+ */
50
+ getStreams() {}
45
51
  };
46
52
 
47
53
  /**
@@ -112,6 +118,13 @@ shaka.media.ClosedCaptionParser = class {
112
118
  this.ceaDecoder_.clear();
113
119
  }
114
120
 
121
+ /**
122
+ * @override
123
+ */
124
+ getStreams() {
125
+ return this.ceaDecoder_.getStreams();
126
+ }
127
+
115
128
  /**
116
129
  * @param {string} mimeType
117
130
  * @param {!shaka.extern.CeaParserPlugin} plugin
@@ -105,6 +105,12 @@ shaka.media.ContentWorkarounds = class {
105
105
  newType: ContentWorkarounds.BOX_TYPE_ENCA_,
106
106
  });
107
107
  })
108
+ .fullBox('ac-4', (box) => {
109
+ boxesToModify.push({
110
+ box,
111
+ newType: ContentWorkarounds.BOX_TYPE_ENCA_,
112
+ });
113
+ })
108
114
  .fullBox('mp4a', (box) => {
109
115
  boxesToModify.push({
110
116
  box,
@@ -715,11 +715,8 @@ shaka.media.DrmEngine = class {
715
715
 
716
716
  const metadatas = this.activeSessions_.values();
717
717
  for (const metadata of metadatas) {
718
- // Tizen 2015 and 2016 models will send multiple webkitneedkey events
719
- // with the same init data. If the duplicates are supressed, playback
720
- // will stall without errors.
721
718
  if (shaka.util.BufferUtils.equal(initData, metadata.initData) &&
722
- !shaka.util.Platform.isTizen2()) {
719
+ this.config_.ignoreDuplicateInitData) {
723
720
  shaka.log.debug('Ignoring duplicate init data.');
724
721
  return;
725
722
  }
@@ -2045,15 +2042,13 @@ shaka.media.DrmEngine = class {
2045
2042
  // audio adaptations, so we shouldn't have to worry about checking
2046
2043
  // robustness.
2047
2044
  if (drm1.keySystem == drm2.keySystem) {
2048
- /** @type {Array<shaka.extern.InitDataOverride>} */
2049
- let initData = [];
2050
- initData = initData.concat(drm1.initData || []);
2051
- initData = initData.concat(drm2.initData || []);
2052
- initData = initData.filter((d, i) => {
2053
- return d.keyId === undefined || i === initData.findIndex((d2) => {
2054
- return d2.keyId === d.keyId;
2055
- });
2056
- });
2045
+ const initDataMap = new Map();
2046
+ const bothInitDatas = (drm1.initData || [])
2047
+ .concat(drm2.initData || []);
2048
+ for (const d of bothInitDatas) {
2049
+ initDataMap.set(d.keyId, d);
2050
+ }
2051
+ const initData = Array.from(initDataMap.values());
2057
2052
 
2058
2053
  const keyIds = drm1.keyIds && drm2.keyIds ?
2059
2054
  new Set([...drm1.keyIds, ...drm2.keyIds]) :