@@ -40,6 +40,13 @@ const DEFAULT_CONFIG: SmartSamplerConfig = {
4040const HASH_DUP_THRESHOLD = 5 ;
4141/** 渐进板书帧(变化触发且 0 < score < 0.3)收紧阈值:距离 ≤ 2 才跳过,避免漏采渐进内容 */
4242const WRITING_HASH_DUP_THRESHOLD = 2 ;
43+ /**
44+ * 内存上限:仅最近 N 个关键帧保留 imageBase64,更早的剥离为空串。
45+ * 单帧 base64 可达数百 KB,长时间采集(1h 约 240 帧)若无界持有会
46+ * 在渲染进程累积数十 MB 且不释放(仅 reset 才清空)。更早帧的图片
47+ * 已由 keyframe_save 增量落盘,元数据(id/timestamp/changeType)保留。
48+ */
49+ const MAX_IMAGE_KEYFRAMES = 30 ;
4350
4451// ================================================================
4552// SmartSampler
@@ -140,6 +147,10 @@ export class SmartSampler {
140147 this . keyframes . push ( keyframe ) ;
141148 this . lastCaptureTime = now ;
142149 if ( hash !== null ) this . lastFrameHash = hash ;
150+
151+ // 内存上限:剥离过早关键帧的 base64(已落盘),避免长时间采集内存无界增长
152+ this . trimOldKeyframeImages ( ) ;
153+
143154 return keyframe ;
144155 }
145156
@@ -155,6 +166,21 @@ export class SmartSampler {
155166 this . lastFrameHash = null ;
156167 }
157168
169+ /**
170+ * 剥离过早关键帧的 imageBase64(保留元数据),将内存占用控制在
171+ * MAX_IMAGE_KEYFRAMES 帧以内。用新对象替换避免突变已发射的事件引用。
172+ */
173+ private trimOldKeyframeImages ( ) : void {
174+ const n = this . keyframes . length ;
175+ if ( n <= MAX_IMAGE_KEYFRAMES ) return ;
176+ const cutoff = n - MAX_IMAGE_KEYFRAMES ;
177+ for ( let i = 0 ; i < cutoff ; i ++ ) {
178+ if ( this . keyframes [ i ] . imageBase64 ) {
179+ this . keyframes [ i ] = { ...this . keyframes [ i ] , imageBase64 : '' } ;
180+ }
181+ }
182+ }
183+
158184 // ================================================================
159185 // 私有方法
160186 // ================================================================
0 commit comments