From 0057478b94cf3c390cf99555d288993a3a2e03bb Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Thu, 16 Apr 2026 13:37:03 -0700 Subject: [PATCH 1/2] videoUploading: add webcam as a source video option --- sample/videoUploading/main.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/sample/videoUploading/main.ts b/sample/videoUploading/main.ts index a66c1e6b..d9e98628 100644 --- a/sample/videoUploading/main.ts +++ b/sample/videoUploading/main.ts @@ -20,6 +20,10 @@ const videos = { url: '../../assets/video/Video_360°._Timelapse._Bled_Lake_in_Slovenia..webm.720p.vp9.webm', mode: '360', }, + webcam: { + url: '', + mode: 'mirror', + }, } as const; const canvas = document.querySelector('canvas') as HTMLCanvasElement; @@ -90,7 +94,21 @@ let canReadVideo = false; async function playVideo(videoName: keyof typeof videos) { canReadVideo = false; - video.src = videos[videoName].url; + + if (video.srcObject) { + (video.srcObject as MediaStream) + .getTracks() + .forEach((track) => track.stop()); + video.srcObject = null; + } + + if (videoName === 'webcam') { + video.srcObject = await navigator.mediaDevices.getUserMedia({ + video: true, + }); + } else { + video.src = videos[videoName].url; + } await video.play(); canReadVideo = true; } @@ -182,6 +200,9 @@ function drawVideo() { combinedAspect > 1 ? [1 / combinedAspect, 1] : [1, combinedAspect], mat ); + if (mode === 'mirror') { + mat3.scale(mat, [-1, 1], mat); + } mat3.translate(mat, [-0.5, -0.5], mat); device.queue.writeBuffer(uniformBuffer, 0, mat); } From ce659dec228f91e41b1d624065325824a72eb75b Mon Sep 17 00:00:00 2001 From: Kai Ninomiya Date: Thu, 16 Apr 2026 14:07:09 -0700 Subject: [PATCH 2/2] show frame dimensions --- sample/videoUploading/index.html | 6 +++--- sample/videoUploading/main.ts | 20 ++++++++++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/sample/videoUploading/index.html b/sample/videoUploading/index.html index 7f712340..52f97c62 100644 --- a/sample/videoUploading/index.html +++ b/sample/videoUploading/index.html @@ -23,9 +23,8 @@ height: 100%; /* make body fill the browser window */ } canvas { - width: 100%; - height: 100%; - max-width: 100%; + width: min(100vw, 100vh); + aspect-ratio: 4 / 3; display: block; } @@ -34,5 +33,6 @@ +

   
 
diff --git a/sample/videoUploading/main.ts b/sample/videoUploading/main.ts
index d9e98628..96a3f1ee 100644
--- a/sample/videoUploading/main.ts
+++ b/sample/videoUploading/main.ts
@@ -26,6 +26,8 @@ const videos = {
   },
 } as const;
 
+const videoinfo = document.getElementById('videoinfo') as HTMLPreElement;
+
 const canvas = document.querySelector('canvas') as HTMLCanvasElement;
 const context = canvas.getContext('webgpu');
 const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
@@ -138,8 +140,22 @@ function drawVideo() {
   const maxSize = device.limits.maxTextureDimension2D;
   canvas.width = Math.min(Math.max(1, canvas.offsetWidth), maxSize);
   canvas.height = Math.min(Math.max(1, canvas.offsetHeight), maxSize);
-  const externalTextureSource =
-    settings.videoSource === 'videoFrame' ? new VideoFrame(video) : video;
+  const videoFrame =
+    settings.videoSource === 'videoFrame' ? new VideoFrame(video) : null;
+  const externalTextureSource = videoFrame ?? video;
+  videoinfo.textContent = `HTMLVideoElement: ${video.videoWidth}x${video.videoHeight}`;
+  if (videoFrame) {
+    videoinfo.textContent +=
+      '\nVideoFrame: ' +
+      JSON.stringify(
+        {
+          codedRect: videoFrame.codedRect,
+          visibleRect: videoFrame.visibleRect,
+        },
+        undefined,
+        2
+      );
+  }
 
   const mode = videos[settings.video].mode;
   const pipeline = mode === '360' ? video360Pipeline : videoCoverPipeline;