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
@@ -4,12 +4,15 @@
4
4
  * SPDX-License-Identifier: Apache-2.0
5
5
  */
6
6
 
7
+ goog.provide('shaka.media.SegmentPrefetch');
8
+
7
9
  goog.require('goog.asserts');
8
- goog.require('shaka.net.NetworkingEngine');
10
+ goog.require('shaka.log');
9
11
  goog.require('shaka.media.InitSegmentReference');
10
12
  goog.require('shaka.media.SegmentReference');
11
- goog.provide('shaka.media.SegmentPrefetch');
12
- goog.require('shaka.log');
13
+ goog.require('shaka.net.NetworkingEngine');
14
+ goog.require('shaka.util.Uint8ArrayUtils');
15
+
13
16
 
14
17
  /**
15
18
  * @summary
@@ -37,8 +40,9 @@ shaka.media.SegmentPrefetch = class {
37
40
  this.fetchDispatcher_ = fetchDispatcher;
38
41
 
39
42
  /**
40
- * @private {!Map.<shaka.media.SegmentReference,
41
- * !shaka.media.SegmentPrefetchOperation>}
43
+ * @private {!Map.<
44
+ * !(shaka.media.SegmentReference|shaka.media.InitSegmentReference),
45
+ * !shaka.media.SegmentPrefetchOperation>}
42
46
  */
43
47
  this.segmentPrefetchMap_ = new Map();
44
48
  }
@@ -47,9 +51,10 @@ shaka.media.SegmentPrefetch = class {
47
51
  * Fetch next segments ahead of current segment.
48
52
  *
49
53
  * @param {(!shaka.media.SegmentReference)} startReference
54
+ * @param {boolean=} skipFirst
50
55
  * @public
51
56
  */
52
- prefetchSegments(startReference) {
57
+ prefetchSegments(startReference, skipFirst = false) {
53
58
  goog.asserts.assert(this.prefetchLimit_ > 0,
54
59
  'SegmentPrefetch can not be used when prefetchLimit <= 0.');
55
60
 
@@ -65,6 +70,14 @@ shaka.media.SegmentPrefetch = class {
65
70
  return;
66
71
  }
67
72
  let reference = startReference;
73
+ if (skipFirst) {
74
+ reference = iterator.next().value;
75
+ if (reference &&
76
+ reference.startTime == startReference.startTime &&
77
+ reference.endTime == startReference.endTime) {
78
+ reference = null;
79
+ }
80
+ }
68
81
  while (this.segmentPrefetchMap_.size < this.prefetchLimit_ &&
69
82
  reference != null) {
70
83
  // By default doesn't prefech preload partial segments when using
@@ -88,9 +101,38 @@ shaka.media.SegmentPrefetch = class {
88
101
  }
89
102
  }
90
103
 
104
+ /**
105
+ * Fetch init segment.
106
+ *
107
+ * @param {!shaka.media.InitSegmentReference} initSegmentReference
108
+ * @public
109
+ */
110
+ prefetchInitSegment(initSegmentReference) {
111
+ goog.asserts.assert(this.prefetchLimit_ > 0,
112
+ 'SegmentPrefetch can not be used when prefetchLimit <= 0.');
113
+
114
+ const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
115
+ if (!this.stream_.segmentIndex) {
116
+ shaka.log.info(logPrefix, 'missing segmentIndex');
117
+ return;
118
+ }
119
+
120
+ if (this.segmentPrefetchMap_.size < this.prefetchLimit_) {
121
+ if (!this.segmentPrefetchMap_.has(initSegmentReference)) {
122
+ const segmentPrefetchOperation =
123
+ new shaka.media.SegmentPrefetchOperation(this.fetchDispatcher_);
124
+ segmentPrefetchOperation.dispatchFetch(
125
+ initSegmentReference, this.stream_);
126
+ this.segmentPrefetchMap_.set(
127
+ initSegmentReference, segmentPrefetchOperation);
128
+ }
129
+ }
130
+ }
131
+
91
132
  /**
92
133
  * Get the result of prefetched segment if already exists.
93
- * @param {(!shaka.media.SegmentReference)} reference
134
+ * @param {!(shaka.media.SegmentReference|shaka.media.InitSegmentReference)}
135
+ * reference
94
136
  * @param {?function(BufferSource):!Promise=} streamDataCallback
95
137
  * @return {?shaka.net.NetworkingEngine.PendingRequest} op
96
138
  * @public
@@ -98,8 +140,6 @@ shaka.media.SegmentPrefetch = class {
98
140
  getPrefetchedSegment(reference, streamDataCallback) {
99
141
  goog.asserts.assert(this.prefetchLimit_ > 0,
100
142
  'SegmentPrefetch can not be used when prefetchLimit <= 0.');
101
- goog.asserts.assert(reference instanceof shaka.media.SegmentReference,
102
- 'getPrefetchedSegment is only used for shaka.media.SegmentReference.');
103
143
 
104
144
  const logPrefix = shaka.media.SegmentPrefetch.logPrefix_(this.stream_);
105
145
 
@@ -109,16 +149,30 @@ shaka.media.SegmentPrefetch = class {
109
149
  segmentPrefetchOperation.setStreamDataCallback(streamDataCallback);
110
150
  }
111
151
  this.segmentPrefetchMap_.delete(reference);
112
- shaka.log.debug(
113
- logPrefix,
114
- 'reused prefetched segment at time:', reference.startTime,
115
- 'mapSize', this.segmentPrefetchMap_.size);
152
+ if (reference instanceof shaka.media.SegmentReference) {
153
+ shaka.log.debug(
154
+ logPrefix,
155
+ 'reused prefetched segment at time:', reference.startTime,
156
+ 'mapSize', this.segmentPrefetchMap_.size);
157
+ } else {
158
+ shaka.log.debug(
159
+ logPrefix,
160
+ 'reused prefetched init segment at time, mapSize',
161
+ this.segmentPrefetchMap_.size);
162
+ }
116
163
  return segmentPrefetchOperation.getOperation();
117
164
  } else {
118
- shaka.log.debug(
119
- logPrefix,
120
- 'missed segment at time:', reference.startTime,
121
- 'mapSize', this.segmentPrefetchMap_.size);
165
+ if (reference instanceof shaka.media.SegmentReference) {
166
+ shaka.log.debug(
167
+ logPrefix,
168
+ 'reused prefetched segment at time:', reference.startTime,
169
+ 'mapSize', this.segmentPrefetchMap_.size);
170
+ } else {
171
+ shaka.log.debug(
172
+ logPrefix,
173
+ 'reused prefetched init segment at time, mapSize',
174
+ this.segmentPrefetchMap_.size);
175
+ }
122
176
  return null;
123
177
  }
124
178
  }
@@ -174,7 +228,8 @@ shaka.media.SegmentPrefetch = class {
174
228
 
175
229
  /**
176
230
  * Remove a segment from prefetch map and abort it.
177
- * @param {(!shaka.media.SegmentReference)} reference
231
+ * @param {!(shaka.media.SegmentReference|shaka.media.InitSegmentReference)}
232
+ * reference
178
233
  * @private
179
234
  */
180
235
  abortPrefetchedSegment_(reference) {
@@ -183,9 +238,13 @@ shaka.media.SegmentPrefetch = class {
183
238
  this.segmentPrefetchMap_.delete(reference);
184
239
  if (segmentPrefetchOperation) {
185
240
  segmentPrefetchOperation.abort();
186
- shaka.log.info(
187
- logPrefix,
188
- 'pop and abort prefetched segment at time:', reference.startTime);
241
+ if (reference instanceof shaka.media.SegmentReference) {
242
+ shaka.log.info(
243
+ logPrefix,
244
+ 'pop and abort prefetched segment at time:', reference.startTime);
245
+ } else {
246
+ shaka.log.info(logPrefix, 'pop and abort prefetched init segment');
247
+ }
189
248
  }
190
249
  }
191
250
 
@@ -221,16 +280,25 @@ shaka.media.SegmentPrefetchOperation = class {
221
280
  /**
222
281
  * Fetch a segments
223
282
  *
224
- * @param {!shaka.media.SegmentReference}
225
- * reference
283
+ * @param {!(shaka.media.SegmentReference|shaka.media.InitSegmentReference)}
284
+ * reference
226
285
  * @param {!shaka.extern.Stream} stream
227
286
  * @public
228
287
  */
229
288
  dispatchFetch(reference, stream) {
289
+ // We need to store the data, because streamDataCallback_ might not be
290
+ // available when you start getting the first data.
291
+ let buffered = new Uint8Array(0);
230
292
  this.operation_ = this.fetchDispatcher_(
231
293
  reference, stream, async (data) => {
294
+ if (buffered.byteLength > 0) {
295
+ buffered = shaka.util.Uint8ArrayUtils.concat(buffered, data);
296
+ } else {
297
+ buffered = data;
298
+ }
232
299
  if (this.streamDataCallback_) {
233
- await this.streamDataCallback_(data);
300
+ await this.streamDataCallback_(buffered);
301
+ buffered = new Uint8Array(0);
234
302
  }
235
303
  });
236
304
  }
@@ -198,13 +198,15 @@ shaka.media.SegmentReference = class {
198
198
  * not available.
199
199
  * @param {?shaka.extern.aes128Key=} aes128Key
200
200
  * The segment's AES-128-CBC full segment encryption key and iv.
201
+ * @param {boolean=} allPartialSegments
202
+ * Indicate if the segment has all partial segments
201
203
  */
202
204
  constructor(
203
205
  startTime, endTime, uris, startByte, endByte, initSegmentReference,
204
206
  timestampOffset, appendWindowStart, appendWindowEnd,
205
207
  partialReferences = [], tilesLayout = '', tileDuration = null,
206
208
  syncTime = null, status = shaka.media.SegmentReference.Status.AVAILABLE,
207
- aes128Key = null) {
209
+ aes128Key = null, allPartialSegments = false) {
208
210
  // A preload hinted Partial Segment has the same startTime and endTime.
209
211
  goog.asserts.assert(startTime <= endTime,
210
212
  'startTime must be less than or equal to endTime');
@@ -284,6 +286,24 @@ shaka.media.SegmentReference = class {
284
286
 
285
287
  /** @type {number} */
286
288
  this.discontinuitySequence = 0;
289
+
290
+ /** @type {boolean} */
291
+ this.allPartialSegments = allPartialSegments;
292
+
293
+ /** @type {boolean} */
294
+ this.partial = false;
295
+
296
+ /** @type {boolean} */
297
+ this.lastPartial = false;
298
+
299
+ for (const partial of this.partialReferences) {
300
+ partial.markAsPartial();
301
+ }
302
+ if (this.allPartialSegments && this.partialReferences.length) {
303
+ const lastPartial =
304
+ this.partialReferences[this.partialReferences.length - 1];
305
+ lastPartial.markAsLastPartial();
306
+ }
287
307
  }
288
308
 
289
309
  /**
@@ -359,6 +379,14 @@ shaka.media.SegmentReference = class {
359
379
  return this.partialReferences.length > 0;
360
380
  }
361
381
 
382
+ /**
383
+ * Returns true if it contains all partial SegmentReferences.
384
+ * @return {boolean}
385
+ */
386
+ hasAllPartialSegments() {
387
+ return this.allPartialSegments;
388
+ }
389
+
362
390
  /**
363
391
  * Returns the segment's tiles layout. Only defined in image segments.
364
392
  *
@@ -437,6 +465,44 @@ shaka.media.SegmentReference = class {
437
465
  return this.independent;
438
466
  }
439
467
 
468
+ /**
469
+ * Mark the reference as partial.
470
+ *
471
+ * @export
472
+ */
473
+ markAsPartial() {
474
+ this.partial = true;
475
+ }
476
+
477
+ /**
478
+ * Returns true if the segment is partial.
479
+ *
480
+ * @return {boolean}
481
+ * @export
482
+ */
483
+ isPartial() {
484
+ return this.partial;
485
+ }
486
+
487
+ /**
488
+ * Mark the reference as being the last part of the full segment
489
+ *
490
+ * @export
491
+ */
492
+ markAsLastPartial() {
493
+ this.lastPartial = true;
494
+ }
495
+
496
+ /**
497
+ * Returns true if reference as being the last part of the full segment.
498
+ *
499
+ * @return {boolean}
500
+ * @export
501
+ */
502
+ isLastPartial() {
503
+ return this.lastPartial;
504
+ }
505
+
440
506
  /**
441
507
  * Mark the reference as byterange optimization.
442
508
  *
@@ -0,0 +1,343 @@
1
+ /*! @license
2
+ * Shaka Player
3
+ * Copyright 2016 Google LLC
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+
7
+ goog.provide('shaka.media.SegmentUtils');
8
+
9
+ goog.require('goog.asserts');
10
+ goog.require('shaka.log');
11
+ goog.require('shaka.media.ClosedCaptionParser');
12
+ goog.require('shaka.util.BufferUtils');
13
+ goog.require('shaka.util.MimeUtils');
14
+ goog.require('shaka.util.Mp4BoxParsers');
15
+ goog.require('shaka.util.Mp4Parser');
16
+ goog.require('shaka.util.TsParser');
17
+
18
+
19
+ /**
20
+ * @summary Utility functions for segment parsing.
21
+ */
22
+ shaka.media.SegmentUtils = class {
23
+ /**
24
+ * @param {string} mimeType
25
+ * @return {shaka.media.SegmentUtils.BasicInfo}
26
+ */
27
+ static getBasicInfoFromMimeType(mimeType) {
28
+ const baseMimeType = shaka.util.MimeUtils.getBasicType(mimeType);
29
+ const type = baseMimeType.split('/')[0];
30
+ const codecs = shaka.util.MimeUtils.getCodecs(mimeType);
31
+ return {
32
+ type: type,
33
+ mimeType: baseMimeType,
34
+ codecs: codecs,
35
+ language: null,
36
+ height: null,
37
+ width: null,
38
+ channelCount: null,
39
+ sampleRate: null,
40
+ closedCaptions: new Map(),
41
+ };
42
+ }
43
+
44
+ /**
45
+ * @param {!BufferSource} data
46
+ * @return {?shaka.media.SegmentUtils.BasicInfo}
47
+ */
48
+ static getBasicInfoFromTs(data) {
49
+ const uint8ArrayData = shaka.util.BufferUtils.toUint8(data);
50
+ const tsParser = new shaka.util.TsParser().parse(uint8ArrayData);
51
+ const tsCodecs = tsParser.getCodecs();
52
+ const videoInfo = tsParser.getVideoInfo();
53
+ const codecs = [];
54
+ let hasAudio = false;
55
+ let hasVideo = false;
56
+ switch (tsCodecs.audio) {
57
+ case 'aac':
58
+ codecs.push('mp4a.40.2');
59
+ hasAudio = true;
60
+ break;
61
+ case 'mp3':
62
+ codecs.push('mp4a.40.34');
63
+ hasAudio = true;
64
+ break;
65
+ case 'ac3':
66
+ codecs.push('ac-3');
67
+ hasAudio = true;
68
+ break;
69
+ case 'ec3':
70
+ codecs.push('ec-3');
71
+ hasAudio = true;
72
+ break;
73
+ }
74
+ switch (tsCodecs.video) {
75
+ case 'avc':
76
+ if (videoInfo.codec) {
77
+ codecs.push(videoInfo.codec);
78
+ } else {
79
+ codecs.push('avc1.42E01E');
80
+ }
81
+ hasVideo = true;
82
+ break;
83
+ case 'hvc':
84
+ if (videoInfo.codec) {
85
+ codecs.push(videoInfo.codec);
86
+ } else {
87
+ codecs.push('hvc1.1.6.L93.90');
88
+ }
89
+ hasVideo = true;
90
+ break;
91
+ }
92
+ if (!codecs.length) {
93
+ return null;
94
+ }
95
+ const onlyAudio = hasAudio && !hasVideo;
96
+ const closedCaptions = new Map();
97
+ if (hasVideo) {
98
+ const captionParser = new shaka.media.ClosedCaptionParser('video/mp2t');
99
+ captionParser.parseFrom(data);
100
+ for (const stream of captionParser.getStreams()) {
101
+ closedCaptions.set(stream, stream);
102
+ }
103
+ captionParser.reset();
104
+ }
105
+ return {
106
+ type: onlyAudio ? 'audio' : 'video',
107
+ mimeType: 'video/mp2t',
108
+ codecs: codecs.join(', '),
109
+ language: null,
110
+ height: videoInfo.height,
111
+ width: videoInfo.width,
112
+ channelCount: null,
113
+ sampleRate: null,
114
+ closedCaptions: closedCaptions,
115
+ };
116
+ }
117
+
118
+ /**
119
+ * @param {?BufferSource} initData
120
+ * @param {!BufferSource} data
121
+ * @return {?shaka.media.SegmentUtils.BasicInfo}
122
+ */
123
+ static getBasicInfoFromMp4(initData, data) {
124
+ const Mp4Parser = shaka.util.Mp4Parser;
125
+ const SegmentUtils = shaka.media.SegmentUtils;
126
+
127
+ const codecs = [];
128
+
129
+ let hasAudio = false;
130
+ let hasVideo = false;
131
+
132
+ const addCodec = (codec) => {
133
+ const codecLC = codec.toLowerCase();
134
+ switch (codecLC) {
135
+ case 'avc1':
136
+ case 'avc3':
137
+ codecs.push(codecLC + '.42E01E');
138
+ hasVideo = true;
139
+ break;
140
+ case 'hev1':
141
+ case 'hvc1':
142
+ codecs.push(codecLC + '.1.6.L93.90');
143
+ hasVideo = true;
144
+ break;
145
+ case 'dvh1':
146
+ case 'dvhe':
147
+ codecs.push(codecLC + '.05.04');
148
+ hasVideo = true;
149
+ break;
150
+ case 'vp09':
151
+ codecs.push(codecLC + '.00.10.08');
152
+ hasVideo = true;
153
+ break;
154
+ case 'av01':
155
+ codecs.push(codecLC + '.0.01M.08');
156
+ hasVideo = true;
157
+ break;
158
+ case 'mp4a':
159
+ // We assume AAC, but this can be wrong since mp4a supports
160
+ // others codecs
161
+ codecs.push('mp4a.40.2');
162
+ hasAudio = true;
163
+ break;
164
+ case 'ac-3':
165
+ case 'ec-3':
166
+ case 'opus':
167
+ case 'flac':
168
+ codecs.push(codecLC);
169
+ hasAudio = true;
170
+ break;
171
+ }
172
+ };
173
+
174
+ const codecBoxParser = (box) => addCodec(box.name);
175
+
176
+ /** @type {?string} */
177
+ let language = null;
178
+ /** @type {?string} */
179
+ let height = null;
180
+ /** @type {?string} */
181
+ let width = null;
182
+ /** @type {?number} */
183
+ let channelCount = null;
184
+ /** @type {?number} */
185
+ let sampleRate = null;
186
+
187
+ new Mp4Parser()
188
+ .box('moov', Mp4Parser.children)
189
+ .box('trak', Mp4Parser.children)
190
+ .fullBox('tkhd', (box) => {
191
+ goog.asserts.assert(
192
+ box.version != null,
193
+ 'TKHD is a full box and should have a valid version.');
194
+ const parsedTKHDBox = shaka.util.Mp4BoxParsers.parseTKHD(
195
+ box.reader, box.version);
196
+ height = String(parsedTKHDBox.height);
197
+ width = String(parsedTKHDBox.width);
198
+ })
199
+ .box('mdia', Mp4Parser.children)
200
+ .fullBox('mdhd', (box) => {
201
+ goog.asserts.assert(
202
+ box.version != null,
203
+ 'MDHD is a full box and should have a valid version.');
204
+ const parsedMDHDBox = shaka.util.Mp4BoxParsers.parseMDHD(
205
+ box.reader, box.version);
206
+ language = parsedMDHDBox.language;
207
+ })
208
+ .box('minf', Mp4Parser.children)
209
+ .box('stbl', Mp4Parser.children)
210
+ .fullBox('stsd', Mp4Parser.sampleDescription)
211
+
212
+ // AUDIO
213
+ // These are the various boxes that signal a codec.
214
+ .box('mp4a', (box) => {
215
+ const parsedMP4ABox = shaka.util.Mp4BoxParsers.parseMP4A(box.reader);
216
+ channelCount = parsedMP4ABox.channelCount;
217
+ sampleRate = parsedMP4ABox.sampleRate;
218
+ if (box.reader.hasMoreData()) {
219
+ Mp4Parser.children(box);
220
+ } else {
221
+ codecBoxParser(box);
222
+ }
223
+ })
224
+ .box('esds', (box) => {
225
+ const parsedESDSBox = shaka.util.Mp4BoxParsers.parseESDS(box.reader);
226
+ codecs.push(parsedESDSBox.codec);
227
+ hasAudio = true;
228
+ })
229
+ .box('ac-3', codecBoxParser)
230
+ .box('ec-3', codecBoxParser)
231
+ .box('opus', codecBoxParser)
232
+ .box('Opus', codecBoxParser)
233
+ .box('fLaC', codecBoxParser)
234
+
235
+ // VIDEO
236
+ // These are the various boxes that signal a codec.
237
+ .box('avc1', (box) => {
238
+ const parsedAVCBox =
239
+ shaka.util.Mp4BoxParsers.parseAVC(box.reader, box.name);
240
+ codecs.push(parsedAVCBox.codec);
241
+ hasVideo = true;
242
+ })
243
+ .box('avc3', (box) => {
244
+ const parsedAVCBox =
245
+ shaka.util.Mp4BoxParsers.parseAVC(box.reader, box.name);
246
+ codecs.push(parsedAVCBox.codec);
247
+ hasVideo = true;
248
+ })
249
+ .box('hev1', codecBoxParser)
250
+ .box('hvc1', codecBoxParser)
251
+ .box('dvh1', codecBoxParser)
252
+ .box('dvhe', codecBoxParser)
253
+ .box('vp09', codecBoxParser)
254
+ .box('av01', codecBoxParser)
255
+
256
+ // This signals an encrypted sample, which we can go inside of to
257
+ // find the codec used.
258
+ // Note: If encrypted, you can only have audio or video, not both.
259
+ .box('enca', Mp4Parser.visualSampleEntry)
260
+ .box('encv', Mp4Parser.visualSampleEntry)
261
+ .box('sinf', Mp4Parser.children)
262
+ .box('frma', (box) => {
263
+ const {codec} = shaka.util.Mp4BoxParsers.parseFRMA(box.reader);
264
+ addCodec(codec);
265
+ })
266
+
267
+ .parse(initData || data, /* partialOkay= */ true);
268
+ if (!codecs.length) {
269
+ return null;
270
+ }
271
+ const onlyAudio = hasAudio && !hasVideo;
272
+ const closedCaptions = new Map();
273
+ if (hasVideo) {
274
+ const captionParser = new shaka.media.ClosedCaptionParser('video/mp4');
275
+ if (initData) {
276
+ captionParser.init(initData);
277
+ }
278
+ captionParser.parseFrom(data);
279
+ for (const stream of captionParser.getStreams()) {
280
+ closedCaptions.set(stream, stream);
281
+ }
282
+ captionParser.reset();
283
+ }
284
+ return {
285
+ type: onlyAudio ? 'audio' : 'video',
286
+ mimeType: onlyAudio ? 'audio/mp4' : 'video/mp4',
287
+ codecs: SegmentUtils.filterDuplicateCodecs_(codecs).join(', '),
288
+ language: language,
289
+ height: height,
290
+ width: width,
291
+ channelCount: channelCount,
292
+ sampleRate: sampleRate,
293
+ closedCaptions: closedCaptions,
294
+ };
295
+ }
296
+
297
+ /**
298
+ * @param {!Array.<string>} codecs
299
+ * @return {!Array.<string>} codecs
300
+ * @private
301
+ */
302
+ static filterDuplicateCodecs_(codecs) {
303
+ // Filter out duplicate codecs.
304
+ const seen = new Set();
305
+ const ret = [];
306
+ for (const codec of codecs) {
307
+ const shortCodec = shaka.util.MimeUtils.getCodecBase(codec);
308
+ if (!seen.has(shortCodec)) {
309
+ ret.push(codec);
310
+ seen.add(shortCodec);
311
+ } else {
312
+ shaka.log.debug('Ignoring duplicate codec');
313
+ }
314
+ }
315
+ return ret;
316
+ }
317
+ };
318
+
319
+
320
+ /**
321
+ * @typedef {{
322
+ * type: string,
323
+ * mimeType: string,
324
+ * codecs: string,
325
+ * language: ?string,
326
+ * height: ?string,
327
+ * width: ?string,
328
+ * channelCount: ?number,
329
+ * sampleRate: ?number,
330
+ * closedCaptions: Map.<string, string>
331
+ * }}
332
+ *
333
+ * @property {string} type
334
+ * @property {string} mimeType
335
+ * @property {string} codecs
336
+ * @property {?string} language
337
+ * @property {?string} height
338
+ * @property {?string} width
339
+ * @property {?number} channelCount
340
+ * @property {?number} sampleRate
341
+ * @property {Map.<string, string>} closedCaptions
342
+ */
343
+ shaka.media.SegmentUtils.BasicInfo;
@@ -174,9 +174,6 @@ shaka.media.StallDetector.MediaElementImplementation = class {
174
174
  if (this.mediaElement_.paused) {
175
175
  return false;
176
176
  }
177
- if (this.mediaElement_.playbackRate == 0) {
178
- return false;
179
- }
180
177
 
181
178
  // If we have don't have enough content, we are not stalled, we are
182
179
  // buffering.