rx-player 3.30.0-dev.2023020100 → 3.30.0-dev.2023030200

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 (183) hide show
  1. package/.eslintrc.js +8 -0
  2. package/CHANGELOG.md +5 -1
  3. package/VERSION +1 -1
  4. package/dist/_esm5.processed/compat/browser_detection.d.ts +23 -12
  5. package/dist/_esm5.processed/compat/browser_detection.js +80 -38
  6. package/dist/_esm5.processed/compat/can_reuse_media_keys.js +2 -2
  7. package/dist/_esm5.processed/compat/eme/close_session.js +2 -2
  8. package/dist/_esm5.processed/compat/event_listeners.js +1 -1
  9. package/dist/_esm5.processed/config.d.ts +2 -0
  10. package/dist/_esm5.processed/core/adaptive/adaptive_representation_selector.js +4 -2
  11. package/dist/_esm5.processed/core/api/debug/buffer_graph.d.ts +28 -0
  12. package/dist/_esm5.processed/core/api/debug/buffer_graph.js +175 -0
  13. package/dist/_esm5.processed/core/api/debug/buffer_size_graph.d.ts +10 -0
  14. package/dist/_esm5.processed/core/api/debug/buffer_size_graph.js +104 -0
  15. package/dist/_esm5.processed/core/api/debug/constants.d.ts +2 -0
  16. package/dist/_esm5.processed/core/api/debug/constants.js +2 -0
  17. package/dist/_esm5.processed/core/api/debug/index.d.ts +2 -0
  18. package/dist/_esm5.processed/core/api/debug/index.js +2 -0
  19. package/dist/_esm5.processed/core/api/debug/modules/general_info.d.ts +3 -0
  20. package/dist/_esm5.processed/core/api/debug/modules/general_info.js +199 -0
  21. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_content.d.ts +4 -0
  22. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_content.js +121 -0
  23. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_size.d.ts +3 -0
  24. package/dist/_esm5.processed/core/api/debug/modules/segment_buffer_size.js +35 -0
  25. package/dist/_esm5.processed/core/api/debug/render.d.ts +3 -0
  26. package/dist/_esm5.processed/core/api/debug/render.js +32 -0
  27. package/dist/_esm5.processed/core/api/debug/utils.d.ts +39 -0
  28. package/dist/_esm5.processed/core/api/debug/utils.js +57 -0
  29. package/dist/_esm5.processed/core/api/playback_observer.js +3 -2
  30. package/dist/_esm5.processed/core/api/public_api.d.ts +3 -0
  31. package/dist/_esm5.processed/core/api/public_api.js +25 -14
  32. package/dist/_esm5.processed/core/api/utils.js +3 -3
  33. package/dist/_esm5.processed/core/decrypt/__tests__/__global__/utils.d.ts +32 -13
  34. package/dist/_esm5.processed/core/decrypt/__tests__/__global__/utils.js +28 -7
  35. package/dist/_esm5.processed/core/decrypt/attach_media_keys.js +1 -1
  36. package/dist/_esm5.processed/core/decrypt/content_decryptor.js +1 -1
  37. package/dist/_esm5.processed/core/decrypt/find_key_system.js +33 -24
  38. package/dist/_esm5.processed/core/decrypt/session_events_listener.js +54 -50
  39. package/dist/_esm5.processed/core/decrypt/utils/check_key_statuses.js +4 -0
  40. package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.d.ts +17 -8
  41. package/dist/_esm5.processed/core/fetchers/cdn_prioritizer.js +10 -6
  42. package/dist/_esm5.processed/core/fetchers/manifest/manifest_fetcher.js +5 -4
  43. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.d.ts +22 -5
  44. package/dist/_esm5.processed/core/fetchers/segment/segment_fetcher.js +37 -21
  45. package/dist/_esm5.processed/core/fetchers/segment/task_prioritizer.js +21 -23
  46. package/dist/_esm5.processed/core/fetchers/utils/schedule_request.js +17 -7
  47. package/dist/_esm5.processed/core/init/directfile_content_initializer.js +1 -1
  48. package/dist/_esm5.processed/core/init/media_source_content_initializer.js +27 -31
  49. package/dist/_esm5.processed/core/init/utils/content_time_boundaries_observer.js +2 -2
  50. package/dist/_esm5.processed/core/init/utils/create_media_source.js +3 -12
  51. package/dist/_esm5.processed/core/init/utils/end_of_stream.js +6 -3
  52. package/dist/_esm5.processed/core/init/utils/get_loaded_reference.js +2 -1
  53. package/dist/_esm5.processed/core/init/utils/initial_seek_and_play.js +9 -5
  54. package/dist/_esm5.processed/core/init/utils/initialize_content_decryption.js +2 -1
  55. package/dist/_esm5.processed/core/init/utils/media_duration_updater.js +23 -19
  56. package/dist/_esm5.processed/core/init/utils/rebuffering_controller.js +1 -1
  57. package/dist/_esm5.processed/core/init/utils/stream_events_emitter/stream_events_emitter.js +6 -4
  58. package/dist/_esm5.processed/core/init/utils/throw_on_media_error.js +1 -1
  59. package/dist/_esm5.processed/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.js +7 -10
  60. package/dist/_esm5.processed/core/segment_buffers/implementations/text/html/html_text_segment_buffer.js +4 -2
  61. package/dist/_esm5.processed/core/segment_buffers/segment_buffers_store.js +13 -9
  62. package/dist/_esm5.processed/core/stream/adaptation/adaptation_stream.js +25 -16
  63. package/dist/_esm5.processed/core/stream/orchestrator/stream_orchestrator.js +26 -12
  64. package/dist/_esm5.processed/core/stream/period/period_stream.js +11 -10
  65. package/dist/_esm5.processed/core/stream/representation/representation_stream.js +15 -15
  66. package/dist/_esm5.processed/core/stream/representation/utils/append_segment_to_buffer.js +1 -1
  67. package/dist/_esm5.processed/core/stream/representation/utils/downloading_queue.js +16 -6
  68. package/dist/_esm5.processed/default_config.d.ts +16 -0
  69. package/dist/_esm5.processed/default_config.js +19 -0
  70. package/dist/_esm5.processed/experimental/features/debug_element.d.ts +8 -0
  71. package/dist/_esm5.processed/experimental/features/debug_element.js +10 -0
  72. package/dist/_esm5.processed/experimental/features/index.d.ts +1 -0
  73. package/dist/_esm5.processed/experimental/features/index.js +1 -0
  74. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.js +7 -4
  75. package/dist/_esm5.processed/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.js +8 -4
  76. package/dist/_esm5.processed/features/features_object.js +1 -0
  77. package/dist/_esm5.processed/features/initialize_features.js +13 -10
  78. package/dist/_esm5.processed/features/types.d.ts +3 -0
  79. package/dist/_esm5.processed/manifest/adaptation.js +4 -0
  80. package/dist/_esm5.processed/manifest/manifest.js +2 -0
  81. package/dist/_esm5.processed/manifest/representation.js +11 -4
  82. package/dist/_esm5.processed/parsers/manifest/dash/common/parse_adaptation_sets.js +105 -137
  83. package/dist/_esm5.processed/parsers/manifest/dash/common/parse_representations.js +25 -5
  84. package/dist/_esm5.processed/public_types.d.ts +1 -0
  85. package/dist/_esm5.processed/transports/dash/add_segment_integrity_checks_to_loader.js +15 -11
  86. package/dist/_esm5.processed/transports/dash/low_latency_segment_loader.js +2 -2
  87. package/dist/_esm5.processed/transports/dash/manifest_parser.js +1 -1
  88. package/dist/_esm5.processed/transports/dash/segment_loader.js +4 -4
  89. package/dist/_esm5.processed/transports/local/segment_loader.js +13 -26
  90. package/dist/_esm5.processed/transports/smooth/segment_loader.js +4 -4
  91. package/dist/_esm5.processed/transports/utils/call_custom_manifest_loader.js +3 -3
  92. package/dist/_esm5.processed/utils/cancellable_sleep.js +4 -10
  93. package/dist/_esm5.processed/utils/create_cancellable_promise.d.ts +26 -0
  94. package/dist/_esm5.processed/utils/create_cancellable_promise.js +52 -0
  95. package/dist/_esm5.processed/utils/reference.js +6 -0
  96. package/dist/_esm5.processed/utils/request/xhr.js +1 -1
  97. package/dist/_esm5.processed/utils/task_canceller.d.ts +34 -15
  98. package/dist/_esm5.processed/utils/task_canceller.js +55 -22
  99. package/dist/rx-player.js +880 -677
  100. package/dist/rx-player.min.js +1 -1
  101. package/jest.config.js +1 -0
  102. package/package.json +19 -18
  103. package/scripts/build/constants.d.ts +1 -0
  104. package/scripts/report_build_sizes +4 -0
  105. package/sonar-project.properties +1 -1
  106. package/src/compat/browser_detection.ts +99 -52
  107. package/src/compat/can_reuse_media_keys.ts +5 -2
  108. package/src/compat/eme/close_session.ts +2 -2
  109. package/src/compat/event_listeners.ts +1 -1
  110. package/src/core/adaptive/adaptive_representation_selector.ts +4 -2
  111. package/src/core/api/debug/buffer_graph.ts +247 -0
  112. package/src/core/api/debug/buffer_size_graph.ts +130 -0
  113. package/src/core/api/debug/constants.ts +2 -0
  114. package/src/core/api/debug/index.ts +3 -0
  115. package/src/core/api/debug/modules/general_info.ts +204 -0
  116. package/src/core/api/debug/modules/segment_buffer_content.ts +155 -0
  117. package/src/core/api/debug/modules/segment_buffer_size.ts +48 -0
  118. package/src/core/api/debug/render.ts +40 -0
  119. package/src/core/api/debug/utils.ts +103 -0
  120. package/src/core/api/playback_observer.ts +4 -2
  121. package/src/core/api/public_api.ts +28 -14
  122. package/src/core/api/utils.ts +3 -3
  123. package/src/core/decrypt/__tests__/__global__/utils.ts +61 -40
  124. package/src/core/decrypt/attach_media_keys.ts +1 -1
  125. package/src/core/decrypt/content_decryptor.ts +1 -1
  126. package/src/core/decrypt/find_key_system.ts +36 -35
  127. package/src/core/decrypt/session_events_listener.ts +57 -56
  128. package/src/core/decrypt/utils/check_key_statuses.ts +6 -0
  129. package/src/core/fetchers/cdn_prioritizer.ts +18 -9
  130. package/src/core/fetchers/manifest/manifest_fetcher.ts +5 -4
  131. package/src/core/fetchers/segment/segment_fetcher.ts +36 -14
  132. package/src/core/fetchers/segment/task_prioritizer.ts +25 -30
  133. package/src/core/fetchers/utils/schedule_request.ts +18 -7
  134. package/src/core/init/directfile_content_initializer.ts +1 -1
  135. package/src/core/init/media_source_content_initializer.ts +37 -41
  136. package/src/core/init/utils/content_time_boundaries_observer.ts +2 -2
  137. package/src/core/init/utils/create_media_source.ts +4 -16
  138. package/src/core/init/utils/end_of_stream.ts +6 -3
  139. package/src/core/init/utils/get_loaded_reference.ts +2 -1
  140. package/src/core/init/utils/initial_seek_and_play.ts +9 -5
  141. package/src/core/init/utils/initialize_content_decryption.ts +2 -1
  142. package/src/core/init/utils/media_duration_updater.ts +25 -20
  143. package/src/core/init/utils/rebuffering_controller.ts +1 -1
  144. package/src/core/init/utils/stream_events_emitter/stream_events_emitter.ts +6 -4
  145. package/src/core/init/utils/throw_on_media_error.ts +1 -1
  146. package/src/core/segment_buffers/implementations/audio_video/audio_video_segment_buffer.ts +7 -11
  147. package/src/core/segment_buffers/implementations/text/html/html_text_segment_buffer.ts +4 -2
  148. package/src/core/segment_buffers/segment_buffers_store.ts +16 -13
  149. package/src/core/stream/adaptation/adaptation_stream.ts +28 -19
  150. package/src/core/stream/orchestrator/stream_orchestrator.ts +26 -12
  151. package/src/core/stream/period/period_stream.ts +11 -10
  152. package/src/core/stream/representation/representation_stream.ts +15 -15
  153. package/src/core/stream/representation/utils/append_segment_to_buffer.ts +1 -1
  154. package/src/core/stream/representation/utils/downloading_queue.ts +16 -4
  155. package/src/default_config.ts +30 -9
  156. package/src/experimental/features/__tests__/debug_element.test.ts +26 -0
  157. package/src/experimental/features/debug_element.ts +13 -0
  158. package/src/experimental/features/index.ts +1 -0
  159. package/src/experimental/tools/VideoThumbnailLoader/prepare_source_buffer.ts +7 -4
  160. package/src/experimental/tools/VideoThumbnailLoader/video_thumbnail_loader.ts +8 -4
  161. package/src/features/__tests__/initialize_features.test.ts +11 -0
  162. package/src/features/features_object.ts +1 -0
  163. package/src/features/initialize_features.ts +15 -10
  164. package/src/features/types.ts +9 -0
  165. package/src/manifest/adaptation.ts +7 -0
  166. package/src/manifest/manifest.ts +4 -0
  167. package/src/manifest/representation.ts +10 -4
  168. package/src/parsers/manifest/dash/common/parse_adaptation_sets.ts +116 -151
  169. package/src/parsers/manifest/dash/common/parse_representations.ts +21 -4
  170. package/src/public_types.ts +1 -0
  171. package/src/transports/dash/add_segment_integrity_checks_to_loader.ts +31 -22
  172. package/src/transports/dash/low_latency_segment_loader.ts +2 -2
  173. package/src/transports/dash/manifest_parser.ts +1 -1
  174. package/src/transports/dash/segment_loader.ts +4 -4
  175. package/src/transports/local/segment_loader.ts +14 -30
  176. package/src/transports/smooth/segment_loader.ts +4 -4
  177. package/src/transports/utils/call_custom_manifest_loader.ts +3 -3
  178. package/src/typings/globals.d.ts +2 -0
  179. package/src/utils/cancellable_sleep.ts +5 -14
  180. package/src/utils/create_cancellable_promise.ts +69 -0
  181. package/src/utils/reference.ts +6 -0
  182. package/src/utils/request/xhr.ts +1 -1
  183. package/src/utils/task_canceller.ts +63 -34
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Maximum history of the buffer size that will be displayed, in milliseconds.
3
+ * For example, a value of `3000` indicates that we will just show at most the
4
+ * buffer size evolution during the last 3 seconds.
5
+ */
6
+ const TIME_SAMPLES_MS = 30000;
7
+
8
+ /**
9
+ * At minimum, that value will be taken in the chart as a maximum buffer size,
10
+ * in seconds.
11
+ * If samples go higher than this size, the chart will adapt automatically to
12
+ * a higher scale.
13
+ * However if values go below that value, the chart won't scale down more than
14
+ * this.
15
+ */
16
+ const MINIMUM_MAX_BUFFER_SIZE = 20;
17
+
18
+ export default class BufferSizeGraph {
19
+ private _history : IHistoryItem[];
20
+
21
+ /** Canvas that will contain the buffer size graph itself. */
22
+ private readonly _canvasElt : HTMLCanvasElement;
23
+
24
+ private readonly _canvasCtxt : CanvasRenderingContext2D | null;
25
+
26
+ constructor(canvasElt : HTMLCanvasElement) {
27
+ this._canvasElt = canvasElt;
28
+ this._canvasCtxt = this._canvasElt.getContext("2d");
29
+ this._history = [];
30
+ }
31
+
32
+ public pushBufferSize(bufferSize : number) : void {
33
+ const now = performance.now();
34
+ this._history.push({ timestamp: now, bufferSize });
35
+ if (this._history.length > 0) {
36
+ const minimumTime = now - TIME_SAMPLES_MS;
37
+ let i;
38
+ for (i = this._history.length - 1; i >= 1; i--) {
39
+ if (this._history[i].timestamp <= minimumTime) {
40
+ break;
41
+ }
42
+ }
43
+ this._history = this._history.slice(i);
44
+ } else {
45
+ this._history = [];
46
+ }
47
+ }
48
+
49
+ public clear() {
50
+ if (this._canvasCtxt !== null) {
51
+ this._canvasCtxt.clearRect(0, 0, this._canvasElt.width, this._canvasElt.height);
52
+ }
53
+ }
54
+
55
+ public reRender(width : number, height : number) : void {
56
+ this._canvasElt.style.width = `${width}px`;
57
+ this._canvasElt.style.height = `${height}px`;
58
+ this._canvasElt.width = width;
59
+ this._canvasElt.height = height;
60
+ this.clear();
61
+ const history = this._history;
62
+ const canvasCtx = this._canvasCtxt;
63
+
64
+ if (history.length === 0) {
65
+ return;
66
+ }
67
+
68
+ const currentMaxSize = getNewMaxBufferSize();
69
+ const minDate = history[0].timestamp;
70
+
71
+ const gridHeight = height / currentMaxSize;
72
+ const gridWidth = width / TIME_SAMPLES_MS;
73
+
74
+ drawData();
75
+
76
+ /**
77
+ * Get more appropriate maximum buffer size to put on top of the graph
78
+ * according to current history.
79
+ */
80
+ function getNewMaxBufferSize() {
81
+ const maxPoint = Math.max(...history.map(d => d.bufferSize));
82
+ return Math.max(maxPoint + 5, MINIMUM_MAX_BUFFER_SIZE);
83
+ }
84
+
85
+ /**
86
+ * Draw all data contained in `history` in the canvas given.
87
+ */
88
+ function drawData() {
89
+ if (canvasCtx === null) {
90
+ return;
91
+ }
92
+ canvasCtx.beginPath();
93
+ canvasCtx.fillStyle = "rgb(200, 100, 200)";
94
+ for (let i = 1; i < history.length; i++) {
95
+ const diff = dateToX(history[i].timestamp) -
96
+ dateToX(history[i - 1].timestamp);
97
+ const y = height - bufferValueToHeight(history[i].bufferSize);
98
+ canvasCtx.fillRect(
99
+ dateToX(history[i - 1].timestamp),
100
+ y,
101
+ diff,
102
+ height);
103
+ }
104
+ canvasCtx.stroke();
105
+ }
106
+
107
+ /**
108
+ * Convert a value of a given data point, to a u coordinate in the canvas.
109
+ * @param {number} bufferVal - Value to convert
110
+ * @returns {number} - y coordinate
111
+ */
112
+ function bufferValueToHeight(bufferVal : number) : number {
113
+ return height - (currentMaxSize - bufferVal) * gridHeight;
114
+ }
115
+
116
+ /**
117
+ * Convert a date of a given data point, to a x coordinate in the canvas.
118
+ * @param {number} date - Date to convert, in milliseconds
119
+ * @returns {number} - x coordinate
120
+ */
121
+ function dateToX(date : number) : number {
122
+ return (date - minDate) * gridWidth;
123
+ }
124
+ }
125
+ }
126
+
127
+ interface IHistoryItem {
128
+ timestamp : number;
129
+ bufferSize : number;
130
+ }
@@ -0,0 +1,2 @@
1
+ /** Interval at which the various debug metrics will be refreshed. */
2
+ export const DEFAULT_REFRESH_INTERVAL = 1000;
@@ -0,0 +1,3 @@
1
+ import renderDebugElement from "./render";
2
+
3
+ export default renderDebugElement;
@@ -0,0 +1,204 @@
1
+ import { CancellationSignal } from "../../../../utils/task_canceller";
2
+ import RxPlayer from "../../public_api";
3
+ import { DEFAULT_REFRESH_INTERVAL } from "../constants";
4
+ import {
5
+ createCompositeElement,
6
+ createElement,
7
+ createMetricTitle,
8
+ isExtendedMode,
9
+ } from "../utils";
10
+
11
+ export default function constructDebugGeneralInfo(
12
+ instance : RxPlayer,
13
+ parentElt : HTMLElement,
14
+ cancelSignal : CancellationSignal
15
+ ) : HTMLElement {
16
+ const generalInfoElt = createElement("div");
17
+ const adaptationsElt = createElement("div");
18
+ const representationsElt = createElement("div");
19
+ updateGeneralInfo();
20
+ const generalInfoItv = setInterval(() => {
21
+ updateGeneralInfo();
22
+ }, DEFAULT_REFRESH_INTERVAL);
23
+ cancelSignal.register(() => {
24
+ clearInterval(generalInfoItv);
25
+ });
26
+
27
+ return createCompositeElement("div", [
28
+ generalInfoElt,
29
+ adaptationsElt,
30
+ representationsElt,
31
+ ]);
32
+ function updateGeneralInfo() {
33
+ const videoElement = instance.getVideoElement();
34
+ if (videoElement === null) {
35
+ // disposed player. Clean-up everything
36
+ generalInfoElt.innerHTML = "";
37
+ adaptationsElt.innerHTML = "";
38
+ representationsElt.innerHTML = "";
39
+ clearInterval(generalInfoItv);
40
+ return;
41
+ } else {
42
+ const currentTime = instance.getPosition();
43
+ const bufferGap = instance.getVideoBufferGap();
44
+ const bufferGapStr = bufferGap === Infinity ? "0" : bufferGap.toFixed(2);
45
+ const valuesLine1 : Array<[string, string]> = [
46
+ ["ct", currentTime.toFixed(2)],
47
+ ["bg", bufferGapStr],
48
+ ["rs", String(videoElement.readyState)],
49
+ ["pr", String(videoElement.playbackRate)],
50
+ ["sp", String(instance.getPlaybackRate())],
51
+ ["pa", String(videoElement.paused ? 1 : 0)],
52
+ ["en", String(videoElement.ended ? 1 : 0)],
53
+ ["li", String(instance.isLive() ? 1 : 0)],
54
+ ["wba", String(instance.getWantedBufferAhead())],
55
+ ["st", `"${instance.getPlayerState()}"`],
56
+ ];
57
+
58
+ const valuesLine2 : Array<[string, string]> = [];
59
+ const ks = instance.getKeySystemConfiguration();
60
+ if (ks !== null) {
61
+ valuesLine2.push(["ks", ks.keySystem]);
62
+ }
63
+ const mbb = instance.getMaxBufferBehind();
64
+ if (mbb !== Infinity) {
65
+ valuesLine2.push(["mbb", String(mbb)]);
66
+ }
67
+ const mba = instance.getMaxBufferAhead();
68
+ if (mba !== Infinity) {
69
+ valuesLine2.push(["mba", String(mba)]);
70
+ }
71
+ const mia = instance.getMinAudioBitrate();
72
+ if (mia !== 0) {
73
+ valuesLine2.push(["mia", String(mia)]);
74
+ }
75
+ const miv = instance.getMinVideoBitrate();
76
+ if (miv !== 0) {
77
+ valuesLine2.push(["miv", String(miv)]);
78
+ }
79
+ const maa = instance.getMaxAudioBitrate();
80
+ if (maa !== Infinity) {
81
+ valuesLine2.push(["maa", String(maa)]);
82
+ }
83
+ const mav = instance.getMaxVideoBitrate();
84
+ if (mav !== Infinity) {
85
+ valuesLine2.push(["mav", String(mav)]);
86
+ }
87
+ const fab = instance.getManualAudioBitrate();
88
+ if (fab >= 0) {
89
+ valuesLine2.push(["fab", String(fab)]);
90
+ }
91
+ const fvb = instance.getManualVideoBitrate();
92
+ if (fvb >= 0) {
93
+ valuesLine2.push(["fvb", String(fvb)]);
94
+ }
95
+ const mbs = instance.getMaxVideoBufferSize();
96
+ if (mbs !== Infinity) {
97
+ valuesLine2.push(["mbs", String(mbs)]);
98
+ }
99
+ const minPos = instance.getMinimumPosition();
100
+ if (minPos !== null) {
101
+ valuesLine1.push(["mip", minPos.toFixed(2)]);
102
+ valuesLine2.push(["dmi", (currentTime - minPos).toFixed(2)]);
103
+ }
104
+ const maxPos = instance.getMaximumPosition();
105
+ if (maxPos !== null) {
106
+ valuesLine1.push(["map", maxPos.toFixed(2)]);
107
+ valuesLine2.push(["dma", (maxPos - currentTime).toFixed(2)]);
108
+ }
109
+ const valuesLine3 : Array<[string, string]> = [];
110
+ const error = instance.getError();
111
+ if (error !== null) {
112
+ valuesLine3.push(["er", `"${String(error)}"`]);
113
+ }
114
+ generalInfoElt.innerHTML = "";
115
+ for (const valueSet of [valuesLine1, valuesLine2, valuesLine3]) {
116
+ if (valueSet.length > 0) {
117
+ const lineInfoElt = createElement("div");
118
+ for (const value of valueSet) {
119
+ lineInfoElt.appendChild(createMetricTitle(value[0]));
120
+ lineInfoElt.appendChild(createElement("span", {
121
+ textContent: value[1] + " ",
122
+ }));
123
+ }
124
+ generalInfoElt.appendChild(lineInfoElt);
125
+ }
126
+ }
127
+ if (isExtendedMode(parentElt)) {
128
+ const url = instance.getUrl();
129
+ if (url !== undefined) {
130
+ const reducedUrl = url.length > 100 ?
131
+ url.substring(0, 99) + "…" :
132
+ url;
133
+
134
+ generalInfoElt.appendChild(createCompositeElement("div", [
135
+ createMetricTitle("url"),
136
+ createElement("span", {
137
+ textContent: reducedUrl,
138
+ }),
139
+ ]));
140
+ }
141
+ }
142
+ }
143
+ if (isExtendedMode(parentElt)) {
144
+ const videoId = instance.getAvailableVideoTracks().map(({ id, active }) =>
145
+ active ? `*${id}` : id);
146
+ const audioId = instance.getAvailableAudioTracks().map(({ id, active }) =>
147
+ active ? `*${id}` : id);
148
+ const textId = instance.getAvailableTextTracks().map(({ id, active }) =>
149
+ active ? `*${id}` : id);
150
+ adaptationsElt.innerHTML = "";
151
+ if (videoId.length > 0) {
152
+ let textContent = `${videoId.length}:${videoId.join(" ")} `;
153
+ if (textContent.length > 100) {
154
+ textContent = textContent.substring(0, 98) + "… ";
155
+ }
156
+ const videoAdaps = createCompositeElement("div", [
157
+ createMetricTitle("vt"),
158
+ createElement("span", { textContent }),
159
+ ]);
160
+ adaptationsElt.appendChild(videoAdaps);
161
+ }
162
+ if (audioId.length > 0) {
163
+ let textContent = `${audioId.length}:${audioId.join(" ")} `;
164
+ if (textContent.length > 100) {
165
+ textContent = textContent.substring(0, 98) + "… ";
166
+ }
167
+ const audioAdaps = createCompositeElement("div", [
168
+ createMetricTitle("at"),
169
+ createElement("span", { textContent }),
170
+ ]);
171
+ adaptationsElt.appendChild(audioAdaps);
172
+ }
173
+ if (textId.length > 0) {
174
+ let textContent = `${textId.length}:${textId.join(" ")} `;
175
+ if (textContent.length > 100) {
176
+ textContent = textContent.substring(0, 98) + "… ";
177
+ }
178
+ const textAdaps = createCompositeElement("div", [
179
+ createMetricTitle("tt"),
180
+ createElement("span", { textContent }),
181
+ ]);
182
+ adaptationsElt.appendChild(textAdaps);
183
+ }
184
+ const videoBitrates = instance.getAvailableVideoBitrates();
185
+ const audioBitrates = instance.getAvailableAudioBitrates();
186
+ representationsElt.innerHTML = "";
187
+ if (videoBitrates.length > 0) {
188
+ representationsElt.appendChild(createMetricTitle("vb"));
189
+ representationsElt.appendChild(createElement("span", {
190
+ textContent: videoBitrates.join(" ") + " ",
191
+ }));
192
+ }
193
+ if (audioBitrates.length > 0) {
194
+ representationsElt.appendChild(createMetricTitle("ab"));
195
+ representationsElt.appendChild(createElement("span", {
196
+ textContent: audioBitrates.join(" ") + " ",
197
+ }));
198
+ }
199
+ } else {
200
+ adaptationsElt.innerHTML = "";
201
+ representationsElt.innerHTML = "";
202
+ }
203
+ }
204
+ }
@@ -0,0 +1,155 @@
1
+ import {
2
+ IAdaptation,
3
+ IPeriod,
4
+ IRepresentation,
5
+ } from "../../../../public_types";
6
+ import isNullOrUndefined from "../../../../utils/is_null_or_undefined";
7
+ import { CancellationSignal } from "../../../../utils/task_canceller";
8
+ import { IBufferType } from "../../../segment_buffers";
9
+ import RxPlayer from "../../public_api";
10
+ import SegmentBufferGraph from "../buffer_graph";
11
+ import { DEFAULT_REFRESH_INTERVAL } from "../constants";
12
+ import {
13
+ createElement,
14
+ createGraphCanvas,
15
+ createMetricTitle,
16
+ isExtendedMode,
17
+ } from "../utils";
18
+
19
+ export default function createSegmentBufferGraph(
20
+ instance : RxPlayer,
21
+ bufferType : IBufferType,
22
+ title : string,
23
+ parentElt : HTMLElement,
24
+ cancelSignal : CancellationSignal
25
+ ) : HTMLElement {
26
+ const bufferGraphWrapper = createElement("div");
27
+ const bufferTitle = createMetricTitle(title);
28
+ const canvasElt = createGraphCanvas();
29
+ const currentRangeRepInfoElt = createElement("div");
30
+ const loadingRangeRepInfoElt = createElement("div");
31
+ const bufferGraph = new SegmentBufferGraph(canvasElt);
32
+ const intervalId = setInterval(update, DEFAULT_REFRESH_INTERVAL);
33
+ cancelSignal.register(() => {
34
+ clearInterval(intervalId);
35
+ });
36
+ bufferGraphWrapper.appendChild(bufferTitle);
37
+ bufferGraphWrapper.appendChild(canvasElt);
38
+ bufferGraphWrapper.appendChild(currentRangeRepInfoElt);
39
+ bufferGraphWrapper.appendChild(loadingRangeRepInfoElt);
40
+ bufferGraphWrapper.style.padding = "5px 0px";
41
+ update();
42
+ return bufferGraphWrapper;
43
+
44
+ function update() {
45
+ if (instance.getVideoElement() === null) {
46
+ // disposed player. Clean-up everything
47
+ bufferGraphWrapper.style.display = "none";
48
+ bufferGraphWrapper.innerHTML = "";
49
+ clearInterval(intervalId);
50
+ return;
51
+ }
52
+ const showAllInfo = isExtendedMode(parentElt);
53
+ const inventory = instance.__priv_getSegmentBufferContent(bufferType);
54
+ if (inventory === null) {
55
+ bufferGraphWrapper.style.display = "none";
56
+ currentRangeRepInfoElt.innerHTML = "";
57
+ loadingRangeRepInfoElt.innerHTML = "";
58
+ } else {
59
+ bufferGraphWrapper.style.display = "block";
60
+ const currentTime = instance.getPosition();
61
+ const width = Math.min(parentElt.clientWidth - 150, 600);
62
+ bufferGraph.update({
63
+ currentTime,
64
+ minimumPosition: instance.getMinimumPosition() ?? undefined,
65
+ maximumPosition: instance.getMaximumPosition() ?? undefined,
66
+ inventory,
67
+ width,
68
+ height: 10,
69
+ });
70
+
71
+ if (!showAllInfo) {
72
+ currentRangeRepInfoElt.innerHTML = "";
73
+ loadingRangeRepInfoElt.innerHTML = "";
74
+ return;
75
+ }
76
+
77
+ currentRangeRepInfoElt.innerHTML = "";
78
+ for (let i = 0; i < inventory.length; i++) {
79
+ const rangeInfo = inventory[i];
80
+ const { bufferedStart, bufferedEnd, infos } = rangeInfo;
81
+ if (bufferedStart !== undefined && bufferedEnd !== undefined &&
82
+ currentTime >= bufferedStart && currentTime < bufferedEnd)
83
+ {
84
+ currentRangeRepInfoElt.appendChild(createMetricTitle("play"));
85
+ currentRangeRepInfoElt.appendChild(createElement("span", {
86
+ textContent: constructRepresentationInfo(infos),
87
+ }));
88
+ break;
89
+ }
90
+ }
91
+
92
+ loadingRangeRepInfoElt.innerHTML = "";
93
+ const rep = instance.getCurrentRepresentations()?.[bufferType];
94
+ const adap = instance.getCurrentAdaptations()?.[bufferType];
95
+ const manifest = instance.getManifest();
96
+ if (manifest !== null && !isNullOrUndefined(rep) && !isNullOrUndefined(adap)) {
97
+ const period = manifest.getPeriodForTime(currentTime);
98
+ if (period !== undefined) {
99
+ loadingRangeRepInfoElt.appendChild(createMetricTitle("load"));
100
+ loadingRangeRepInfoElt.appendChild(createElement("span", {
101
+ textContent: constructRepresentationInfo({
102
+ period,
103
+ adaptation: adap,
104
+ representation: rep,
105
+ }),
106
+ }));
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+
113
+ function constructRepresentationInfo(
114
+ content : {
115
+ period : IPeriod;
116
+ adaptation : IAdaptation;
117
+ representation : IRepresentation;
118
+ }
119
+ ) : string {
120
+ const period = content.period;
121
+ const { language,
122
+ isAudioDescription,
123
+ isClosedCaption,
124
+ isTrickModeTrack,
125
+ isSignInterpreted,
126
+ type: bufferType } = content.adaptation;
127
+ const { id, height, width, bitrate, codec } = content.representation;
128
+ let representationInfo = `"${id}" `;
129
+ if (height !== undefined && width !== undefined) {
130
+ representationInfo += `${width}x${height} `;
131
+ }
132
+ if (bitrate !== undefined) {
133
+ representationInfo += `(${(bitrate / 1000).toFixed(0)}kbps) `;
134
+ }
135
+ if (codec !== undefined) {
136
+ representationInfo += `c:"${codec}" `;
137
+ }
138
+ if (language !== undefined) {
139
+ representationInfo += `l:"${language}" `;
140
+ }
141
+ if (bufferType === "video" && typeof isSignInterpreted === "boolean") {
142
+ representationInfo += `si:${isSignInterpreted ? 1 : 0} `;
143
+ }
144
+ if (bufferType === "video" && typeof isTrickModeTrack === "boolean") {
145
+ representationInfo += `tm:${isTrickModeTrack ? 1 : 0} `;
146
+ }
147
+ if (bufferType === "audio" && typeof isAudioDescription === "boolean") {
148
+ representationInfo += `ad:${isAudioDescription ? 1 : 0} `;
149
+ }
150
+ if (bufferType === "text" && typeof isClosedCaption === "boolean") {
151
+ representationInfo += `cc:${isClosedCaption ? 1 : 0} `;
152
+ }
153
+ representationInfo += `p:${period.start}-${period.end ?? "?"}`;
154
+ return representationInfo;
155
+ }
@@ -0,0 +1,48 @@
1
+ import { CancellationSignal } from "../../../../utils/task_canceller";
2
+ import RxPlayer from "../../public_api";
3
+ import BufferSizeGraph from "../buffer_size_graph";
4
+ import { DEFAULT_REFRESH_INTERVAL } from "../constants";
5
+ import {
6
+ createElement,
7
+ createGraphCanvas,
8
+ createMetricTitle,
9
+ } from "../utils";
10
+
11
+ export default function createSegmentBufferSizeGraph(
12
+ instance : RxPlayer,
13
+ parentElt : HTMLElement,
14
+ cancelSignal : CancellationSignal
15
+ ) : HTMLElement {
16
+ const bufferSizeGraphWrapperElt = createElement("div");
17
+ const bufferSizeTitle = createMetricTitle("bgap");
18
+ const canvasElt = createGraphCanvas();
19
+
20
+ const bufferSizeGraph = new BufferSizeGraph(canvasElt);
21
+ const intervalId = setInterval(addBufferSize, DEFAULT_REFRESH_INTERVAL);
22
+ cancelSignal.register(() => {
23
+ clearInterval(intervalId);
24
+ });
25
+ bufferSizeGraphWrapperElt.appendChild(bufferSizeTitle);
26
+ bufferSizeGraphWrapperElt.appendChild(canvasElt);
27
+ bufferSizeGraphWrapperElt.style.padding = "7px 0px";
28
+ addBufferSize();
29
+ return bufferSizeGraphWrapperElt;
30
+
31
+ function addBufferSize() {
32
+ if (instance.getVideoElement() === null) {
33
+ // disposed player. Clean-up everything
34
+ bufferSizeGraphWrapperElt.innerHTML = "";
35
+ clearInterval(intervalId);
36
+ return;
37
+ }
38
+ const bufferGap = instance.getVideoBufferGap();
39
+ if (bufferGap === Infinity) {
40
+ bufferSizeGraph.pushBufferSize(0);
41
+ } else {
42
+ bufferSizeGraph.pushBufferSize(bufferGap);
43
+ }
44
+ const width = Math.min(parentElt.clientWidth - 150, 600);
45
+ bufferSizeGraph.reRender(width, 10);
46
+ }
47
+ }
48
+
@@ -0,0 +1,40 @@
1
+ import { CancellationSignal } from "../../../utils/task_canceller";
2
+ import RxPlayer from "../public_api";
3
+ import constructDebugGeneralInfo from "./modules/general_info";
4
+ import createSegmentBufferGraph from "./modules/segment_buffer_content";
5
+ import createSegmentBufferSizeGraph from "./modules/segment_buffer_size";
6
+ import { createCompositeElement, createElement } from "./utils";
7
+
8
+ export default function renderDebugElement(
9
+ parentElt : HTMLElement,
10
+ instance : RxPlayer,
11
+ cancelSignal : CancellationSignal
12
+ ) : void {
13
+ const debugElementTitleElt = createElement("div", {
14
+ textContent: "RxPlayer Debug Information",
15
+ });
16
+ debugElementTitleElt.style.fontWeight = "bold";
17
+ debugElementTitleElt.style.borderBottom = "1px solid white";
18
+ debugElementTitleElt.style.marginBottom = "5px";
19
+ debugElementTitleElt.style.fontStyle = "italic";
20
+
21
+ const debugWrapperElt = createCompositeElement("div", [
22
+ debugElementTitleElt,
23
+ constructDebugGeneralInfo(instance, parentElt, cancelSignal),
24
+ createSegmentBufferGraph(instance, "video", "vbuf", parentElt, cancelSignal),
25
+ createSegmentBufferGraph(instance, "audio", "abuf", parentElt, cancelSignal),
26
+ createSegmentBufferGraph(instance, "text", "tbuf", parentElt, cancelSignal),
27
+ createSegmentBufferSizeGraph(instance, parentElt, cancelSignal),
28
+ ]);
29
+ debugWrapperElt.style.backgroundColor = "#00000099";
30
+ debugWrapperElt.style.padding = "7px";
31
+ debugWrapperElt.style.fontSize = "13px";
32
+ debugWrapperElt.style.fontFamily = "mono";
33
+ debugWrapperElt.style.color = "white";
34
+ debugWrapperElt.style.display = "inline-block";
35
+ debugWrapperElt.style.bottom = "0px";
36
+ parentElt.appendChild(debugWrapperElt);
37
+ cancelSignal.register(() => {
38
+ parentElt.removeChild(debugWrapperElt);
39
+ });
40
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Create an HTML element.
3
+ * @param {string} elementName - The element's name, like `"div"` for example.
4
+ * @param {Object} [options={}] - Optional attributes for the element.
5
+ * @param {string} [options.textContent] - Inner text for that element.
6
+ * @param {string} [options.className] - Value for a `class` attribute
7
+ * associated to this element.
8
+ * @param {string} [options.href] - Value for a `href` attribute
9
+ * associated to this element.
10
+ * @returns {HTMLElement}
11
+ */
12
+ export function createElement(
13
+ elementName : "input",
14
+ opts? : CreateElementOptions | undefined
15
+ ) : HTMLInputElement;
16
+ export function createElement(
17
+ elementName : "button",
18
+ opts? : CreateElementOptions | undefined
19
+ ) : HTMLButtonElement;
20
+ export function createElement(
21
+ elementName : "a",
22
+ opts? : CreateElementOptions | undefined
23
+ ) : HTMLLinkElement;
24
+ export function createElement(
25
+ elementName : "canvas",
26
+ opts? : CreateElementOptions | undefined
27
+ ) : HTMLCanvasElement;
28
+ export function createElement(
29
+ elementName : string,
30
+ opts? : CreateElementOptions | undefined
31
+ ) : HTMLElement;
32
+ export function createElement(
33
+ elementName : string,
34
+ {
35
+ textContent,
36
+ className,
37
+ } : CreateElementOptions | undefined = {}
38
+ ) : HTMLElement {
39
+ const elt = document.createElement(elementName);
40
+ if (className !== undefined) {
41
+ elt.className = className;
42
+ }
43
+ if (textContent !== undefined) {
44
+ elt.textContent = textContent;
45
+ }
46
+ return elt;
47
+ }
48
+
49
+ interface CreateElementOptions {
50
+ textContent? : string | undefined;
51
+ className? : string | undefined;
52
+ }
53
+
54
+ /**
55
+ * Create an HTML element which may contain mutiple HTML sub-elements.
56
+ * @param {string} rootElementName - The element's name, like `"div"` for
57
+ * example.
58
+ * @param {Array.<string|HTMLElement>} parts - The HTML sub-elements, in order.
59
+ * Those can also just be strings, in which case only text nodes (and no actual
60
+ * HTMLElement) will be added at this place.
61
+ * @param {Object} [options={}] - Optional attributes for the element.
62
+ * @param {string} [options.className] - Value for a `class` attribute
63
+ * associated to this element.
64
+ * @returns {HTMLElement}
65
+ */
66
+ export function createCompositeElement(
67
+ rootElementName : string,
68
+ parts : Array<HTMLElement | string>,
69
+ { className } : { className? : string } | undefined = {}
70
+ ) : HTMLElement {
71
+ const elt = document.createElement(rootElementName);
72
+ if (className !== undefined) {
73
+ elt.className = className;
74
+ }
75
+ for (const subElt of parts) {
76
+ if (typeof subElt === "string") {
77
+ elt.appendChild(document.createTextNode(subElt));
78
+ } else {
79
+ elt.appendChild(subElt);
80
+ }
81
+ }
82
+ return elt;
83
+ }
84
+
85
+ export function isExtendedMode(parentElt : HTMLElement) : boolean {
86
+ return parentElt.clientHeight > 400;
87
+ }
88
+
89
+ export function createMetricTitle(title : string) : HTMLElement {
90
+ const elt = createElement("span", {
91
+ textContent: title + "/",
92
+ });
93
+ elt.style.fontWeight = "bold";
94
+ return elt;
95
+ }
96
+
97
+ export function createGraphCanvas() : HTMLCanvasElement {
98
+ const canvasElt = createElement("canvas");
99
+ canvasElt.style.border = "1px solid white";
100
+ canvasElt.style.height = "15px";
101
+ canvasElt.style.marginLeft = "2px";
102
+ return canvasElt;
103
+ }