@xiboplayer/renderer 0.7.6 → 0.7.7
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/package.json +4 -4
- package/src/renderer-lite.js +77 -17
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xiboplayer/renderer",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.7",
|
|
4
4
|
"description": "RendererLite - Fast, efficient XLF layout rendering engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"pdfjs-dist": "^4.10.38",
|
|
15
|
-
"@xiboplayer/
|
|
16
|
-
"@xiboplayer/utils": "0.7.
|
|
17
|
-
"@xiboplayer/
|
|
15
|
+
"@xiboplayer/schedule": "0.7.7",
|
|
16
|
+
"@xiboplayer/utils": "0.7.7",
|
|
17
|
+
"@xiboplayer/cache": "0.7.7"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
20
|
"jsdom": "^25.0.1",
|
package/src/renderer-lite.js
CHANGED
|
@@ -1860,27 +1860,36 @@ export class RendererLite {
|
|
|
1860
1860
|
}
|
|
1861
1861
|
|
|
1862
1862
|
const videoEl = widgetElement.querySelector('video');
|
|
1863
|
-
if (videoEl
|
|
1863
|
+
if (videoEl) {
|
|
1864
|
+
videoEl.pause();
|
|
1864
1865
|
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1866
|
+
// Stop MediaStream tracks (webcam/mic) to release the device
|
|
1867
|
+
if (videoEl._mediaStream) {
|
|
1868
|
+
videoEl._mediaStream.getTracks().forEach(t => t.stop());
|
|
1869
|
+
videoEl._mediaStream = null;
|
|
1870
|
+
videoEl.srcObject = null;
|
|
1871
|
+
}
|
|
1871
1872
|
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1873
|
+
// Destroy HLS.js instance to free worker + buffers
|
|
1874
|
+
if (videoEl._hlsInstance) {
|
|
1875
|
+
videoEl._hlsInstance.destroy();
|
|
1876
|
+
videoEl._hlsInstance = null;
|
|
1877
|
+
}
|
|
1878
|
+
|
|
1879
|
+
// Release decoded video buffers (GPU dmabufs) — without this, paused
|
|
1880
|
+
// videos hold texture memory until the layout is evicted from the pool.
|
|
1881
|
+
// removeAttribute('src') + load() forces the browser to drop the decoded
|
|
1882
|
+
// frame, releasing GPU dmabufs immediately instead of at pool eviction.
|
|
1883
|
+
videoEl.removeAttribute('src');
|
|
1884
|
+
videoEl.load();
|
|
1877
1885
|
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1886
|
+
// Remove event listeners to prevent accumulation across widget cycles
|
|
1887
|
+
if (videoEl._eventCleanup) {
|
|
1888
|
+
for (const [event, handler] of videoEl._eventCleanup) {
|
|
1889
|
+
videoEl.removeEventListener(event, handler);
|
|
1890
|
+
}
|
|
1891
|
+
videoEl._eventCleanup = null;
|
|
1882
1892
|
}
|
|
1883
|
-
videoEl._eventCleanup = null;
|
|
1884
1893
|
}
|
|
1885
1894
|
|
|
1886
1895
|
const audioEl = widgetElement.querySelector('audio');
|
|
@@ -2863,6 +2872,13 @@ export class RendererLite {
|
|
|
2863
2872
|
return true;
|
|
2864
2873
|
}
|
|
2865
2874
|
|
|
2875
|
+
// Don't preload if already in-flight (prevents triple preload when
|
|
2876
|
+
// 75% and 90% timers both fire before the async preload completes)
|
|
2877
|
+
if (this._preloadingLayoutId === layoutId) {
|
|
2878
|
+
this.log.info(`Layout ${layoutId} preload already in-flight, skipping`);
|
|
2879
|
+
return true;
|
|
2880
|
+
}
|
|
2881
|
+
|
|
2866
2882
|
try {
|
|
2867
2883
|
this.log.info(`Preloading layout ${layoutId} into pool...`);
|
|
2868
2884
|
|
|
@@ -3095,6 +3111,50 @@ export class RendererLite {
|
|
|
3095
3111
|
}
|
|
3096
3112
|
|
|
3097
3113
|
this.log.info(`Swapped to preloaded layout ${layoutId} (instant transition)`);
|
|
3114
|
+
this._logResourceStats(layoutId);
|
|
3115
|
+
}
|
|
3116
|
+
|
|
3117
|
+
/**
|
|
3118
|
+
* Log resource allocation stats for debugging memory/GPU leaks.
|
|
3119
|
+
* Called after every layout swap to track DOM node accumulation,
|
|
3120
|
+
* video element lifecycle, and pool state.
|
|
3121
|
+
*/
|
|
3122
|
+
_logResourceStats(layoutId) {
|
|
3123
|
+
const domNodes = document.querySelectorAll('*').length;
|
|
3124
|
+
const videos = document.querySelectorAll('video').length;
|
|
3125
|
+
const videosSrc = document.querySelectorAll('video[src]').length;
|
|
3126
|
+
const canvases = document.querySelectorAll('canvas').length;
|
|
3127
|
+
const iframes = document.querySelectorAll('iframe').length;
|
|
3128
|
+
const images = document.querySelectorAll('img').length;
|
|
3129
|
+
const poolSize = this.layoutPool ? this.layoutPool.size : 0;
|
|
3130
|
+
const regionCount = this.regions ? this.regions.size : 0;
|
|
3131
|
+
const widgetElements = [...(this.regions?.values() || [])].reduce(
|
|
3132
|
+
(sum, r) => sum + (r.widgetElements?.size || 0), 0
|
|
3133
|
+
);
|
|
3134
|
+
const jsHeap = performance?.memory ? {
|
|
3135
|
+
used: Math.round(performance.memory.usedJSHeapSize / 1048576),
|
|
3136
|
+
total: Math.round(performance.memory.totalJSHeapSize / 1048576),
|
|
3137
|
+
limit: Math.round(performance.memory.jsHeapSizeLimit / 1048576),
|
|
3138
|
+
} : null;
|
|
3139
|
+
|
|
3140
|
+
// Count blob URLs still tracked (potential leak indicator)
|
|
3141
|
+
const blobUrls = this._blobUrls ? [...this._blobUrls.values()].reduce((s, set) => s + set.size, 0) : 0;
|
|
3142
|
+
const blobLayouts = this._blobUrls ? this._blobUrls.size : 0;
|
|
3143
|
+
|
|
3144
|
+
// Preload wrapper divs in DOM (should be 0-1 in normal operation)
|
|
3145
|
+
const preloadWrappers = document.querySelectorAll('.renderer-lite-preload-wrapper').length;
|
|
3146
|
+
|
|
3147
|
+
// Audio overlay elements
|
|
3148
|
+
const audioEls = document.querySelectorAll('audio').length;
|
|
3149
|
+
|
|
3150
|
+
const heapStr = jsHeap ? `heap=${jsHeap.used}/${jsHeap.total}MB (limit ${jsHeap.limit}MB)` : 'heap=N/A';
|
|
3151
|
+
this.log.info(
|
|
3152
|
+
`[Resources] layout=${layoutId} dom=${domNodes} videos=${videos}(src=${videosSrc}) ` +
|
|
3153
|
+
`canvas=${canvases} iframe=${iframes} img=${images} audio=${audioEls} ` +
|
|
3154
|
+
`pool=${poolSize} preloadWrappers=${preloadWrappers} ` +
|
|
3155
|
+
`regions=${regionCount} widgets=${widgetElements} ` +
|
|
3156
|
+
`blobs=${blobUrls}(${blobLayouts} layouts) ${heapStr}`
|
|
3157
|
+
);
|
|
3098
3158
|
}
|
|
3099
3159
|
|
|
3100
3160
|
/**
|