rx-player 4.0.0-beta.1 → 4.0.0-beta.2

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 (169) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/CONTRIBUTING.md +48 -168
  3. package/FILES.md +40 -92
  4. package/VERSION +1 -1
  5. package/dist/_esm5.processed/compat/browser_detection.d.ts +3 -1
  6. package/dist/_esm5.processed/compat/browser_detection.js +7 -2
  7. package/dist/_esm5.processed/compat/eme/load_session.js +1 -1
  8. package/dist/_esm5.processed/compat/has_issues_with_high_media_source_duration.d.ts +21 -0
  9. package/dist/_esm5.processed/compat/has_issues_with_high_media_source_duration.js +26 -0
  10. package/dist/_esm5.processed/config.d.ts +2 -0
  11. package/dist/_esm5.processed/core/adaptive/adaptive_representation_selector.js +5 -4
  12. package/dist/_esm5.processed/core/adaptive/buffer_based_chooser.d.ts +18 -1
  13. package/dist/_esm5.processed/core/adaptive/buffer_based_chooser.js +106 -25
  14. package/dist/_esm5.processed/core/adaptive/guess_based_chooser.js +6 -6
  15. package/dist/_esm5.processed/core/adaptive/network_analyzer.js +8 -5
  16. package/dist/_esm5.processed/core/adaptive/utils/representation_score_calculator.d.ts +19 -1
  17. package/dist/_esm5.processed/core/adaptive/utils/representation_score_calculator.js +1 -1
  18. package/dist/_esm5.processed/core/api/debug/render.js +1 -1
  19. package/dist/_esm5.processed/core/api/playback_observer.js +1 -0
  20. package/dist/_esm5.processed/core/api/public_api.d.ts +54 -1
  21. package/dist/_esm5.processed/core/api/public_api.js +232 -35
  22. package/dist/_esm5.processed/core/api/track_management/media_element_tracks_store.js +10 -1
  23. package/dist/_esm5.processed/core/api/track_management/track_dispatcher.d.ts +13 -1
  24. package/dist/_esm5.processed/core/api/track_management/track_dispatcher.js +30 -15
  25. package/dist/_esm5.processed/core/api/track_management/tracks_store.d.ts +3 -1
  26. package/dist/_esm5.processed/core/api/track_management/tracks_store.js +67 -152
  27. package/dist/_esm5.processed/core/api/utils.d.ts +10 -0
  28. package/dist/_esm5.processed/core/api/utils.js +20 -0
  29. package/dist/_esm5.processed/core/decrypt/session_events_listener.js +7 -1
  30. package/dist/_esm5.processed/core/decrypt/utils/clean_old_loaded_sessions.js +2 -0
  31. package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.js +5 -1
  32. package/dist/_esm5.processed/core/init/directfile_content_initializer.js +1 -1
  33. package/dist/_esm5.processed/core/init/media_source_content_initializer.js +47 -10
  34. package/dist/_esm5.processed/core/init/types.d.ts +9 -1
  35. package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.d.ts +28 -1
  36. package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.js +22 -9
  37. package/dist/_esm5.processed/core/init/utils/media_source_duration_updater.d.ts +58 -0
  38. package/dist/_esm5.processed/core/init/utils/{media_duration_updater.js → media_source_duration_updater.js} +84 -87
  39. package/dist/_esm5.processed/core/init/utils/rebuffering_controller.d.ts +36 -2
  40. package/dist/_esm5.processed/core/init/utils/rebuffering_controller.js +82 -2
  41. package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.d.ts +18 -7
  42. package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.js +31 -40
  43. package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.d.ts +8 -0
  44. package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.js +12 -0
  45. package/dist/_esm5.processed/core/segment_buffers/implementations/text/native/native_text_segment_buffer.d.ts +8 -0
  46. package/dist/_esm5.processed/core/segment_buffers/implementations/text/native/native_text_segment_buffer.js +12 -0
  47. package/dist/_esm5.processed/core/segment_buffers/implementations/types.d.ts +11 -4
  48. package/dist/_esm5.processed/core/segment_buffers/index.d.ts +2 -2
  49. package/dist/_esm5.processed/core/stream/adaptation/utils/create_representation_estimator.d.ts +47 -0
  50. package/dist/_esm5.processed/core/stream/adaptation/utils/create_representation_estimator.js +70 -0
  51. package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +15 -8
  52. package/dist/_esm5.processed/core/stream/period/period_stream.js +1 -1
  53. package/dist/_esm5.processed/core/stream/representation/representation_stream.js +22 -13
  54. package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.d.ts +4 -2
  55. package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.js +2 -2
  56. package/dist/_esm5.processed/core/stream/representation/utils/push_init_segment.d.ts +3 -2
  57. package/dist/_esm5.processed/core/stream/representation/utils/push_init_segment.js +8 -8
  58. package/dist/_esm5.processed/core/stream/representation/utils/push_media_segment.d.ts +2 -2
  59. package/dist/_esm5.processed/core/stream/representation/utils/push_media_segment.js +2 -3
  60. package/dist/_esm5.processed/default_config.d.ts +25 -0
  61. package/dist/_esm5.processed/default_config.js +27 -2
  62. package/dist/_esm5.processed/errors/index.d.ts +2 -2
  63. package/dist/_esm5.processed/errors/media_error.d.ts +23 -1
  64. package/dist/_esm5.processed/errors/media_error.js +18 -5
  65. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/load_and_push_segment.d.ts +1 -1
  66. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/load_and_push_segment.js +8 -7
  67. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.js +17 -9
  68. package/dist/_esm5.processed/experimental/tools/mediaCapabilitiesProber/index.js +0 -2
  69. package/dist/_esm5.processed/manifest/adaptation.d.ts +21 -2
  70. package/dist/_esm5.processed/manifest/adaptation.js +76 -1
  71. package/dist/_esm5.processed/manifest/manifest.js +1 -1
  72. package/dist/_esm5.processed/manifest/period.js +2 -2
  73. package/dist/_esm5.processed/manifest/representation.d.ts +33 -2
  74. package/dist/_esm5.processed/manifest/representation.js +21 -0
  75. package/dist/_esm5.processed/manifest/utils.js +1 -3
  76. package/dist/_esm5.processed/parsers/manifest/dash/js-parser/parse_from_document.d.ts +1 -1
  77. package/dist/_esm5.processed/parsers/manifest/dash/js-parser/parse_from_document.js +1 -1
  78. package/dist/_esm5.processed/parsers/manifest/dash/wasm-parser/ts/dash-wasm-parser.js +1 -0
  79. package/dist/_esm5.processed/public_types.d.ts +13 -3
  80. package/dist/_esm5.processed/tools/TextTrackRenderer/text_track_renderer.js +1 -1
  81. package/dist/_esm5.processed/transports/smooth/isobmff/create_boxes.d.ts +4 -6
  82. package/dist/_esm5.processed/transports/smooth/isobmff/create_boxes.js +4 -6
  83. package/dist/_esm5.processed/utils/is_null_or_undefined.d.ts +1 -1
  84. package/dist/_esm5.processed/utils/is_null_or_undefined.js +1 -1
  85. package/dist/mpd-parser.wasm +0 -0
  86. package/dist/rx-player.js +4709 -4218
  87. package/dist/rx-player.min.js +1 -1
  88. package/package.json +42 -36
  89. package/scripts/build/generate_build.js +1 -1
  90. package/scripts/fast_demo_build.js +4 -3
  91. package/scripts/generate_full_demo.js +1 -1
  92. package/sonar-project.properties +1 -1
  93. package/src/compat/browser_detection.ts +7 -1
  94. package/src/compat/eme/load_session.ts +1 -1
  95. package/src/compat/has_issues_with_high_media_source_duration.ts +27 -0
  96. package/src/core/adaptive/__tests__/buffer_based_chooser.test.ts +147 -48
  97. package/src/core/adaptive/adaptive_representation_selector.ts +7 -4
  98. package/src/core/adaptive/buffer_based_chooser.ts +144 -26
  99. package/src/core/adaptive/guess_based_chooser.ts +9 -8
  100. package/src/core/adaptive/network_analyzer.ts +9 -4
  101. package/src/core/adaptive/utils/representation_score_calculator.ts +22 -2
  102. package/src/core/api/debug/render.ts +1 -1
  103. package/src/core/api/playback_observer.ts +1 -0
  104. package/src/core/api/public_api.ts +277 -44
  105. package/src/core/api/track_management/media_element_tracks_store.ts +17 -8
  106. package/src/core/api/track_management/track_dispatcher.ts +37 -14
  107. package/src/core/api/track_management/tracks_store.ts +77 -167
  108. package/src/core/api/utils.ts +26 -0
  109. package/src/core/decrypt/session_events_listener.ts +6 -1
  110. package/src/core/decrypt/utils/clean_old_loaded_sessions.ts +2 -1
  111. package/src/core/decrypt/utils/loaded_sessions_store.ts +8 -1
  112. package/src/core/init/directfile_content_initializer.ts +1 -0
  113. package/src/core/init/media_source_content_initializer.ts +52 -9
  114. package/src/core/init/types.ts +9 -1
  115. package/src/core/init/utils/content_time_boundaries_observer.ts +46 -10
  116. package/src/core/init/utils/{media_duration_updater.ts → media_source_duration_updater.ts} +100 -112
  117. package/src/core/init/utils/rebuffering_controller.ts +114 -3
  118. package/src/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.ts +56 -55
  119. package/src/core/segment_buffers/implementations/text/html/html_text_segment_buffer.ts +16 -0
  120. package/src/core/segment_buffers/implementations/text/native/native_text_segment_buffer.ts +16 -0
  121. package/src/core/segment_buffers/implementations/types.ts +16 -4
  122. package/src/core/segment_buffers/index.ts +2 -0
  123. package/src/core/stream/adaptation/utils/create_representation_estimator.ts +114 -0
  124. package/src/core/stream/orchestrator/stream_orchestrator.ts +16 -8
  125. package/src/core/stream/period/period_stream.ts +2 -1
  126. package/src/core/stream/representation/representation_stream.ts +34 -22
  127. package/src/core/stream/representation/utils/append_segment_to_buffer.ts +8 -3
  128. package/src/core/stream/representation/utils/push_init_segment.ts +11 -6
  129. package/src/core/stream/representation/utils/push_media_segment.ts +3 -3
  130. package/src/default_config.ts +29 -2
  131. package/src/errors/__tests__/media_error.test.ts +6 -6
  132. package/src/errors/index.ts +4 -1
  133. package/src/errors/media_error.ts +67 -1
  134. package/src/experimental/tools/VideoThumbnailLoader/load_and_push_segment.ts +10 -7
  135. package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.ts +17 -6
  136. package/src/experimental/tools/mediaCapabilitiesProber/index.ts +0 -4
  137. package/src/manifest/__tests__/manifest.test.ts +7 -7
  138. package/src/manifest/__tests__/period.test.ts +90 -45
  139. package/src/manifest/adaptation.ts +89 -1
  140. package/src/manifest/manifest.ts +1 -1
  141. package/src/manifest/period.ts +4 -2
  142. package/src/manifest/representation.ts +67 -1
  143. package/src/manifest/utils.ts +1 -3
  144. package/src/parsers/manifest/dash/js-parser/parse_from_document.ts +1 -1
  145. package/src/parsers/manifest/dash/wasm-parser/ts/dash-wasm-parser.ts +1 -0
  146. package/src/parsers/texttracks/ttml/parse_ttml.ts +1 -1
  147. package/src/public_types.ts +16 -1
  148. package/src/tools/TextTrackRenderer/text_track_renderer.ts +1 -1
  149. package/src/transports/smooth/isobmff/create_boxes.ts +4 -6
  150. package/src/typings/globals.d.ts +20 -20
  151. package/src/utils/is_null_or_undefined.ts +1 -1
  152. package/dist/_esm5.processed/core/init/utils/media_duration_updater.d.ts +0 -56
  153. package/scripts/doc-generator/construct_table_of_contents.js +0 -76
  154. package/scripts/doc-generator/convert_MD_to_HMTL.js +0 -26
  155. package/scripts/doc-generator/create_documentation.js +0 -331
  156. package/scripts/doc-generator/create_documentation_page.js +0 -209
  157. package/scripts/doc-generator/create_page.js +0 -210
  158. package/scripts/doc-generator/generate_header_html.js +0 -147
  159. package/scripts/doc-generator/generate_page_html.js +0 -115
  160. package/scripts/doc-generator/generate_page_list_html.js +0 -92
  161. package/scripts/doc-generator/generate_sidebar_html.js +0 -85
  162. package/scripts/doc-generator/get_search_data_for_content.js +0 -137
  163. package/scripts/doc-generator/index.js +0 -34
  164. package/scripts/doc-generator/parse_doc_configs.js +0 -327
  165. package/scripts/doc-generator/scripts/lunr.js +0 -10
  166. package/scripts/doc-generator/scripts/script.js +0 -451
  167. package/scripts/doc-generator/styles/code.css +0 -99
  168. package/scripts/doc-generator/styles/style.css +0 -835
  169. package/scripts/doc-generator/utils.js +0 -74
@@ -21,11 +21,8 @@ import {
21
21
  import config from "../../../../config";
22
22
  import log from "../../../../log";
23
23
  import { getLoggableSegmentId } from "../../../../manifest";
24
- import areArraysOfNumbersEqual from "../../../../utils/are_arrays_of_numbers_equal";
25
24
  import assertUnreachable from "../../../../utils/assert_unreachable";
26
- import { toUint8Array } from "../../../../utils/byte_parsing";
27
25
  import createCancellablePromise from "../../../../utils/create_cancellable_promise";
28
- import hashBuffer from "../../../../utils/hash_buffer";
29
26
  import noop from "../../../../utils/noop";
30
27
  import objectAssign from "../../../../utils/object_assign";
31
28
  import TaskCanceller, {
@@ -139,20 +136,26 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
139
136
  private _pendingTask : IAVSBPendingTask | null;
140
137
 
141
138
  /**
142
- * Keep track of the of the latest init segment pushed in the linked
143
- * SourceBuffer.
139
+ * Keep track of the unique identifier of the of the latest init segment
140
+ * pushed to the linked SourceBuffer.
144
141
  *
145
- * This allows to be sure the right initialization segment is pushed before
146
- * any chunk is.
142
+ * Such identifiers are first declared through the `declareInitSegment`
143
+ * method and the corresponding initialization segment is then pushed through
144
+ * the `pushChunk` method.
145
+ *
146
+ * Keeping track of this allows to be sure the right initialization segment is
147
+ * pushed before any chunk is.
147
148
  *
148
149
  * `null` if no initialization segment have been pushed to the
149
150
  * `AudioVideoSegmentBuffer` yet.
150
151
  */
151
- private _lastInitSegment : { /** The init segment itself. */
152
- data : Uint8Array;
153
- /** Hash of the initSegment for fast comparison */
154
- hash : number; } |
155
- null;
152
+ private _lastInitSegmentUniqueId : string | null;
153
+
154
+ /**
155
+ * Link unique identifiers for initialization segments (as communicated by
156
+ * `declareInitSegment`) to the corresponding initialization data.
157
+ */
158
+ private _initSegmentsMap : Map<string, BufferSource>;
156
159
 
157
160
  /**
158
161
  * @constructor
@@ -174,8 +177,9 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
174
177
  this._sourceBuffer = sourceBuffer;
175
178
  this._queue = [];
176
179
  this._pendingTask = null;
177
- this._lastInitSegment = null;
180
+ this._lastInitSegmentUniqueId = null;
178
181
  this.codec = codec;
182
+ this._initSegmentsMap = new Map();
179
183
 
180
184
  const onError = this._onPendingTaskError.bind(this);
181
185
  const reCheck = this._flush.bind(this);
@@ -198,6 +202,20 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
198
202
  });
199
203
  }
200
204
 
205
+ public declareInitSegment(
206
+ uniqueId : string,
207
+ initSegmentData : unknown
208
+ ) : void {
209
+ assertDataIsBufferSource(initSegmentData);
210
+ this._initSegmentsMap.set(uniqueId, initSegmentData);
211
+ }
212
+
213
+ public freeInitSegment(
214
+ uniqueId : string
215
+ ) : void {
216
+ this._initSegmentsMap.delete(uniqueId);
217
+ }
218
+
201
219
  /**
202
220
  * Push a chunk of the media segment given to the attached SourceBuffer, in a
203
221
  * FIFO queue.
@@ -229,12 +247,12 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
229
247
  infos : IPushChunkInfos<unknown>,
230
248
  cancellationSignal : CancellationSignal
231
249
  ) : Promise<void> {
232
- assertPushedDataIsBufferSource(infos);
250
+ assertDataIsBufferSource(infos.data.chunk);
233
251
  log.debug("AVSB: receiving order to push data to the SourceBuffer",
234
252
  this.bufferType,
235
253
  getLoggableSegmentId(infos.inventoryInfos));
236
254
  return this._addToQueue({ type: SegmentBufferOperation.Push,
237
- value: infos },
255
+ value: infos as IPushChunkInfos<BufferSource> },
238
256
  cancellationSignal);
239
257
  }
240
258
 
@@ -350,7 +368,7 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
350
368
  * @param {Event} err
351
369
  */
352
370
  private _onPendingTaskError(err : unknown) : void {
353
- this._lastInitSegment = null; // initialize init segment as a security
371
+ this._lastInitSegmentUniqueId = null; // initialize init segment as a security
354
372
  if (this._pendingTask !== null) {
355
373
  const error = err instanceof Error ?
356
374
  err :
@@ -447,7 +465,7 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
447
465
  const error = e instanceof Error ?
448
466
  e :
449
467
  new Error("An unknown error occured when preparing a push operation");
450
- this._lastInitSegment = null; // initialize init segment as a security
468
+ this._lastInitSegmentUniqueId = null; // initialize init segment as a security
451
469
  nextItem.reject(error);
452
470
  return;
453
471
  }
@@ -557,15 +575,17 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
557
575
  this._sourceBuffer.appendWindowEnd = appendWindow[1];
558
576
  }
559
577
 
560
- if (data.initSegment !== null &&
561
- (hasUpdatedSourceBufferType || !this._isLastInitSegment(data.initSegment)))
578
+ if (data.initSegmentUniqueId !== null &&
579
+ (hasUpdatedSourceBufferType ||
580
+ !this._isLastInitSegment(data.initSegmentUniqueId)))
562
581
  {
563
582
  // Push initialization segment before the media segment
564
- const segmentData = data.initSegment;
583
+ const segmentData = this._initSegmentsMap.get(data.initSegmentUniqueId);
584
+ if (segmentData === undefined) {
585
+ throw new Error("Invalid initialization segment uniqueId");
586
+ }
565
587
  dataToPush.push(segmentData);
566
- const initU8 = toUint8Array(segmentData);
567
- this._lastInitSegment = { data: initU8,
568
- hash: hashBuffer(initU8) };
588
+ this._lastInitSegmentUniqueId = data.initSegmentUniqueId;
569
589
  }
570
590
 
571
591
  if (data.chunk !== null) {
@@ -576,28 +596,16 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
576
596
  }
577
597
 
578
598
  /**
579
- * Return `true` if the given `segmentData` is the same segment than the last
599
+ * Return `true` if the given `uniqueId` is the identifier of the last
580
600
  * initialization segment pushed to the `AudioVideoSegmentBuffer`.
581
- * @param {BufferSource} segmentData
601
+ * @param {string} uniqueId
582
602
  * @returns {boolean}
583
603
  */
584
- private _isLastInitSegment(segmentData : BufferSource) : boolean {
585
- if (this._lastInitSegment === null) {
604
+ private _isLastInitSegment(uniqueId : string) : boolean {
605
+ if (this._lastInitSegmentUniqueId === null) {
586
606
  return false;
587
607
  }
588
- if (this._lastInitSegment.data === segmentData) {
589
- return true;
590
- }
591
- const oldInit = this._lastInitSegment.data;
592
- if (oldInit.byteLength === segmentData.byteLength) {
593
- const newInitU8 = toUint8Array(segmentData);
594
- if (hashBuffer(newInitU8) === this._lastInitSegment.hash &&
595
- areArraysOfNumbersEqual(oldInit, newInitU8))
596
- {
597
- return true;
598
- }
599
- }
600
- return false;
608
+ return this._lastInitSegmentUniqueId === uniqueId;
601
609
  }
602
610
  }
603
611
 
@@ -605,27 +613,20 @@ export default class AudioVideoSegmentBuffer extends SegmentBuffer {
605
613
  * Throw if the given input is not in the expected format.
606
614
  * Allows to enforce runtime type-checking as compile-time type-checking here is
607
615
  * difficult to enforce.
608
- * @param {Object} pushedData
616
+ * @param {Object} data
609
617
  */
610
- function assertPushedDataIsBufferSource(
611
- pushedData : IPushChunkInfos<unknown>
612
- ) : asserts pushedData is IPushChunkInfos<BufferSource> {
618
+ function assertDataIsBufferSource(
619
+ data : unknown
620
+ ) : asserts data is BufferSource {
613
621
  if (__ENVIRONMENT__.CURRENT_ENV === __ENVIRONMENT__.PRODUCTION as number) {
614
622
  return;
615
623
  }
616
- const { chunk, initSegment } = pushedData.data;
617
624
  if (
618
- typeof chunk !== "object" ||
619
- typeof initSegment !== "object" ||
620
- (
621
- chunk !== null &&
622
- !(chunk instanceof ArrayBuffer) &&
623
- !((chunk as ArrayBufferView).buffer instanceof ArrayBuffer)
624
- ) ||
625
+ typeof data !== "object" ||
625
626
  (
626
- initSegment !== null &&
627
- !(initSegment instanceof ArrayBuffer) &&
628
- !((initSegment as ArrayBufferView).buffer instanceof ArrayBuffer)
627
+ data !== null &&
628
+ !(data instanceof ArrayBuffer) &&
629
+ !((data as ArrayBufferView).buffer instanceof ArrayBuffer)
629
630
  )
630
631
  ) {
631
632
  throw new Error("Invalid data given to the AudioVideoSegmentBuffer");
@@ -137,6 +137,22 @@ export default class HTMLTextSegmentBuffer extends SegmentBuffer {
137
137
  this.autoRefreshSubtitles(this._canceller.signal);
138
138
  }
139
139
 
140
+ /**
141
+ * @param {string} uniqueId
142
+ */
143
+ public declareInitSegment(uniqueId : string): void {
144
+ log.warn("ISB: Declaring initialization segment for image SegmentBuffer",
145
+ uniqueId);
146
+ }
147
+
148
+ /**
149
+ * @param {string} uniqueId
150
+ */
151
+ public freeInitSegment(uniqueId : string): void {
152
+ log.warn("ISB: Freeing initialization segment for image SegmentBuffer",
153
+ uniqueId);
154
+ }
155
+
140
156
  /**
141
157
  * Push text segment to the HTMLTextSegmentBuffer.
142
158
  * @param {Object} infos
@@ -61,6 +61,22 @@ export default class NativeTextSegmentBuffer extends SegmentBuffer {
61
61
  this._trackElement = trackElement;
62
62
  }
63
63
 
64
+ /**
65
+ * @param {string} uniqueId
66
+ */
67
+ public declareInitSegment(uniqueId : string): void {
68
+ log.warn("ISB: Declaring initialization segment for image SegmentBuffer",
69
+ uniqueId);
70
+ }
71
+
72
+ /**
73
+ * @param {string} uniqueId
74
+ */
75
+ public freeInitSegment(uniqueId : string): void {
76
+ log.warn("ISB: Freeing initialization segment for image SegmentBuffer",
77
+ uniqueId);
78
+ }
79
+
64
80
  /**
65
81
  * @param {Object} infos
66
82
  * @returns {Promise}
@@ -87,6 +87,13 @@ export abstract class SegmentBuffer {
87
87
  this._segmentInventory = new SegmentInventory();
88
88
  }
89
89
 
90
+ public abstract declareInitSegment(
91
+ uniqueId : string,
92
+ initSegmentData : unknown
93
+ ) : void;
94
+
95
+ public abstract freeInitSegment(uniqueId : string) : void;
96
+
90
97
  /**
91
98
  * Push a chunk of the media segment given to the attached buffer, in a
92
99
  * FIFO queue.
@@ -96,7 +103,8 @@ export abstract class SegmentBuffer {
96
103
  * pushed.
97
104
  *
98
105
  * Depending on the type of data appended, the pushed chunk might rely on an
99
- * initialization segment, given through the `data.initSegment` property.
106
+ * initialization segment, which had to be previously declared through the
107
+ * `declareInitSegment` method.
100
108
  *
101
109
  * Such initialization segment will be first pushed to the buffer if the
102
110
  * last pushed segment was associated to another initialization segment.
@@ -106,7 +114,7 @@ export abstract class SegmentBuffer {
106
114
  * reference).
107
115
  *
108
116
  * If you don't need any initialization segment to push the wanted chunk, you
109
- * can just set `data.initSegment` to `null`.
117
+ * can just set the corresponding property to `null`.
110
118
  *
111
119
  * You can also only push an initialization segment by setting the
112
120
  * `data.chunk` argument to null.
@@ -229,12 +237,16 @@ export type IBufferType = "audio" |
229
237
  */
230
238
  export interface IPushedChunkData<T> {
231
239
  /**
232
- * The whole initialization segment's data related to the chunk you want to
240
+ * The `uniqueId` of the initialization segment linked to the data you want to
233
241
  * push.
242
+ *
243
+ * That identifier should previously have been declared through the
244
+ * `declareInitSegment` method and not freed.
245
+ *
234
246
  * To set to `null` either if no initialization data is needed, or if you are
235
247
  * confident that the last pushed one is compatible.
236
248
  */
237
- initSegment: T | null;
249
+ initSegmentUniqueId : string | null;
238
250
  /**
239
251
  * Chunk you want to push.
240
252
  * This can be the whole decodable segment's data or just a decodable sub-part
@@ -30,6 +30,7 @@ import {
30
30
  import {
31
31
  IBufferedChunk,
32
32
  IChunkContext,
33
+ IInsertedChunkInfos,
33
34
  getFirstSegmentAfterPeriod,
34
35
  getLastSegmentBeforePeriod,
35
36
  } from "./inventory";
@@ -51,6 +52,7 @@ export {
51
52
 
52
53
  IBufferedChunk,
53
54
  IChunkContext,
55
+ IInsertedChunkInfos,
54
56
 
55
57
  IPushChunkInfos,
56
58
  IPushedChunkData,
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Copyright 2015 CANAL+ Group
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import { MediaError } from "../../../../errors";
18
+ import Manifest, {
19
+ Adaptation,
20
+ Period,
21
+ Representation,
22
+ } from "../../../../manifest";
23
+ import { IPlayerError } from "../../../../public_types";
24
+ import createSharedReference, {
25
+ IReadOnlySharedReference,
26
+ } from "../../../../utils/reference";
27
+ import { CancellationSignal } from "../../../../utils/task_canceller";
28
+ import {
29
+ IABREstimate,
30
+ IRepresentationEstimatorPlaybackObservation,
31
+ IRepresentationEstimator,
32
+ IRepresentationEstimatorCallbacks,
33
+ } from "../../../adaptive";
34
+ import { IReadOnlyPlaybackObserver } from "../../../api";
35
+
36
+ /**
37
+ * Produce estimates to know which Representation should be played.
38
+ * @param {Object} content - The Manifest, Period and Adaptation wanted.
39
+ * @param {Object} representationEstimator - `IRepresentationEstimator` which
40
+ * will produce Representation estimates.
41
+ * @param {Object} currentRepresentation - Reference emitting the
42
+ * currently-loaded Representation.
43
+ * @param {Object} playbackObserver - Allows to observe the current playback
44
+ * conditions.
45
+ * @param {Function} onFatalError - Callback called when a fatal error was
46
+ * thrown. Once this callback is called, no estimate will be produced.
47
+ * @param {Object} cancellationSignal - `CancellationSignal` allowing to abort
48
+ * the production of estimates (and clean-up all linked resources).
49
+ * @returns {Object} - Returns an object with the following properties:
50
+ * - `estimateRef`: Reference emitting the last estimate
51
+ * - `abrCallbacks`: Callbacks allowing to report back network and playback
52
+ * activities to improve the estimates given.
53
+ */
54
+ export default function getRepresentationEstimate(
55
+ content : { manifest : Manifest;
56
+ period : Period;
57
+ adaptation : Adaptation; },
58
+ representationEstimator : IRepresentationEstimator,
59
+ currentRepresentation : IReadOnlySharedReference<Representation | null>,
60
+ playbackObserver : IReadOnlyPlaybackObserver<
61
+ IRepresentationEstimatorPlaybackObservation
62
+ >,
63
+ onFatalError: (err : IPlayerError) => void,
64
+ cancellationSignal : CancellationSignal
65
+ ) : { estimateRef : IReadOnlySharedReference<IABREstimate>;
66
+ abrCallbacks : IRepresentationEstimatorCallbacks; }
67
+ {
68
+ const { manifest, adaptation } = content;
69
+ const representations = createSharedReference<Representation[]>(
70
+ [],
71
+ cancellationSignal);
72
+ updateRepresentationsReference();
73
+ manifest.addEventListener("decipherabilityUpdate", updateRepresentationsReference);
74
+ const unregisterCleanUp = cancellationSignal.register(cleanUp);
75
+ const { estimates: estimateRef,
76
+ callbacks: abrCallbacks } = representationEstimator(content,
77
+ currentRepresentation,
78
+ representations,
79
+ playbackObserver,
80
+ cancellationSignal);
81
+ return { abrCallbacks, estimateRef };
82
+
83
+ function updateRepresentationsReference() : void {
84
+ /** Representations for which a `RepresentationStream` can be created. */
85
+ const newRepr = adaptation.getPlayableRepresentations();
86
+ if (newRepr.length === 0) {
87
+ const noRepErr = new MediaError("NO_PLAYABLE_REPRESENTATION",
88
+ "No Representation in the chosen " +
89
+ adaptation.type + " Adaptation can be played",
90
+ { adaptation });
91
+ cleanUp();
92
+ onFatalError(noRepErr);
93
+ return;
94
+ }
95
+
96
+ const prevRepr = representations.getValue();
97
+ if (prevRepr.length === newRepr.length) {
98
+ if (prevRepr.every((r, idx) => r.id === newRepr[idx].id)) {
99
+ return ;
100
+ }
101
+ }
102
+ representations.setValue(newRepr);
103
+ }
104
+
105
+ /** Clean-up all resources taken here. */
106
+ function cleanUp() : void {
107
+ manifest.removeEventListener("decipherabilityUpdate", updateRepresentationsReference);
108
+
109
+ // check to protect against the case where it is not yet defined.
110
+ if (typeof unregisterCleanUp !== "undefined") {
111
+ unregisterCleanUp();
112
+ }
113
+ }
114
+ }
@@ -201,7 +201,13 @@ export default function StreamOrchestrator(
201
201
  }, { clearSignal: orchestratorCancelSignal, includeLastObservation: true });
202
202
 
203
203
  manifest.addEventListener("decipherabilityUpdate", (evt) => {
204
+ if (orchestratorCancelSignal.isCancelled()) {
205
+ return;
206
+ }
204
207
  onDecipherabilityUpdates(evt).catch(err => {
208
+ if (orchestratorCancelSignal.isCancelled()) {
209
+ return;
210
+ }
205
211
  currentCanceller.cancel();
206
212
  callbacks.error(err);
207
213
  });
@@ -465,12 +471,10 @@ export default function StreamOrchestrator(
465
471
  ...consecutivePeriodStreamCb,
466
472
  streamStatusUpdate(value : IStreamStatusPayload) : void {
467
473
  if (value.hasFinishedLoading) {
468
- if (nextStreamInfo === null) {
469
- const nextPeriod = manifest.getPeriodAfter(basePeriod);
470
- if (nextPeriod !== null) {
471
- // current Stream is full, create the next one if not
472
- createNextPeriodStream(nextPeriod);
473
- }
474
+ const nextPeriod = manifest.getPeriodAfter(basePeriod);
475
+ if (nextPeriod !== null) {
476
+ // current Stream is full, create the next one if not
477
+ checkOrCreateNextPeriodStream(nextPeriod);
474
478
  }
475
479
  } else if (nextStreamInfo !== null) {
476
480
  // current Stream is active, destroy next Stream if created
@@ -502,9 +506,13 @@ export default function StreamOrchestrator(
502
506
  * Create `PeriodStream` for the next Period, specified under `nextPeriod`.
503
507
  * @param {Object} nextPeriod
504
508
  */
505
- function createNextPeriodStream(nextPeriod : Period) : void {
509
+ function checkOrCreateNextPeriodStream(nextPeriod : Period) : void {
506
510
  if (nextStreamInfo !== null) {
507
- log.warn("Stream: Creating next `PeriodStream` while it was already created.");
511
+ if (nextStreamInfo.period.id === nextPeriod.id) {
512
+ return;
513
+ }
514
+ log.warn("Stream: Creating next `PeriodStream` while one was already created.",
515
+ bufferType, nextPeriod.id, nextStreamInfo.period.id);
508
516
  consecutivePeriodStreamCb.periodStreamCleared({ type: bufferType,
509
517
  manifest,
510
518
  period: nextStreamInfo.period });
@@ -372,7 +372,8 @@ function getFirstDeclaredMimeType(adaptation : Adaptation) : string {
372
372
  if (representations.length === 0) {
373
373
  const noRepErr = new MediaError("NO_PLAYABLE_REPRESENTATION",
374
374
  "No Representation in the chosen " +
375
- adaptation.type + " Adaptation can be played");
375
+ adaptation.type + " Adaptation can be played",
376
+ { adaptation });
376
377
  throw noRepErr;
377
378
  }
378
379
  return representations[0].getMimeTypeString();
@@ -109,11 +109,17 @@ export default function RepresentationStream<TSegmentDataType>(
109
109
  segmentsLoadingCanceller.linkToSignal(globalCanceller.signal);
110
110
 
111
111
  /** Saved initialization segment state for this representation. */
112
- const initSegmentState : IInitSegmentState<TSegmentDataType> = {
112
+ const initSegmentState : IInitSegmentState = {
113
113
  segment: representation.index.getInitSegment(),
114
- segmentData: null,
114
+ uniqueId: null,
115
115
  isLoaded: false,
116
116
  };
117
+ globalCanceller.signal.register(() => {
118
+ // Free initialization segment if one has been declared
119
+ if (initSegmentState.uniqueId !== null) {
120
+ segmentBuffer.freeInitSegment(initSegmentState.uniqueId);
121
+ }
122
+ });
117
123
 
118
124
  /** Emit the last scheduled downloading queue for segments. */
119
125
  const lastSegmentQueue = createSharedReference<IDownloadQueueItem>({
@@ -125,7 +131,6 @@ export default function RepresentationStream<TSegmentDataType>(
125
131
  const hasInitSegment = initSegmentState.segment !== null;
126
132
 
127
133
  if (!hasInitSegment) {
128
- initSegmentState.segmentData = null;
129
134
  initSegmentState.isLoaded = true;
130
135
  }
131
136
 
@@ -371,21 +376,27 @@ export default function RepresentationStream<TSegmentDataType>(
371
376
  {
372
377
  representation.index.initialize(evt.segmentList);
373
378
  }
374
- initSegmentState.segmentData = evt.initializationData;
375
379
  initSegmentState.isLoaded = true;
376
380
 
377
- pushInitSegment({ playbackObserver,
378
- content,
379
- segment: evt.segment,
380
- segmentData: evt.initializationData,
381
- segmentBuffer },
382
- globalCanceller.signal)
383
- .then((result) => {
384
- if (result !== null) {
385
- callbacks.addedSegment(result);
386
- }
387
- })
388
- .catch(onFatalBufferError);
381
+ if (evt.initializationData !== null) {
382
+ const initSegmentUniqueId = representation.uniqueId;
383
+ initSegmentState.uniqueId = initSegmentUniqueId;
384
+ segmentBuffer.declareInitSegment(initSegmentUniqueId,
385
+ evt.initializationData);
386
+ pushInitSegment({ playbackObserver,
387
+ content,
388
+ initSegmentUniqueId,
389
+ segment: evt.segment,
390
+ segmentData: evt.initializationData,
391
+ segmentBuffer },
392
+ globalCanceller.signal)
393
+ .then((result) => {
394
+ if (result !== null) {
395
+ callbacks.addedSegment(result);
396
+ }
397
+ })
398
+ .catch(onFatalBufferError);
399
+ }
389
400
 
390
401
  // Sometimes the segment list is only known once the initialization segment
391
402
  // is parsed. Thus we immediately re-check if there's new segments to load.
@@ -412,10 +423,10 @@ export default function RepresentationStream<TSegmentDataType>(
412
423
  }
413
424
  }
414
425
 
415
- const initSegmentData = initSegmentState.segmentData;
426
+ const initSegmentUniqueId = initSegmentState.uniqueId;
416
427
  pushMediaSegment({ playbackObserver,
417
428
  content,
418
- initSegmentData,
429
+ initSegmentUniqueId,
419
430
  parsedSegment: evt,
420
431
  segment: evt.segment,
421
432
  segmentBuffer },
@@ -451,17 +462,18 @@ export default function RepresentationStream<TSegmentDataType>(
451
462
  * Information about the initialization segment linked to the Representation
452
463
  * which the RepresentationStream try to download segments for.
453
464
  */
454
- interface IInitSegmentState<T> {
465
+ interface IInitSegmentState {
455
466
  /**
456
467
  * Segment Object describing that initialization segment.
457
468
  * `null` if there's no initialization segment for that Representation.
458
469
  */
459
470
  segment : ISegment | null;
460
471
  /**
461
- * Initialization segment data.
462
- * `null` either when it doesn't exist or when it has not been loaded yet.
472
+ * Unique identifier used to identify the initialization segment data, used by
473
+ * the `SegmentBuffer`.
474
+ * `null` either when it doesn't exist or when it has not been declared yet.
463
475
  */
464
- segmentData : T | null;
476
+ uniqueId : string | null;
465
477
  /** `true` if the initialization segment has been loaded and parsed. */
466
478
  isLoaded : boolean;
467
479
  }
@@ -22,6 +22,7 @@ import { MediaError } from "../../../../errors";
22
22
  import { CancellationError, CancellationSignal } from "../../../../utils/task_canceller";
23
23
  import { IReadOnlyPlaybackObserver } from "../../../api";
24
24
  import {
25
+ IInsertedChunkInfos,
25
26
  IPushChunkInfos,
26
27
  SegmentBuffer,
27
28
  } from "../../../segment_buffers";
@@ -41,7 +42,7 @@ import forceGarbageCollection from "./force_garbage_collection";
41
42
  export default async function appendSegmentToBuffer<T>(
42
43
  playbackObserver : IReadOnlyPlaybackObserver<IRepresentationStreamPlaybackObservation>,
43
44
  segmentBuffer : SegmentBuffer,
44
- dataInfos : IPushChunkInfos<T>,
45
+ dataInfos : IPushChunkInfos<T> & { inventoryInfos: IInsertedChunkInfos },
45
46
  cancellationSignal : CancellationSignal
46
47
  ) : Promise<void> {
47
48
  try {
@@ -55,7 +56,9 @@ export default async function appendSegmentToBuffer<T>(
55
56
  const reason = appendError instanceof Error ?
56
57
  appendError.toString() :
57
58
  "An unknown error happened when pushing content";
58
- throw new MediaError("BUFFER_APPEND_ERROR", reason);
59
+ throw new MediaError("BUFFER_APPEND_ERROR",
60
+ reason,
61
+ { adaptation: dataInfos.inventoryInfos.adaptation });
59
62
  }
60
63
  const { position } = playbackObserver.getReference().getValue();
61
64
  const currentPos = position.pending ?? position.last;
@@ -66,7 +69,9 @@ export default async function appendSegmentToBuffer<T>(
66
69
  const reason = err2 instanceof Error ? err2.toString() :
67
70
  "Could not clean the buffer";
68
71
 
69
- throw new MediaError("BUFFER_FULL_ERROR", reason);
72
+ throw new MediaError("BUFFER_FULL_ERROR",
73
+ reason,
74
+ { adaptation: dataInfos.inventoryInfos.adaptation });
70
75
  }
71
76
  }
72
77
  }