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.
- package/CHANGELOG.md +40 -0
- package/CONTRIBUTING.md +48 -168
- package/FILES.md +40 -92
- package/VERSION +1 -1
- package/dist/_esm5.processed/compat/browser_detection.d.ts +3 -1
- package/dist/_esm5.processed/compat/browser_detection.js +7 -2
- package/dist/_esm5.processed/compat/eme/load_session.js +1 -1
- package/dist/_esm5.processed/compat/has_issues_with_high_media_source_duration.d.ts +21 -0
- package/dist/_esm5.processed/compat/has_issues_with_high_media_source_duration.js +26 -0
- package/dist/_esm5.processed/config.d.ts +2 -0
- package/dist/_esm5.processed/core/adaptive/adaptive_representation_selector.js +5 -4
- package/dist/_esm5.processed/core/adaptive/buffer_based_chooser.d.ts +18 -1
- package/dist/_esm5.processed/core/adaptive/buffer_based_chooser.js +106 -25
- package/dist/_esm5.processed/core/adaptive/guess_based_chooser.js +6 -6
- package/dist/_esm5.processed/core/adaptive/network_analyzer.js +8 -5
- package/dist/_esm5.processed/core/adaptive/utils/representation_score_calculator.d.ts +19 -1
- package/dist/_esm5.processed/core/adaptive/utils/representation_score_calculator.js +1 -1
- package/dist/_esm5.processed/core/api/debug/render.js +1 -1
- package/dist/_esm5.processed/core/api/playback_observer.js +1 -0
- package/dist/_esm5.processed/core/api/public_api.d.ts +54 -1
- package/dist/_esm5.processed/core/api/public_api.js +232 -35
- package/dist/_esm5.processed/core/api/track_management/media_element_tracks_store.js +10 -1
- package/dist/_esm5.processed/core/api/track_management/track_dispatcher.d.ts +13 -1
- package/dist/_esm5.processed/core/api/track_management/track_dispatcher.js +30 -15
- package/dist/_esm5.processed/core/api/track_management/tracks_store.d.ts +3 -1
- package/dist/_esm5.processed/core/api/track_management/tracks_store.js +67 -152
- package/dist/_esm5.processed/core/api/utils.d.ts +10 -0
- package/dist/_esm5.processed/core/api/utils.js +20 -0
- package/dist/_esm5.processed/core/decrypt/session_events_listener.js +7 -1
- package/dist/_esm5.processed/core/decrypt/utils/clean_old_loaded_sessions.js +2 -0
- package/dist/_esm5.processed/core/decrypt/utils/loaded_sessions_store.js +5 -1
- package/dist/_esm5.processed/core/init/directfile_content_initializer.js +1 -1
- package/dist/_esm5.processed/core/init/media_source_content_initializer.js +47 -10
- package/dist/_esm5.processed/core/init/types.d.ts +9 -1
- package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.d.ts +28 -1
- package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.js +22 -9
- package/dist/_esm5.processed/core/init/utils/media_source_duration_updater.d.ts +58 -0
- package/dist/_esm5.processed/core/init/utils/{media_duration_updater.js → media_source_duration_updater.js} +84 -87
- package/dist/_esm5.processed/core/init/utils/rebuffering_controller.d.ts +36 -2
- package/dist/_esm5.processed/core/init/utils/rebuffering_controller.js +82 -2
- package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.d.ts +18 -7
- package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.js +31 -40
- package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.d.ts +8 -0
- package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.js +12 -0
- package/dist/_esm5.processed/core/segment_buffers/implementations/text/native/native_text_segment_buffer.d.ts +8 -0
- package/dist/_esm5.processed/core/segment_buffers/implementations/text/native/native_text_segment_buffer.js +12 -0
- package/dist/_esm5.processed/core/segment_buffers/implementations/types.d.ts +11 -4
- package/dist/_esm5.processed/core/segment_buffers/index.d.ts +2 -2
- package/dist/_esm5.processed/core/stream/adaptation/utils/create_representation_estimator.d.ts +47 -0
- package/dist/_esm5.processed/core/stream/adaptation/utils/create_representation_estimator.js +70 -0
- package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +15 -8
- package/dist/_esm5.processed/core/stream/period/period_stream.js +1 -1
- package/dist/_esm5.processed/core/stream/representation/representation_stream.js +22 -13
- package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.d.ts +4 -2
- package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.js +2 -2
- package/dist/_esm5.processed/core/stream/representation/utils/push_init_segment.d.ts +3 -2
- package/dist/_esm5.processed/core/stream/representation/utils/push_init_segment.js +8 -8
- package/dist/_esm5.processed/core/stream/representation/utils/push_media_segment.d.ts +2 -2
- package/dist/_esm5.processed/core/stream/representation/utils/push_media_segment.js +2 -3
- package/dist/_esm5.processed/default_config.d.ts +25 -0
- package/dist/_esm5.processed/default_config.js +27 -2
- package/dist/_esm5.processed/errors/index.d.ts +2 -2
- package/dist/_esm5.processed/errors/media_error.d.ts +23 -1
- package/dist/_esm5.processed/errors/media_error.js +18 -5
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/load_and_push_segment.d.ts +1 -1
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/load_and_push_segment.js +8 -7
- package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.js +17 -9
- package/dist/_esm5.processed/experimental/tools/mediaCapabilitiesProber/index.js +0 -2
- package/dist/_esm5.processed/manifest/adaptation.d.ts +21 -2
- package/dist/_esm5.processed/manifest/adaptation.js +76 -1
- package/dist/_esm5.processed/manifest/manifest.js +1 -1
- package/dist/_esm5.processed/manifest/period.js +2 -2
- package/dist/_esm5.processed/manifest/representation.d.ts +33 -2
- package/dist/_esm5.processed/manifest/representation.js +21 -0
- package/dist/_esm5.processed/manifest/utils.js +1 -3
- package/dist/_esm5.processed/parsers/manifest/dash/js-parser/parse_from_document.d.ts +1 -1
- package/dist/_esm5.processed/parsers/manifest/dash/js-parser/parse_from_document.js +1 -1
- package/dist/_esm5.processed/parsers/manifest/dash/wasm-parser/ts/dash-wasm-parser.js +1 -0
- package/dist/_esm5.processed/public_types.d.ts +13 -3
- package/dist/_esm5.processed/tools/TextTrackRenderer/text_track_renderer.js +1 -1
- package/dist/_esm5.processed/transports/smooth/isobmff/create_boxes.d.ts +4 -6
- package/dist/_esm5.processed/transports/smooth/isobmff/create_boxes.js +4 -6
- package/dist/_esm5.processed/utils/is_null_or_undefined.d.ts +1 -1
- package/dist/_esm5.processed/utils/is_null_or_undefined.js +1 -1
- package/dist/mpd-parser.wasm +0 -0
- package/dist/rx-player.js +4709 -4218
- package/dist/rx-player.min.js +1 -1
- package/package.json +42 -36
- package/scripts/build/generate_build.js +1 -1
- package/scripts/fast_demo_build.js +4 -3
- package/scripts/generate_full_demo.js +1 -1
- package/sonar-project.properties +1 -1
- package/src/compat/browser_detection.ts +7 -1
- package/src/compat/eme/load_session.ts +1 -1
- package/src/compat/has_issues_with_high_media_source_duration.ts +27 -0
- package/src/core/adaptive/__tests__/buffer_based_chooser.test.ts +147 -48
- package/src/core/adaptive/adaptive_representation_selector.ts +7 -4
- package/src/core/adaptive/buffer_based_chooser.ts +144 -26
- package/src/core/adaptive/guess_based_chooser.ts +9 -8
- package/src/core/adaptive/network_analyzer.ts +9 -4
- package/src/core/adaptive/utils/representation_score_calculator.ts +22 -2
- package/src/core/api/debug/render.ts +1 -1
- package/src/core/api/playback_observer.ts +1 -0
- package/src/core/api/public_api.ts +277 -44
- package/src/core/api/track_management/media_element_tracks_store.ts +17 -8
- package/src/core/api/track_management/track_dispatcher.ts +37 -14
- package/src/core/api/track_management/tracks_store.ts +77 -167
- package/src/core/api/utils.ts +26 -0
- package/src/core/decrypt/session_events_listener.ts +6 -1
- package/src/core/decrypt/utils/clean_old_loaded_sessions.ts +2 -1
- package/src/core/decrypt/utils/loaded_sessions_store.ts +8 -1
- package/src/core/init/directfile_content_initializer.ts +1 -0
- package/src/core/init/media_source_content_initializer.ts +52 -9
- package/src/core/init/types.ts +9 -1
- package/src/core/init/utils/content_time_boundaries_observer.ts +46 -10
- package/src/core/init/utils/{media_duration_updater.ts → media_source_duration_updater.ts} +100 -112
- package/src/core/init/utils/rebuffering_controller.ts +114 -3
- package/src/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.ts +56 -55
- package/src/core/segment_buffers/implementations/text/html/html_text_segment_buffer.ts +16 -0
- package/src/core/segment_buffers/implementations/text/native/native_text_segment_buffer.ts +16 -0
- package/src/core/segment_buffers/implementations/types.ts +16 -4
- package/src/core/segment_buffers/index.ts +2 -0
- package/src/core/stream/adaptation/utils/create_representation_estimator.ts +114 -0
- package/src/core/stream/orchestrator/stream_orchestrator.ts +16 -8
- package/src/core/stream/period/period_stream.ts +2 -1
- package/src/core/stream/representation/representation_stream.ts +34 -22
- package/src/core/stream/representation/utils/append_segment_to_buffer.ts +8 -3
- package/src/core/stream/representation/utils/push_init_segment.ts +11 -6
- package/src/core/stream/representation/utils/push_media_segment.ts +3 -3
- package/src/default_config.ts +29 -2
- package/src/errors/__tests__/media_error.test.ts +6 -6
- package/src/errors/index.ts +4 -1
- package/src/errors/media_error.ts +67 -1
- package/src/experimental/tools/VideoThumbnailLoader/load_and_push_segment.ts +10 -7
- package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.ts +17 -6
- package/src/experimental/tools/mediaCapabilitiesProber/index.ts +0 -4
- package/src/manifest/__tests__/manifest.test.ts +7 -7
- package/src/manifest/__tests__/period.test.ts +90 -45
- package/src/manifest/adaptation.ts +89 -1
- package/src/manifest/manifest.ts +1 -1
- package/src/manifest/period.ts +4 -2
- package/src/manifest/representation.ts +67 -1
- package/src/manifest/utils.ts +1 -3
- package/src/parsers/manifest/dash/js-parser/parse_from_document.ts +1 -1
- package/src/parsers/manifest/dash/wasm-parser/ts/dash-wasm-parser.ts +1 -0
- package/src/parsers/texttracks/ttml/parse_ttml.ts +1 -1
- package/src/public_types.ts +16 -1
- package/src/tools/TextTrackRenderer/text_track_renderer.ts +1 -1
- package/src/transports/smooth/isobmff/create_boxes.ts +4 -6
- package/src/typings/globals.d.ts +20 -20
- package/src/utils/is_null_or_undefined.ts +1 -1
- package/dist/_esm5.processed/core/init/utils/media_duration_updater.d.ts +0 -56
- package/scripts/doc-generator/construct_table_of_contents.js +0 -76
- package/scripts/doc-generator/convert_MD_to_HMTL.js +0 -26
- package/scripts/doc-generator/create_documentation.js +0 -331
- package/scripts/doc-generator/create_documentation_page.js +0 -209
- package/scripts/doc-generator/create_page.js +0 -210
- package/scripts/doc-generator/generate_header_html.js +0 -147
- package/scripts/doc-generator/generate_page_html.js +0 -115
- package/scripts/doc-generator/generate_page_list_html.js +0 -92
- package/scripts/doc-generator/generate_sidebar_html.js +0 -85
- package/scripts/doc-generator/get_search_data_for_content.js +0 -137
- package/scripts/doc-generator/index.js +0 -34
- package/scripts/doc-generator/parse_doc_configs.js +0 -327
- package/scripts/doc-generator/scripts/lunr.js +0 -10
- package/scripts/doc-generator/scripts/script.js +0 -451
- package/scripts/doc-generator/styles/code.css +0 -99
- package/scripts/doc-generator/styles/style.css +0 -835
- 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
|
|
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
|
-
*
|
|
146
|
-
*
|
|
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
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
561
|
-
(hasUpdatedSourceBufferType ||
|
|
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.
|
|
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
|
-
|
|
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 `
|
|
599
|
+
* Return `true` if the given `uniqueId` is the identifier of the last
|
|
580
600
|
* initialization segment pushed to the `AudioVideoSegmentBuffer`.
|
|
581
|
-
* @param {
|
|
601
|
+
* @param {string} uniqueId
|
|
582
602
|
* @returns {boolean}
|
|
583
603
|
*/
|
|
584
|
-
private _isLastInitSegment(
|
|
585
|
-
if (this.
|
|
604
|
+
private _isLastInitSegment(uniqueId : string) : boolean {
|
|
605
|
+
if (this._lastInitSegmentUniqueId === null) {
|
|
586
606
|
return false;
|
|
587
607
|
}
|
|
588
|
-
|
|
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}
|
|
616
|
+
* @param {Object} data
|
|
609
617
|
*/
|
|
610
|
-
function
|
|
611
|
-
|
|
612
|
-
) : asserts
|
|
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
|
|
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
|
-
|
|
627
|
-
!(
|
|
628
|
-
!((
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
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
|
|
509
|
+
function checkOrCreateNextPeriodStream(nextPeriod : Period) : void {
|
|
506
510
|
if (nextStreamInfo !== null) {
|
|
507
|
-
|
|
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
|
|
112
|
+
const initSegmentState : IInitSegmentState = {
|
|
113
113
|
segment: representation.index.getInitSegment(),
|
|
114
|
-
|
|
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
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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
|
|
426
|
+
const initSegmentUniqueId = initSegmentState.uniqueId;
|
|
416
427
|
pushMediaSegment({ playbackObserver,
|
|
417
428
|
content,
|
|
418
|
-
|
|
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
|
|
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
|
-
*
|
|
462
|
-
* `
|
|
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
|
-
|
|
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",
|
|
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",
|
|
72
|
+
throw new MediaError("BUFFER_FULL_ERROR",
|
|
73
|
+
reason,
|
|
74
|
+
{ adaptation: dataInfos.inventoryInfos.adaptation });
|
|
70
75
|
}
|
|
71
76
|
}
|
|
72
77
|
}
|