@@ -65,14 +65,14 @@ export class CanvasManager {
6565
6666 constructor ( options : {
6767 recordCanvas : boolean ;
68+ recordVideos : boolean ;
6869 mutationCb : canvasMutationCallback ;
6970 win : IWindow ;
7071 blockClass : blockClass ;
7172 blockSelector : string | null ;
7273 mirror : Mirror ;
7374 sampling ?: 'all' | number ;
7475 dataURLOptions : DataURLOptions ;
75- resizeQuality ?: 'pixelated' | 'low' | 'medium' | 'high' ;
7676 resizeFactor ?: number ;
7777 maxSnapshotDimension ?: number ;
7878 logger ?: {
@@ -86,6 +86,7 @@ export class CanvasManager {
8686 blockClass,
8787 blockSelector,
8888 recordCanvas,
89+ recordVideos,
8990 dataURLOptions,
9091 } = options ;
9192 this . mutationCb = options . mutationCb ;
@@ -96,29 +97,29 @@ export class CanvasManager {
9697 this . initCanvasMutationObserver ( win , blockClass , blockSelector ) ;
9798 if ( recordCanvas && typeof sampling === 'number' )
9899 this . initCanvasFPSObserver (
100+ recordVideos ,
99101 sampling ,
100102 win ,
101103 blockClass ,
102104 blockSelector ,
103105 {
104106 dataURLOptions,
105107 } ,
106- options . resizeQuality ,
107108 options . resizeFactor ,
108109 options . maxSnapshotDimension ,
109110 ) ;
110111 }
111112
112113 private debug (
113- canvas ? : HTMLCanvasElement ,
114+ element : HTMLCanvasElement | HTMLVideoElement ,
114115 ...args : Parameters < typeof console . log >
115116 ) {
116117 if ( ! this . logger ) return ;
117- let prefix = ' [highlight-canvas]' ;
118- if ( canvas ) {
119- prefix += ` [ctx:${ ( canvas as ICanvas ) . __context } ]` ;
118+ let prefix = ` [highlight-${ element . tagName . toLowerCase ( ) } ]` ;
119+ if ( element . tagName === ' canvas' ) {
120+ prefix += ` [ctx:${ ( element as ICanvas ) . __context } ]` ;
120121 }
121- this . logger . debug ( prefix , canvas , ...args ) ;
122+ this . logger . debug ( prefix , element , ...args ) ;
122123 }
123124
124125 private processMutation : canvasManagerMutationCallback = (
@@ -139,14 +140,14 @@ export class CanvasManager {
139140 } ;
140141
141142 private initCanvasFPSObserver (
143+ recordVideos : boolean ,
142144 fps : number ,
143145 win : IWindow ,
144146 blockClass : blockClass ,
145147 blockSelector : string | null ,
146148 options : {
147149 dataURLOptions : DataURLOptions ;
148150 } ,
149- resizeQuality ?: 'pixelated' | 'low' | 'medium' | 'high' ,
150151 resizeFactor ?: number ,
151152 maxSnapshotDimension ?: number ,
152153 ) {
@@ -164,14 +165,14 @@ export class CanvasManager {
164165
165166 if ( ! ( 'base64' in e . data ) ) return ;
166167
167- const { base64, type, canvasWidth , canvasHeight } = e . data ;
168+ const { base64, type, dx , dy , dw , dh } = e . data ;
168169 this . mutationCb ( {
169170 id,
170171 type : CanvasContext [ '2D' ] ,
171172 commands : [
172173 {
173174 property : 'clearRect' , // wipe canvas
174- args : [ 0 , 0 , canvasWidth , canvasHeight ] ,
175+ args : [ dx , dy , dw , dh ] ,
175176 } ,
176177 {
177178 property : 'drawImage' , // draws (semi-transparent) image
@@ -186,10 +187,10 @@ export class CanvasManager {
186187 } ,
187188 ] ,
188189 } as CanvasArg ,
189- 0 ,
190- 0 ,
191- canvasWidth ,
192- canvasHeight ,
190+ dx ,
191+ dy ,
192+ dw ,
193+ dh ,
193194 ] ,
194195 } ,
195196 ] ,
@@ -211,12 +212,25 @@ export class CanvasManager {
211212 return matchedCanvas ;
212213 } ;
213214
214- const takeCanvasSnapshots = ( timestamp : DOMHighResTimeStamp ) => {
215+ const getVideos = ( ) : HTMLVideoElement [ ] => {
216+ const matchedVideos : HTMLVideoElement [ ] = [ ] ;
217+ if ( recordVideos ) {
218+ win . document . querySelectorAll ( 'video' ) . forEach ( ( video ) => {
219+ if ( video . src !== '' && video . src . indexOf ( 'blob:' ) === - 1 ) return ;
220+ if ( ! isBlocked ( video , blockClass , blockSelector , true ) ) {
221+ matchedVideos . push ( video ) ;
222+ }
223+ } ) ;
224+ }
225+ return matchedVideos ;
226+ } ;
227+
228+ const takeSnapshots = ( timestamp : DOMHighResTimeStamp ) => {
215229 if (
216230 lastSnapshotTime &&
217231 timestamp - lastSnapshotTime < timeBetweenSnapshots
218232 ) {
219- rafId = requestAnimationFrame ( takeCanvasSnapshots ) ;
233+ rafId = requestAnimationFrame ( takeSnapshots ) ;
220234 return ;
221235 }
222236 lastSnapshotTime = timestamp ;
@@ -266,43 +280,104 @@ export class CanvasManager {
266280 const width = canvas . width * scale ;
267281 const height = canvas . height * scale ;
268282
269- window . performance . mark ( `canvas-${ canvas . id } -snapshot` ) ;
270283 const bitmap = await createImageBitmap ( canvas , {
271- resizeQuality : resizeQuality || 'low' ,
272284 resizeWidth : width ,
273285 resizeHeight : height ,
274286 } ) ;
275- this . debug (
276- canvas ,
277- 'took a snapshot in' ,
278- window . performance . measure ( `canvas-snapshot` ) ,
279- ) ;
280- window . performance . mark ( `canvas-postMessage` ) ;
287+ this . debug ( canvas , 'created image bitmap' ) ;
281288 worker . postMessage (
282289 {
283290 id,
284291 bitmap,
285292 width,
286293 height,
287- canvasWidth : canvas . width ,
288- canvasHeight : canvas . height ,
294+ dx : 0 ,
295+ dy : 0 ,
296+ dw : canvas . width ,
297+ dh : canvas . height ,
289298 dataURLOptions : options . dataURLOptions ,
290299 } ,
291300 [ bitmap ] ,
292301 ) ;
293- this . debug (
294- canvas ,
295- 'send message in' ,
296- window . performance . measure ( `canvas-postMessage` ) ,
302+ this . debug ( canvas , 'sent message' ) ;
303+ } finally {
304+ snapshotInProgressMap . set ( id , false ) ;
305+ }
306+ } ) ;
307+ getVideos ( ) . forEach ( async ( video : HTMLVideoElement ) => {
308+ this . debug ( video , 'starting video snapshotting' ) ;
309+ const id = this . mirror . getId ( video ) ;
310+ if ( snapshotInProgressMap . get ( id ) ) {
311+ this . debug ( video , 'video snapshotting already in progress for' , id ) ;
312+ return ;
313+ }
314+ snapshotInProgressMap . set ( id , true ) ;
315+ try {
316+ const { width : boxWidth , height : boxHeight } =
317+ video . getBoundingClientRect ( ) ;
318+ const { actualWidth, actualHeight } = {
319+ actualWidth : video . videoWidth ,
320+ actualHeight : video . videoHeight ,
321+ } ;
322+ const maxDim = Math . max ( actualWidth , actualHeight ) ;
323+ let scale = resizeFactor || 1 ;
324+ if ( maxSnapshotDimension ) {
325+ scale = Math . min ( scale , maxSnapshotDimension / maxDim ) ;
326+ }
327+ const width = actualWidth * scale ;
328+ const height = actualHeight * scale ;
329+
330+ const bitmap = await createImageBitmap ( video , {
331+ resizeWidth : width ,
332+ resizeHeight : height ,
333+ } ) ;
334+
335+ let outputScale = Math . max ( boxWidth , boxHeight ) / maxDim ;
336+ const outputWidth = actualWidth * outputScale ;
337+ const outputHeight = actualHeight * outputScale ;
338+ const offsetX = ( boxWidth - outputWidth ) / 2 ;
339+ const offsetY = ( boxHeight - outputHeight ) / 2 ;
340+ this . debug ( video , 'created image bitmap' , {
341+ actualWidth,
342+ actualHeight,
343+ boxWidth,
344+ boxHeight,
345+ outputWidth,
346+ outputHeight,
347+ resizeWidth : width ,
348+ resizeHeight : height ,
349+ scale,
350+ outputScale,
351+ offsetX,
352+ offsetY,
353+ } ) ;
354+
355+ worker . postMessage (
356+ {
357+ id,
358+ bitmap,
359+ width,
360+ height,
361+ dx : offsetX ,
362+ dy : offsetY ,
363+ dw : outputWidth ,
364+ dh : outputHeight ,
365+ dataURLOptions : options . dataURLOptions ,
366+ } ,
367+ [ bitmap ] ,
297368 ) ;
369+ this . debug ( video , 'send message' ) ;
370+ } catch ( e ) {
371+ this . debug ( video , 'failed to snapshot' , e ) ;
298372 } finally {
299373 snapshotInProgressMap . set ( id , false ) ;
300374 }
301375 } ) ;
302- rafId = requestAnimationFrame ( takeCanvasSnapshots ) ;
376+
377+ rafId = requestAnimationFrame ( takeSnapshots ) ;
303378 } ;
304379
305- rafId = requestAnimationFrame ( takeCanvasSnapshots ) ;
380+ rafId = requestAnimationFrame ( takeSnapshots ) ;
306381
307382 this . resetObservers = ( ) => {
308383 canvasContextReset ( ) ;
0 commit comments