@@ -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 . toLowerCase ( ) === ' 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 ) {
@@ -165,14 +166,14 @@ export class CanvasManager {
165166
166167 if ( ! ( 'base64' in e . data ) ) return ;
167168
168- const { base64, type, canvasWidth , canvasHeight } = e . data ;
169+ const { base64, type, dx , dy , dw , dh } = e . data ;
169170 this . mutationCb ( {
170171 id,
171172 type : CanvasContext [ '2D' ] ,
172173 commands : [
173174 {
174175 property : 'clearRect' , // wipe canvas
175- args : [ 0 , 0 , canvasWidth , canvasHeight ] ,
176+ args : [ dx , dy , dw , dh ] ,
176177 } ,
177178 {
178179 property : 'drawImage' , // draws (semi-transparent) image
@@ -187,10 +188,10 @@ export class CanvasManager {
187188 } ,
188189 ] ,
189190 } as CanvasArg ,
190- 0 ,
191- 0 ,
192- canvasWidth ,
193- canvasHeight ,
191+ dx ,
192+ dy ,
193+ dw ,
194+ dh ,
194195 ] ,
195196 } ,
196197 ] ,
@@ -212,17 +213,31 @@ export class CanvasManager {
212213 return matchedCanvas ;
213214 } ;
214215
215- const takeCanvasSnapshots = ( timestamp : DOMHighResTimeStamp ) => {
216+ const getVideos = ( ) : HTMLVideoElement [ ] => {
217+ const matchedVideos : HTMLVideoElement [ ] = [ ] ;
218+ if ( recordVideos ) {
219+ win . document . querySelectorAll ( 'video' ) . forEach ( ( video ) => {
220+ if ( video . src !== '' && video . src . indexOf ( 'blob:' ) === - 1 ) return ;
221+ if ( ! isBlocked ( video , blockClass , blockSelector , true ) ) {
222+ matchedVideos . push ( video ) ;
223+ }
224+ } ) ;
225+ }
226+ return matchedVideos ;
227+ } ;
228+
229+ const takeSnapshots = async ( timestamp : DOMHighResTimeStamp ) => {
216230 if (
217231 lastSnapshotTime &&
218232 timestamp - lastSnapshotTime < timeBetweenSnapshots
219233 ) {
220- rafId = requestAnimationFrame ( takeCanvasSnapshots ) ;
234+ rafId = requestAnimationFrame ( takeSnapshots ) ;
221235 return ;
222236 }
223237 lastSnapshotTime = timestamp ;
224238
225- getCanvas ( ) . forEach ( async ( canvas : HTMLCanvasElement ) => {
239+ const promises : Promise < void > [ ] = [ ]
240+ promises . push ( ...getCanvas ( ) . map ( async ( canvas : HTMLCanvasElement ) => {
226241 this . debug ( canvas , 'starting snapshotting' ) ;
227242 const id = this . mirror . getId ( canvas ) ;
228243 if ( snapshotInProgressMap . get ( id ) ) {
@@ -273,43 +288,107 @@ export class CanvasManager {
273288 const width = canvas . width * scale ;
274289 const height = canvas . height * scale ;
275290
276- window . performance . mark ( `canvas-${ canvas . id } -snapshot` ) ;
277291 const bitmap = await createImageBitmap ( canvas , {
278- resizeQuality : resizeQuality || 'low' ,
279292 resizeWidth : width ,
280293 resizeHeight : height ,
281294 } ) ;
282- this . debug (
283- canvas ,
284- 'took a snapshot in' ,
285- window . performance . measure ( `canvas-snapshot` ) ,
295+ this . debug ( canvas , 'created image bitmap' ) ;
296+ worker . postMessage (
297+ {
298+ id,
299+ bitmap,
300+ width,
301+ height,
302+ dx : 0 ,
303+ dy : 0 ,
304+ dw : canvas . width ,
305+ dh : canvas . height ,
306+ dataURLOptions : options . dataURLOptions ,
307+ } ,
308+ [ bitmap ] ,
286309 ) ;
287- window . performance . mark ( `canvas-postMessage` ) ;
310+ this . debug ( canvas , 'sent message' ) ;
311+ } catch ( e ) {
312+ this . debug ( canvas , 'failed to snapshot' , e ) ;
313+ } finally {
314+ snapshotInProgressMap . set ( id , false ) ;
315+ }
316+ } ) )
317+ promises . push ( ...getVideos ( ) . map ( async ( video : HTMLVideoElement ) => {
318+ this . debug ( video , 'starting video snapshotting' ) ;
319+ const id = this . mirror . getId ( video ) ;
320+ if ( snapshotInProgressMap . get ( id ) ) {
321+ this . debug ( video , 'video snapshotting already in progress for' , id ) ;
322+ return ;
323+ }
324+ snapshotInProgressMap . set ( id , true ) ;
325+ try {
326+ const { width : boxWidth , height : boxHeight } =
327+ video . getBoundingClientRect ( ) ;
328+ const { actualWidth, actualHeight } = {
329+ actualWidth : video . videoWidth ,
330+ actualHeight : video . videoHeight ,
331+ } ;
332+ const maxDim = Math . max ( actualWidth , actualHeight ) ;
333+ let scale = resizeFactor || 1 ;
334+ if ( maxSnapshotDimension ) {
335+ scale = Math . min ( scale , maxSnapshotDimension / maxDim ) ;
336+ }
337+ const width = actualWidth * scale ;
338+ const height = actualHeight * scale ;
339+
340+ const bitmap = await createImageBitmap ( video , {
341+ resizeWidth : width ,
342+ resizeHeight : height ,
343+ } ) ;
344+
345+ let outputScale = Math . max ( boxWidth , boxHeight ) / maxDim ;
346+ const outputWidth = actualWidth * outputScale ;
347+ const outputHeight = actualHeight * outputScale ;
348+ const offsetX = ( boxWidth - outputWidth ) / 2 ;
349+ const offsetY = ( boxHeight - outputHeight ) / 2 ;
350+ this . debug ( video , 'created image bitmap' , {
351+ actualWidth,
352+ actualHeight,
353+ boxWidth,
354+ boxHeight,
355+ outputWidth,
356+ outputHeight,
357+ resizeWidth : width ,
358+ resizeHeight : height ,
359+ scale,
360+ outputScale,
361+ offsetX,
362+ offsetY,
363+ } ) ;
364+
288365 worker . postMessage (
289366 {
290367 id,
291368 bitmap,
292369 width,
293370 height,
294- canvasWidth : canvas . width ,
295- canvasHeight : canvas . height ,
371+ dx : offsetX ,
372+ dy : offsetY ,
373+ dw : outputWidth ,
374+ dh : outputHeight ,
296375 dataURLOptions : options . dataURLOptions ,
297376 } ,
298377 [ bitmap ] ,
299378 ) ;
300- this . debug (
301- canvas ,
302- 'send message in' ,
303- window . performance . measure ( `canvas-postMessage` ) ,
304- ) ;
379+ this . debug ( video , 'send message' ) ;
380+ } catch ( e ) {
381+ this . debug ( video , 'failed to snapshot' , e ) ;
305382 } finally {
306383 snapshotInProgressMap . set ( id , false ) ;
307384 }
308- } ) ;
309- rafId = requestAnimationFrame ( takeCanvasSnapshots ) ;
385+ } ) )
386+ await Promise . all ( promises )
387+
388+ rafId = requestAnimationFrame ( takeSnapshots ) ;
310389 } ;
311390
312- rafId = requestAnimationFrame ( takeCanvasSnapshots ) ;
391+ rafId = requestAnimationFrame ( takeSnapshots ) ;
313392
314393 this . resetObservers = ( ) => {
315394 canvasContextReset ( ) ;
0 commit comments