about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--js/pico-cam/index.html5
-rw-r--r--js/pico-cam/pico-cam.js79
-rw-r--r--js/pico-cam/service-worker.js4
3 files changed, 84 insertions, 4 deletions
diff --git a/js/pico-cam/index.html b/js/pico-cam/index.html
index 0948b04..05b8335 100644
--- a/js/pico-cam/index.html
+++ b/js/pico-cam/index.html
@@ -46,6 +46,10 @@
             color: #FFFFFF;
         }
 
+        .capture-frame#captureVideo.active {
+            background-color: #FF0000;
+        }
+
         .media-container {
             display: flex;
             justify-content: center;
@@ -76,6 +80,7 @@
     <div class="footer">
         <button id="toggleCamera">Turn Camera On</button>
         <button id="captureFrame" class="capture-frame" disabled>Capture Frame</button>
+        <button id="captureVideo" class="capture-frame" disabled>Capture Video</button>
     </div>
     <div class="media-container">
         <video id="webcam" autoplay playsinline style="display:none;"></video>
diff --git a/js/pico-cam/pico-cam.js b/js/pico-cam/pico-cam.js
index f51179e..3dc7646 100644
--- a/js/pico-cam/pico-cam.js
+++ b/js/pico-cam/pico-cam.js
@@ -1,5 +1,6 @@
 const toggleCameraButton = document.getElementById('toggleCamera');
 const captureFrameButton = document.getElementById('captureFrame');
+const captureVideoButton = document.getElementById('captureVideo');
 const video = document.getElementById('webcam');
 const canvas = document.getElementById('ditheredOutput');
 const context = canvas.getContext('2d');
@@ -8,6 +9,7 @@ let stream = null;
 let isCameraOn = false;
 const lowResWidth = 160; // Gameboy camera resolution: 160×144
 const lowResHeight = 144;
+const videoLength = 3000; // 3 seconds
 video.style.width = lowResWidth + 'px';
 video.style.height = lowResHeight + 'px';
 
@@ -24,7 +26,9 @@ toggleCameraButton.addEventListener('click', async () => {
             isCameraOn = true;
             toggleCameraButton.textContent = 'Turn Camera Off';
             captureFrameButton.disabled = false;
+            captureVideoButton.disabled = false;
             captureFrameButton.classList.add('active');
+            captureVideoButton.classList.add('active');
             video.addEventListener('loadeddata', () => {
                 canvas.width = lowResWidth;
                 canvas.height = lowResHeight;
@@ -39,19 +43,90 @@ toggleCameraButton.addEventListener('click', async () => {
         isCameraOn = false;
         toggleCameraButton.textContent = 'Turn Camera On';
         captureFrameButton.disabled = true;
+        captureVideoButton.disabled = true;
         captureFrameButton.classList.remove('active');
+        captureVideoButton.classList.remove('active');
     }
 });
 
 captureFrameButton.addEventListener('click', () => {
     const link = document.createElement('a');
     link.href = canvas.toDataURL('image/png');
-    link.download = 'frame.png';
+    link.download = 'dithered-frame.png';
     link.click();
 });
 
+captureVideoButton.addEventListener('click', () => {
+    const startRecording = (canvas, duration) => {
+        return new Promise((resolve, reject) => {
+            const stream = canvas.captureStream();
+            if (!stream) {
+                const errtxt = 'Stream capture from canvas failed.';
+                console.error(errtxt);
+                return reject(errtxt);
+            }
+
+            const mediaRecorder = new MediaRecorder(stream, { mimeType: 'video/webm' });
+            let chunks = [];
+
+            mediaRecorder.ondataavailable = (event) => {
+                if (event.data.size > 0) {
+                    chunks.push(event.data);
+                }
+            };
+
+            mediaRecorder.onstop = () => {
+                const blob = new Blob(chunks, { type: 'video/webm' });
+                resolve(blob);
+            };
+
+            mediaRecorder.onerror = (event) => {
+                console.error('MediaRecorder error:', event.error);
+                reject(event.error);
+            };
+
+            // console.log('Starting recording...');
+            mediaRecorder.start();
+            setTimeout(() => {
+                // console.log('Stopping recording...');
+                mediaRecorder.stop();
+            }, duration);
+        });
+    };
+
+    const handleCaptureVideo = async () => {
+        if (!isCameraOn) {
+            console.error('Camera is not active.');
+            return;
+        }
+
+        try {
+            captureVideoButton.disabled = true;
+            const videoBlob = await startRecording(ditheredOutput, videoLength);
+            const videoURL = URL.createObjectURL(videoBlob);
+
+            const videoLink = document.createElement('a');
+            videoLink.href = videoURL;
+            videoLink.download = 'dithered-video.webm';
+            videoLink.click();
+
+            // Enable the button again after recording
+            captureVideoButton.disabled = false;
+        } catch (error) {
+            console.error('Error recording video:', error);
+            captureVideoButton.disabled = false;
+        }
+    };
+
+    handleCaptureVideo();
+});
+
+
 function processFrame() {
-    if (!isCameraOn) return;
+    if (!isCameraOn) {
+        console.error('Camera is not active.');
+        return;
+    }
 
     // Crop and draw the middle part of the video frame
     const videoAspect = video.videoWidth / video.videoHeight;
diff --git a/js/pico-cam/service-worker.js b/js/pico-cam/service-worker.js
index a64353c..ce5271b 100644
--- a/js/pico-cam/service-worker.js
+++ b/js/pico-cam/service-worker.js
@@ -1,4 +1,4 @@
-var CACHE_NAME = 'pico-cam-v1';
+var CACHE_NAME = 'pico-cam-v2';
 var urlsToCache = [
   './',
   './index.html',
@@ -30,7 +30,7 @@ self.addEventListener('fetch', function(event) {
 });
 
 self.addEventListener('activate', function(event) {
-    var cacheWhitelist = ['pico-cam-v1'];
+    var cacheWhitelist = ['pico-cam-v2'];
     event.waitUntil(
         caches.keys().then(function(cacheNames) {
             return Promise.all(